From d1744a96d33655f17c7f80e5535017a97839c42a Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 04:33:35 -0300 Subject: [PATCH 01/20] feat(Novel Mania): Update and add setting up for v2 --- websites/N/Novel Mania/{ => v1}/metadata.json | 0 websites/N/Novel Mania/{ => v1}/presence.ts | 0 websites/N/Novel Mania/v2/metadata.json | 60 +++++ websites/N/Novel Mania/v2/novelmania.json | 54 +++++ websites/N/Novel Mania/v2/presence.ts | 222 ++++++++++++++++++ 5 files changed, 336 insertions(+) rename websites/N/Novel Mania/{ => v1}/metadata.json (100%) rename websites/N/Novel Mania/{ => v1}/presence.ts (100%) create mode 100644 websites/N/Novel Mania/v2/metadata.json create mode 100644 websites/N/Novel Mania/v2/novelmania.json create mode 100644 websites/N/Novel Mania/v2/presence.ts diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/v1/metadata.json similarity index 100% rename from websites/N/Novel Mania/metadata.json rename to websites/N/Novel Mania/v1/metadata.json diff --git a/websites/N/Novel Mania/presence.ts b/websites/N/Novel Mania/v1/presence.ts similarity index 100% rename from websites/N/Novel Mania/presence.ts rename to websites/N/Novel Mania/v1/presence.ts diff --git a/websites/N/Novel Mania/v2/metadata.json b/websites/N/Novel Mania/v2/metadata.json new file mode 100644 index 000000000000..cd507f0f66b7 --- /dev/null +++ b/websites/N/Novel Mania/v2/metadata.json @@ -0,0 +1,60 @@ +{ + "apiVersion": 2, + "author": { + "name": "Shuichi", + "id": "179440483796385792" + }, + "service": "Novel Mania", + "altnames": [ + "NovelMania" + ], + "description": { + "en": "Read webnovels in Portuguese anywhere and anytime.", + "pt-br": "Leia webnovels em português a qualquer momento e a qualquer hora." + }, + "url": "novelmania.com.br", + "regExp": "^https?[:][/][/]([a-z0-9-]+[.])*novelmania[.]com[.]br[/]", + "version": "2.0.0", + "logo": "https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png", + "thumbnail": "https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/thumbnail.jpg", + "color": "#008cff", + "category": "other", + "tags": [ + "novel", + "lightnovel", + "light novel", + "webnovel", + "web novel", + "reader", + "scanlator", + "translation", + "book" + ], + "settings": [ + { + "id": "lang", + "multiLanguage": true + }, + { + "id": "hideInfo", + "title": "Privacy Mode", + "icon": "fas fa-compress-arrows-alt", + "value": false + }, + { + "id": "showButtons", + "title": "Show Buttons", + "icon": "fas fa-compress-arrows-alt", + "value": true, + "if": { + "showInfo": false + } + }, + { + "id": "showTimestamp", + "title": "Show Timestamp", + "icon": "fas fa-clock", + "value": true + } + ] +} \ No newline at end of file diff --git a/websites/N/Novel Mania/v2/novelmania.json b/websites/N/Novel Mania/v2/novelmania.json new file mode 100644 index 000000000000..a094bd0bab9c --- /dev/null +++ b/websites/N/Novel Mania/v2/novelmania.json @@ -0,0 +1,54 @@ +{ + "novelmania.volume": { + "description": "Text for when user's reading a novel with volumes.", + "message": "Volume" + }, + "novelmania.book": { + "description": "Text for when user's reading a novel with books.", + "message": "Book" + }, + "novelmania.viewingProfile": { + "description": "Text for when user's visualizing someone's profile.", + "message": "Viewing profile" + }, + "novelmania.lists": { + "description": "Text for when user's visualizing 'lists' page.", + "message": "listas" + }, + "novelmania.news": { + "description": "Text for when user's visualizing 'news' page.", + "message": "news" + }, + "novelmania.genre": { + "description": "Text for when user's browsing genres.", + "message": "genres" + }, + "novelmania.novel": { + "description": "Just a placeholder for a novel title. It doesn't matter what it is, it will be replaced by the actual title.", + "message": "novel" + }, + "novelmania.readNovelButton": { + "description": "", + "message": "Read this novel!" + }, + "novelmania.readChapterButton": { + "description": "Text from 'reading chapter button'. Call to read along!", + "message": "Read this chapter!" + }, + "novelmania.visitWebsiteButton": { + "description": "Text from 'visit website button'.", + "message": "Visit this website!" + }, + "novelmania.readListButton": { + "description": "Text from 'read this list button'.", + "message": "Read this list!" + }, + "novelmania.readNewsButton": { + "description": "Text from 'read this news button'.", + "message": "Read this news!" + }, + "novelmania.visitUserProfileButton": { + "description": "Text from 'spy on this user button'. Naughty, naughty...", + "message": "Spy on this user!" + } +} \ No newline at end of file diff --git a/websites/N/Novel Mania/v2/presence.ts b/websites/N/Novel Mania/v2/presence.ts new file mode 100644 index 000000000000..069fb51e17dd --- /dev/null +++ b/websites/N/Novel Mania/v2/presence.ts @@ -0,0 +1,222 @@ +import { ActivityType, Assets } from 'premid' + +const presence = new Presence({ + clientId: '738522217221980222', +}) +const browsingTimestamp = Math.floor(Date.now() / 1000) +enum ActivityAssets { + Logo = 'https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png', +} +async function getStrings() { + return presence.getStrings({ + browse: 'general.browsing', + home: 'general.viewHome', + privacy: 'general.privacy', + reading: 'general.reading', + view: 'general.view', + volume: 'novelmania.volume', + profile: 'general.viewProfile', + lists: 'novelmania.lists', + news: 'novelmania.news', + genre: 'novelmania.genre', + novel: 'novelmania.novel', + readNovelButton: 'novelmania.readNovelButton', + readChapterButton: 'novelmania.readChapterButton', + visitWebsiteButton: 'novelmania.visitWebsiteButton', + readListButton: 'novelmania.readListButton', + readNewsButton: 'novelmania.readNewsButton', + visitUserProfileButton: 'novelmania.visitUserProfileButton', + }) +} +let oldUserLanguage: string | null = null +let strings: Awaited> + +presence.on('UpdateData', async () => { + strings = await getStrings() + + const [showButtons, showTime, hideInfo, userLanguage] = await Promise.all([ + presence.getSetting('showButtons') || true, + presence.getSetting('showTimestamp') || true, + presence.getSetting('hideInfo') || false, + presence.getSetting('lang').catch(() => 'pt') + ]); + + if (oldUserLanguage !== userLanguage) { + oldUserLanguage = userLanguage + strings = await getStrings() + } + + + const presenceData: PresenceData = { + largeImageKey: ActivityAssets.Logo, + startTimestamp: showTime ? browsingTimestamp : undefined, + type: ActivityType.Watching + + } + const { pathname, origin } = window.location + const cleanPath = pathname.replace(/\/$/, '') || '/' + const [part1, part2, part3, part4] = cleanPath.slice(1).split('/') // page, slug (if any), chapter (if any), volume/book (if any) + + const privacyCheck = (foo: any): string => { + return !hideInfo ? foo : strings.privacy + } + + const getPageTitle = (): string => document.querySelector('#main h1')?.textContent || strings.novel + let buttons: [ButtonData, ButtonData?] | undefined + + switch (part1) { + case '': + if (cleanPath === '/') { + presenceData.state = strings.home; + + buttons = [ + { + label: strings.visitWebsiteButton, + url: origin, + }, + ] + } + break + + case 'u': /* Seeing some user profile */ + if (part2) { + presenceData.details = `${strings.profile}` + presenceData.state = privacyCheck(getPageTitle()); + buttons = [ + { + label: strings.visitUserProfileButton, + url: origin + `/u/${part2}`, + }, + ] + } + break + + case 'novels': + if (!part2) { /* Searching some novel */ + presenceData.details = `${strings.browse}`; + presenceData.state = privacyCheck(getPageTitle()); + + const params = new URLSearchParams(window.location.search); + const searchTerm = (document.querySelector('input[name="q"]') as HTMLInputElement)?.value || params.get('q'); + + if (searchTerm) { + presenceData.details = `${strings.browse} ${privacyCheck(decodeURIComponent(searchTerm))}`; + } + + // Extrai nomes dos filtros - apenas se houver busca ou query params + if (searchTerm || window.location.search) { + const badges = document.querySelectorAll('span.inline-flex.rounded-full.border'); + if (badges.length > 0) { + const visibleFilters: string[] = []; + for (let i = 0; i < badges.length; i++) { + const text = badges[i]?.childNodes[0]?.textContent?.trim(); + if (text) visibleFilters.push(text); + } + if (visibleFilters.length) { + presenceData.state = privacyCheck(visibleFilters.join(', ')); + } + } + } + break + } + if (part3 === 'capitulos' && part4) { /* Reading some novel's chapter */ + const novelName = document.querySelector("#conteudo-principal > div > header > div > p")?.textContent || part2?.split('-').slice(0, 2).join(' ') || strings.novel; + const noveltype = document.querySelector("#conteudo-principal > div > main > div > div > header > p")?.textContent || part4?.split('-').slice(0, 2).join(' ') || strings.volume; + const currentChapTitle = document.querySelector("#reader-chapter-title")?.textContent || novelName; + + presenceData.details = `${strings.reading} ${privacyCheck(novelName)}`; + presenceData.state = `${privacyCheck(currentChapTitle)} - ${privacyCheck(noveltype)}`; + presenceData.smallImageKey = Assets.Reading + + buttons = [ + { + label: strings.readNovelButton, + url: origin + `/novels/${part2}`, + }, + { + label: strings.readChapterButton, + url: origin + `/novels/${part2}/capitulos/${part4}`, + }, + ] + break + } + if (part2) { /* At some novel's page */ + const novelName = document.querySelector("#main > div > h1")?.textContent || strings.novel + presenceData.state = ` ${strings.view} ${privacyCheck(novelName)}`; + + buttons = [ + { + label: strings.readNovelButton, + url: origin + `/novels/${part2}`, + }, + ] + } + break + + case 'listas': /* Searching some lists */ + if (!part2) { + presenceData.state = `${strings.browse} ${strings.lists}`; + presenceData.smallImageKey = Assets.Reading + break + } + + presenceData.details = `${strings.view} ${strings.lists}`; + presenceData.state = `${privacyCheck(getPageTitle())} - ${privacyCheck(document.querySelector("#main > div > div:nth-child(2) > a")?.textContent || strings.novel)}`; + presenceData.smallImageKey = Assets.Reading + + buttons = [ + { + label: strings.readListButton, + url: origin + `/listas/${part2}`, + }, + ] + break + + case 'noticias': + if (!part2) { /* Searching some news */ + presenceData.details = `${strings.reading} ${strings.news}`; /* reading news list */ + presenceData.state = `${privacyCheck(getPageTitle())}`; + presenceData.smallImageKey = Assets.Reading + break + } + + presenceData.details = `${strings.reading} ${strings.news}`; /* reading a news */ + presenceData.state = `${privacyCheck(getPageTitle())}`; + presenceData.smallImageKey = Assets.Reading + + buttons = [ + { + label: strings.readNewsButton, + url: origin + `/noticias/${part2}`, + }, + ] + break + + case 'genero': /* Browsing some genres */ + if (part2) { + presenceData.details = `${strings.browse} ${strings.genre}`; + presenceData.state = privacyCheck(getPageTitle()); + presenceData.smallImageKey = Assets.Reading + } + break + + default: /* At any other page, it doesn't matter */ + presenceData.state = `${strings.view} ${privacyCheck(getPageTitle())}`; + presenceData.smallImageKey = Assets.Reading + break + } + + if (showButtons && buttons && (cleanPath === '/' || !hideInfo)) + presenceData.buttons = buttons + + // Set the activity + if (presenceData.state) { + presence.setActivity(presenceData) + + if (!showTime) + delete presenceData.startTimestamp + + } else { + presence.clearActivity() + } +}) From 61b70b607c0abeafd43e70e23b360f3f2bf70e6b Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 04:42:04 -0300 Subject: [PATCH 02/20] fix(Novel Mania): Update everything website has changed --- websites/N/Novel Mania/v2/metadata.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/websites/N/Novel Mania/v2/metadata.json b/websites/N/Novel Mania/v2/metadata.json index cd507f0f66b7..3fba9a9aa0ad 100644 --- a/websites/N/Novel Mania/v2/metadata.json +++ b/websites/N/Novel Mania/v2/metadata.json @@ -1,5 +1,6 @@ { - "apiVersion": 2, + "$schema": "https://schemas.premid.app/metadata/1.17", + "apiVersion": 1, "author": { "name": "Shuichi", "id": "179440483796385792" From 1649427b44b18285859c5a4ca140a0aed886c48d Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 11:01:01 -0300 Subject: [PATCH 03/20] Remove folders as I dont know whta to do with then --- websites/N/Novel Mania/v1/metadata.json | 26 --- websites/N/Novel Mania/v1/presence.ts | 78 -------- websites/N/Novel Mania/v2/metadata.json | 61 ------ websites/N/Novel Mania/v2/novelmania.json | 54 ------ websites/N/Novel Mania/v2/presence.ts | 222 ---------------------- 5 files changed, 441 deletions(-) delete mode 100644 websites/N/Novel Mania/v1/metadata.json delete mode 100644 websites/N/Novel Mania/v1/presence.ts delete mode 100644 websites/N/Novel Mania/v2/metadata.json delete mode 100644 websites/N/Novel Mania/v2/novelmania.json delete mode 100644 websites/N/Novel Mania/v2/presence.ts diff --git a/websites/N/Novel Mania/v1/metadata.json b/websites/N/Novel Mania/v1/metadata.json deleted file mode 100644 index 7ad8f79cd1de..000000000000 --- a/websites/N/Novel Mania/v1/metadata.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "https://schemas.premid.app/metadata/1.16", - "apiVersion": 1, - "author": { - "name": "Shuichi", - "id": "179440483796385792" - }, - "service": "Novel Mania", - "description": { - "en": "Read webnovels in Portuguese anywhere and anytime.", - "nl": "Lees overal en altijd webnovels in het Portugees.", - "pt-br": "Leia webnovels em português a qualquer momento e a qualquer hora." - }, - "url": "novelmania.com.br", - "regExp": "^https?[:][/][/]([a-z0-9-]+[.])*novelmania[.]com[.]br[/]", - "version": "1.1.0", - "logo": "https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png", - "thumbnail": "https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/thumbnail.jpg", - "color": "#008cff", - "category": "other", - "tags": [ - "novel", - "mania", - "translator" - ] -} diff --git a/websites/N/Novel Mania/v1/presence.ts b/websites/N/Novel Mania/v1/presence.ts deleted file mode 100644 index f3b79755c8d9..000000000000 --- a/websites/N/Novel Mania/v1/presence.ts +++ /dev/null @@ -1,78 +0,0 @@ -const presence = new Presence({ - clientId: '738522217221980222', -}) -const browsingTimestamp = Math.floor(Date.now() / 1000) - -presence.on('UpdateData', async () => { - const presenceData: PresenceData = { - largeImageKey: 'https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png', - startTimestamp: browsingTimestamp, - } - const path = document.location.pathname - const porcent = document.querySelector( - '#settings-section > div > ul > li:nth-child(1) > div > div', - ) - const currentChapTitle = document.querySelector( - 'body > div:nth-child(4) > main > section.landing.novel-single > div.novel-head.pt-3 > div > div > div:nth-child(1) > nav > ol > li.breadcrumb-item.active', - ) - const novelName = document.querySelector('h1') - const VolNumb = document.querySelector('#chapter-content > h3') - const NovelTitle = document.querySelector( - 'body > div:nth-child(4) > main > section.landing.novel-single > div.novel-head.pt-3 > div > div > div:nth-child(2) > div > h1 > a', - ) - const PagTitle = document.querySelector('h2') - if (path === '/' || !path) { - presenceData.details = 'Na página inicial' /* at home */ - presenceData.state = 'Só olhando... Que estranho!' /* Juist lookin, how strange! */ - } - else if (path.includes('/noticias/')) { - presenceData.details = 'Lendo Notícia:' /* reading a notice */ - presenceData.state = novelName?.textContent - } - else if (path.includes('/novels/')) { - if (document.body.contains(document.querySelector('#myTab'))) { - presenceData.details = 'No Indice da Novel:' /* At the novels's indice */ - presenceData.state = novelName?.textContent - } - else if (document.body.contains(VolNumb)) { - presenceData.details = `Lendo ${NovelTitle?.textContent} || ${VolNumb?.textContent}` /* reading volume */ - presenceData.state = `Em ${porcent?.textContent} do ${currentChapTitle?.textContent}` /* in X% of... novel chapter */ - } - else { - presenceData.details = `Lendo${NovelTitle?.textContent}` /* reading *novel**/ - presenceData.state = `Em ${porcent?.textContent}do${currentChapTitle?.textContent}` /* in X% of... novel chapter */ - } - } - else if (path.includes('/u/')) { - presenceData.details = 'Bisbilhotando:' /* Seeing the * user profile */ - presenceData.state = document.querySelector( - 'body > div > main > section.profile-top > div > div > div > div.col-sm-8.col-md-9.d-flex.align-items-center > div > ul > li.admin-name > h5', - )?.textContent - } - else if ( - path.includes('/editoria') - || path.includes('/salao-da-contribuicao') - || path.includes('/regras-setorias') - || path.includes('/politica-de-privacidade') - || path.includes('contato') - ) { - presenceData.details = 'Lendo Página: ' /* reading page */ - presenceData.state = PagTitle?.textContent - } - else if ( - path.includes('/genero/') - || path.includes('/chinesa') - || path.includes('/japonesa') - || path.includes('/coreana') - || path.includes('/brasileira') - || path.includes('/americana') - ) { - presenceData.details = 'Procurando:' /* searching for */ - presenceData.state = PagTitle?.textContent - } - else { - presenceData.details = 'Buscando...' /* searching... */ - presenceData.state = 'Algo Incrível' /* Something incredible */ - } - presence.setActivity(presenceData) -}) diff --git a/websites/N/Novel Mania/v2/metadata.json b/websites/N/Novel Mania/v2/metadata.json deleted file mode 100644 index 3fba9a9aa0ad..000000000000 --- a/websites/N/Novel Mania/v2/metadata.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "$schema": "https://schemas.premid.app/metadata/1.17", - "apiVersion": 1, - "author": { - "name": "Shuichi", - "id": "179440483796385792" - }, - "service": "Novel Mania", - "altnames": [ - "NovelMania" - ], - "description": { - "en": "Read webnovels in Portuguese anywhere and anytime.", - "pt-br": "Leia webnovels em português a qualquer momento e a qualquer hora." - }, - "url": "novelmania.com.br", - "regExp": "^https?[:][/][/]([a-z0-9-]+[.])*novelmania[.]com[.]br[/]", - "version": "2.0.0", - "logo": "https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png", - "thumbnail": "https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/thumbnail.jpg", - "color": "#008cff", - "category": "other", - "tags": [ - "novel", - "lightnovel", - "light novel", - "webnovel", - "web novel", - "reader", - "scanlator", - "translation", - "book" - ], - "settings": [ - { - "id": "lang", - "multiLanguage": true - }, - { - "id": "hideInfo", - "title": "Privacy Mode", - "icon": "fas fa-compress-arrows-alt", - "value": false - }, - { - "id": "showButtons", - "title": "Show Buttons", - "icon": "fas fa-compress-arrows-alt", - "value": true, - "if": { - "showInfo": false - } - }, - { - "id": "showTimestamp", - "title": "Show Timestamp", - "icon": "fas fa-clock", - "value": true - } - ] -} \ No newline at end of file diff --git a/websites/N/Novel Mania/v2/novelmania.json b/websites/N/Novel Mania/v2/novelmania.json deleted file mode 100644 index a094bd0bab9c..000000000000 --- a/websites/N/Novel Mania/v2/novelmania.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "novelmania.volume": { - "description": "Text for when user's reading a novel with volumes.", - "message": "Volume" - }, - "novelmania.book": { - "description": "Text for when user's reading a novel with books.", - "message": "Book" - }, - "novelmania.viewingProfile": { - "description": "Text for when user's visualizing someone's profile.", - "message": "Viewing profile" - }, - "novelmania.lists": { - "description": "Text for when user's visualizing 'lists' page.", - "message": "listas" - }, - "novelmania.news": { - "description": "Text for when user's visualizing 'news' page.", - "message": "news" - }, - "novelmania.genre": { - "description": "Text for when user's browsing genres.", - "message": "genres" - }, - "novelmania.novel": { - "description": "Just a placeholder for a novel title. It doesn't matter what it is, it will be replaced by the actual title.", - "message": "novel" - }, - "novelmania.readNovelButton": { - "description": "", - "message": "Read this novel!" - }, - "novelmania.readChapterButton": { - "description": "Text from 'reading chapter button'. Call to read along!", - "message": "Read this chapter!" - }, - "novelmania.visitWebsiteButton": { - "description": "Text from 'visit website button'.", - "message": "Visit this website!" - }, - "novelmania.readListButton": { - "description": "Text from 'read this list button'.", - "message": "Read this list!" - }, - "novelmania.readNewsButton": { - "description": "Text from 'read this news button'.", - "message": "Read this news!" - }, - "novelmania.visitUserProfileButton": { - "description": "Text from 'spy on this user button'. Naughty, naughty...", - "message": "Spy on this user!" - } -} \ No newline at end of file diff --git a/websites/N/Novel Mania/v2/presence.ts b/websites/N/Novel Mania/v2/presence.ts deleted file mode 100644 index 069fb51e17dd..000000000000 --- a/websites/N/Novel Mania/v2/presence.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { ActivityType, Assets } from 'premid' - -const presence = new Presence({ - clientId: '738522217221980222', -}) -const browsingTimestamp = Math.floor(Date.now() / 1000) -enum ActivityAssets { - Logo = 'https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png', -} -async function getStrings() { - return presence.getStrings({ - browse: 'general.browsing', - home: 'general.viewHome', - privacy: 'general.privacy', - reading: 'general.reading', - view: 'general.view', - volume: 'novelmania.volume', - profile: 'general.viewProfile', - lists: 'novelmania.lists', - news: 'novelmania.news', - genre: 'novelmania.genre', - novel: 'novelmania.novel', - readNovelButton: 'novelmania.readNovelButton', - readChapterButton: 'novelmania.readChapterButton', - visitWebsiteButton: 'novelmania.visitWebsiteButton', - readListButton: 'novelmania.readListButton', - readNewsButton: 'novelmania.readNewsButton', - visitUserProfileButton: 'novelmania.visitUserProfileButton', - }) -} -let oldUserLanguage: string | null = null -let strings: Awaited> - -presence.on('UpdateData', async () => { - strings = await getStrings() - - const [showButtons, showTime, hideInfo, userLanguage] = await Promise.all([ - presence.getSetting('showButtons') || true, - presence.getSetting('showTimestamp') || true, - presence.getSetting('hideInfo') || false, - presence.getSetting('lang').catch(() => 'pt') - ]); - - if (oldUserLanguage !== userLanguage) { - oldUserLanguage = userLanguage - strings = await getStrings() - } - - - const presenceData: PresenceData = { - largeImageKey: ActivityAssets.Logo, - startTimestamp: showTime ? browsingTimestamp : undefined, - type: ActivityType.Watching - - } - const { pathname, origin } = window.location - const cleanPath = pathname.replace(/\/$/, '') || '/' - const [part1, part2, part3, part4] = cleanPath.slice(1).split('/') // page, slug (if any), chapter (if any), volume/book (if any) - - const privacyCheck = (foo: any): string => { - return !hideInfo ? foo : strings.privacy - } - - const getPageTitle = (): string => document.querySelector('#main h1')?.textContent || strings.novel - let buttons: [ButtonData, ButtonData?] | undefined - - switch (part1) { - case '': - if (cleanPath === '/') { - presenceData.state = strings.home; - - buttons = [ - { - label: strings.visitWebsiteButton, - url: origin, - }, - ] - } - break - - case 'u': /* Seeing some user profile */ - if (part2) { - presenceData.details = `${strings.profile}` - presenceData.state = privacyCheck(getPageTitle()); - buttons = [ - { - label: strings.visitUserProfileButton, - url: origin + `/u/${part2}`, - }, - ] - } - break - - case 'novels': - if (!part2) { /* Searching some novel */ - presenceData.details = `${strings.browse}`; - presenceData.state = privacyCheck(getPageTitle()); - - const params = new URLSearchParams(window.location.search); - const searchTerm = (document.querySelector('input[name="q"]') as HTMLInputElement)?.value || params.get('q'); - - if (searchTerm) { - presenceData.details = `${strings.browse} ${privacyCheck(decodeURIComponent(searchTerm))}`; - } - - // Extrai nomes dos filtros - apenas se houver busca ou query params - if (searchTerm || window.location.search) { - const badges = document.querySelectorAll('span.inline-flex.rounded-full.border'); - if (badges.length > 0) { - const visibleFilters: string[] = []; - for (let i = 0; i < badges.length; i++) { - const text = badges[i]?.childNodes[0]?.textContent?.trim(); - if (text) visibleFilters.push(text); - } - if (visibleFilters.length) { - presenceData.state = privacyCheck(visibleFilters.join(', ')); - } - } - } - break - } - if (part3 === 'capitulos' && part4) { /* Reading some novel's chapter */ - const novelName = document.querySelector("#conteudo-principal > div > header > div > p")?.textContent || part2?.split('-').slice(0, 2).join(' ') || strings.novel; - const noveltype = document.querySelector("#conteudo-principal > div > main > div > div > header > p")?.textContent || part4?.split('-').slice(0, 2).join(' ') || strings.volume; - const currentChapTitle = document.querySelector("#reader-chapter-title")?.textContent || novelName; - - presenceData.details = `${strings.reading} ${privacyCheck(novelName)}`; - presenceData.state = `${privacyCheck(currentChapTitle)} - ${privacyCheck(noveltype)}`; - presenceData.smallImageKey = Assets.Reading - - buttons = [ - { - label: strings.readNovelButton, - url: origin + `/novels/${part2}`, - }, - { - label: strings.readChapterButton, - url: origin + `/novels/${part2}/capitulos/${part4}`, - }, - ] - break - } - if (part2) { /* At some novel's page */ - const novelName = document.querySelector("#main > div > h1")?.textContent || strings.novel - presenceData.state = ` ${strings.view} ${privacyCheck(novelName)}`; - - buttons = [ - { - label: strings.readNovelButton, - url: origin + `/novels/${part2}`, - }, - ] - } - break - - case 'listas': /* Searching some lists */ - if (!part2) { - presenceData.state = `${strings.browse} ${strings.lists}`; - presenceData.smallImageKey = Assets.Reading - break - } - - presenceData.details = `${strings.view} ${strings.lists}`; - presenceData.state = `${privacyCheck(getPageTitle())} - ${privacyCheck(document.querySelector("#main > div > div:nth-child(2) > a")?.textContent || strings.novel)}`; - presenceData.smallImageKey = Assets.Reading - - buttons = [ - { - label: strings.readListButton, - url: origin + `/listas/${part2}`, - }, - ] - break - - case 'noticias': - if (!part2) { /* Searching some news */ - presenceData.details = `${strings.reading} ${strings.news}`; /* reading news list */ - presenceData.state = `${privacyCheck(getPageTitle())}`; - presenceData.smallImageKey = Assets.Reading - break - } - - presenceData.details = `${strings.reading} ${strings.news}`; /* reading a news */ - presenceData.state = `${privacyCheck(getPageTitle())}`; - presenceData.smallImageKey = Assets.Reading - - buttons = [ - { - label: strings.readNewsButton, - url: origin + `/noticias/${part2}`, - }, - ] - break - - case 'genero': /* Browsing some genres */ - if (part2) { - presenceData.details = `${strings.browse} ${strings.genre}`; - presenceData.state = privacyCheck(getPageTitle()); - presenceData.smallImageKey = Assets.Reading - } - break - - default: /* At any other page, it doesn't matter */ - presenceData.state = `${strings.view} ${privacyCheck(getPageTitle())}`; - presenceData.smallImageKey = Assets.Reading - break - } - - if (showButtons && buttons && (cleanPath === '/' || !hideInfo)) - presenceData.buttons = buttons - - // Set the activity - if (presenceData.state) { - presence.setActivity(presenceData) - - if (!showTime) - delete presenceData.startTimestamp - - } else { - presence.clearActivity() - } -}) From 58342d519c319c8e3f1ddc230f493fd4e56199b5 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 11:05:52 -0300 Subject: [PATCH 04/20] update(Novel Mania): Remove versionize folders as I actually didn't needed them --- websites/N/Novel Mania/metadata.json | 61 +++++++ websites/N/Novel Mania/novelmania.json | 54 ++++++ websites/N/Novel Mania/presence.ts | 222 +++++++++++++++++++++++++ 3 files changed, 337 insertions(+) create mode 100644 websites/N/Novel Mania/metadata.json create mode 100644 websites/N/Novel Mania/novelmania.json create mode 100644 websites/N/Novel Mania/presence.ts diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json new file mode 100644 index 000000000000..3fba9a9aa0ad --- /dev/null +++ b/websites/N/Novel Mania/metadata.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://schemas.premid.app/metadata/1.17", + "apiVersion": 1, + "author": { + "name": "Shuichi", + "id": "179440483796385792" + }, + "service": "Novel Mania", + "altnames": [ + "NovelMania" + ], + "description": { + "en": "Read webnovels in Portuguese anywhere and anytime.", + "pt-br": "Leia webnovels em português a qualquer momento e a qualquer hora." + }, + "url": "novelmania.com.br", + "regExp": "^https?[:][/][/]([a-z0-9-]+[.])*novelmania[.]com[.]br[/]", + "version": "2.0.0", + "logo": "https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png", + "thumbnail": "https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/thumbnail.jpg", + "color": "#008cff", + "category": "other", + "tags": [ + "novel", + "lightnovel", + "light novel", + "webnovel", + "web novel", + "reader", + "scanlator", + "translation", + "book" + ], + "settings": [ + { + "id": "lang", + "multiLanguage": true + }, + { + "id": "hideInfo", + "title": "Privacy Mode", + "icon": "fas fa-compress-arrows-alt", + "value": false + }, + { + "id": "showButtons", + "title": "Show Buttons", + "icon": "fas fa-compress-arrows-alt", + "value": true, + "if": { + "showInfo": false + } + }, + { + "id": "showTimestamp", + "title": "Show Timestamp", + "icon": "fas fa-clock", + "value": true + } + ] +} \ No newline at end of file diff --git a/websites/N/Novel Mania/novelmania.json b/websites/N/Novel Mania/novelmania.json new file mode 100644 index 000000000000..a094bd0bab9c --- /dev/null +++ b/websites/N/Novel Mania/novelmania.json @@ -0,0 +1,54 @@ +{ + "novelmania.volume": { + "description": "Text for when user's reading a novel with volumes.", + "message": "Volume" + }, + "novelmania.book": { + "description": "Text for when user's reading a novel with books.", + "message": "Book" + }, + "novelmania.viewingProfile": { + "description": "Text for when user's visualizing someone's profile.", + "message": "Viewing profile" + }, + "novelmania.lists": { + "description": "Text for when user's visualizing 'lists' page.", + "message": "listas" + }, + "novelmania.news": { + "description": "Text for when user's visualizing 'news' page.", + "message": "news" + }, + "novelmania.genre": { + "description": "Text for when user's browsing genres.", + "message": "genres" + }, + "novelmania.novel": { + "description": "Just a placeholder for a novel title. It doesn't matter what it is, it will be replaced by the actual title.", + "message": "novel" + }, + "novelmania.readNovelButton": { + "description": "", + "message": "Read this novel!" + }, + "novelmania.readChapterButton": { + "description": "Text from 'reading chapter button'. Call to read along!", + "message": "Read this chapter!" + }, + "novelmania.visitWebsiteButton": { + "description": "Text from 'visit website button'.", + "message": "Visit this website!" + }, + "novelmania.readListButton": { + "description": "Text from 'read this list button'.", + "message": "Read this list!" + }, + "novelmania.readNewsButton": { + "description": "Text from 'read this news button'.", + "message": "Read this news!" + }, + "novelmania.visitUserProfileButton": { + "description": "Text from 'spy on this user button'. Naughty, naughty...", + "message": "Spy on this user!" + } +} \ No newline at end of file diff --git a/websites/N/Novel Mania/presence.ts b/websites/N/Novel Mania/presence.ts new file mode 100644 index 000000000000..069fb51e17dd --- /dev/null +++ b/websites/N/Novel Mania/presence.ts @@ -0,0 +1,222 @@ +import { ActivityType, Assets } from 'premid' + +const presence = new Presence({ + clientId: '738522217221980222', +}) +const browsingTimestamp = Math.floor(Date.now() / 1000) +enum ActivityAssets { + Logo = 'https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png', +} +async function getStrings() { + return presence.getStrings({ + browse: 'general.browsing', + home: 'general.viewHome', + privacy: 'general.privacy', + reading: 'general.reading', + view: 'general.view', + volume: 'novelmania.volume', + profile: 'general.viewProfile', + lists: 'novelmania.lists', + news: 'novelmania.news', + genre: 'novelmania.genre', + novel: 'novelmania.novel', + readNovelButton: 'novelmania.readNovelButton', + readChapterButton: 'novelmania.readChapterButton', + visitWebsiteButton: 'novelmania.visitWebsiteButton', + readListButton: 'novelmania.readListButton', + readNewsButton: 'novelmania.readNewsButton', + visitUserProfileButton: 'novelmania.visitUserProfileButton', + }) +} +let oldUserLanguage: string | null = null +let strings: Awaited> + +presence.on('UpdateData', async () => { + strings = await getStrings() + + const [showButtons, showTime, hideInfo, userLanguage] = await Promise.all([ + presence.getSetting('showButtons') || true, + presence.getSetting('showTimestamp') || true, + presence.getSetting('hideInfo') || false, + presence.getSetting('lang').catch(() => 'pt') + ]); + + if (oldUserLanguage !== userLanguage) { + oldUserLanguage = userLanguage + strings = await getStrings() + } + + + const presenceData: PresenceData = { + largeImageKey: ActivityAssets.Logo, + startTimestamp: showTime ? browsingTimestamp : undefined, + type: ActivityType.Watching + + } + const { pathname, origin } = window.location + const cleanPath = pathname.replace(/\/$/, '') || '/' + const [part1, part2, part3, part4] = cleanPath.slice(1).split('/') // page, slug (if any), chapter (if any), volume/book (if any) + + const privacyCheck = (foo: any): string => { + return !hideInfo ? foo : strings.privacy + } + + const getPageTitle = (): string => document.querySelector('#main h1')?.textContent || strings.novel + let buttons: [ButtonData, ButtonData?] | undefined + + switch (part1) { + case '': + if (cleanPath === '/') { + presenceData.state = strings.home; + + buttons = [ + { + label: strings.visitWebsiteButton, + url: origin, + }, + ] + } + break + + case 'u': /* Seeing some user profile */ + if (part2) { + presenceData.details = `${strings.profile}` + presenceData.state = privacyCheck(getPageTitle()); + buttons = [ + { + label: strings.visitUserProfileButton, + url: origin + `/u/${part2}`, + }, + ] + } + break + + case 'novels': + if (!part2) { /* Searching some novel */ + presenceData.details = `${strings.browse}`; + presenceData.state = privacyCheck(getPageTitle()); + + const params = new URLSearchParams(window.location.search); + const searchTerm = (document.querySelector('input[name="q"]') as HTMLInputElement)?.value || params.get('q'); + + if (searchTerm) { + presenceData.details = `${strings.browse} ${privacyCheck(decodeURIComponent(searchTerm))}`; + } + + // Extrai nomes dos filtros - apenas se houver busca ou query params + if (searchTerm || window.location.search) { + const badges = document.querySelectorAll('span.inline-flex.rounded-full.border'); + if (badges.length > 0) { + const visibleFilters: string[] = []; + for (let i = 0; i < badges.length; i++) { + const text = badges[i]?.childNodes[0]?.textContent?.trim(); + if (text) visibleFilters.push(text); + } + if (visibleFilters.length) { + presenceData.state = privacyCheck(visibleFilters.join(', ')); + } + } + } + break + } + if (part3 === 'capitulos' && part4) { /* Reading some novel's chapter */ + const novelName = document.querySelector("#conteudo-principal > div > header > div > p")?.textContent || part2?.split('-').slice(0, 2).join(' ') || strings.novel; + const noveltype = document.querySelector("#conteudo-principal > div > main > div > div > header > p")?.textContent || part4?.split('-').slice(0, 2).join(' ') || strings.volume; + const currentChapTitle = document.querySelector("#reader-chapter-title")?.textContent || novelName; + + presenceData.details = `${strings.reading} ${privacyCheck(novelName)}`; + presenceData.state = `${privacyCheck(currentChapTitle)} - ${privacyCheck(noveltype)}`; + presenceData.smallImageKey = Assets.Reading + + buttons = [ + { + label: strings.readNovelButton, + url: origin + `/novels/${part2}`, + }, + { + label: strings.readChapterButton, + url: origin + `/novels/${part2}/capitulos/${part4}`, + }, + ] + break + } + if (part2) { /* At some novel's page */ + const novelName = document.querySelector("#main > div > h1")?.textContent || strings.novel + presenceData.state = ` ${strings.view} ${privacyCheck(novelName)}`; + + buttons = [ + { + label: strings.readNovelButton, + url: origin + `/novels/${part2}`, + }, + ] + } + break + + case 'listas': /* Searching some lists */ + if (!part2) { + presenceData.state = `${strings.browse} ${strings.lists}`; + presenceData.smallImageKey = Assets.Reading + break + } + + presenceData.details = `${strings.view} ${strings.lists}`; + presenceData.state = `${privacyCheck(getPageTitle())} - ${privacyCheck(document.querySelector("#main > div > div:nth-child(2) > a")?.textContent || strings.novel)}`; + presenceData.smallImageKey = Assets.Reading + + buttons = [ + { + label: strings.readListButton, + url: origin + `/listas/${part2}`, + }, + ] + break + + case 'noticias': + if (!part2) { /* Searching some news */ + presenceData.details = `${strings.reading} ${strings.news}`; /* reading news list */ + presenceData.state = `${privacyCheck(getPageTitle())}`; + presenceData.smallImageKey = Assets.Reading + break + } + + presenceData.details = `${strings.reading} ${strings.news}`; /* reading a news */ + presenceData.state = `${privacyCheck(getPageTitle())}`; + presenceData.smallImageKey = Assets.Reading + + buttons = [ + { + label: strings.readNewsButton, + url: origin + `/noticias/${part2}`, + }, + ] + break + + case 'genero': /* Browsing some genres */ + if (part2) { + presenceData.details = `${strings.browse} ${strings.genre}`; + presenceData.state = privacyCheck(getPageTitle()); + presenceData.smallImageKey = Assets.Reading + } + break + + default: /* At any other page, it doesn't matter */ + presenceData.state = `${strings.view} ${privacyCheck(getPageTitle())}`; + presenceData.smallImageKey = Assets.Reading + break + } + + if (showButtons && buttons && (cleanPath === '/' || !hideInfo)) + presenceData.buttons = buttons + + // Set the activity + if (presenceData.state) { + presence.setActivity(presenceData) + + if (!showTime) + delete presenceData.startTimestamp + + } else { + presence.clearActivity() + } +}) From 214401d9cd2d13c654eff2de00f612135136d712 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 11:09:09 -0300 Subject: [PATCH 05/20] small change... --- websites/N/Novel Mania/metadata.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json index 3fba9a9aa0ad..2b9422a0a967 100644 --- a/websites/N/Novel Mania/metadata.json +++ b/websites/N/Novel Mania/metadata.json @@ -11,6 +11,7 @@ ], "description": { "en": "Read webnovels in Portuguese anywhere and anytime.", + "nl": "Lees overal en altijd webnovels in het Portugees.", "pt-br": "Leia webnovels em português a qualquer momento e a qualquer hora." }, "url": "novelmania.com.br", @@ -22,6 +23,8 @@ "category": "other", "tags": [ "novel", + "mania", + "translator", "lightnovel", "light novel", "webnovel", From 5532a1eb27fe5d1871ef28278d77ac9de4b71ff2 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 11:20:09 -0300 Subject: [PATCH 06/20] =?UTF-8?q?small=20change=20=C2=B2...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- websites/N/Novel Mania/presence.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websites/N/Novel Mania/presence.ts b/websites/N/Novel Mania/presence.ts index 069fb51e17dd..925909a0277f 100644 --- a/websites/N/Novel Mania/presence.ts +++ b/websites/N/Novel Mania/presence.ts @@ -49,7 +49,7 @@ presence.on('UpdateData', async () => { const presenceData: PresenceData = { largeImageKey: ActivityAssets.Logo, - startTimestamp: showTime ? browsingTimestamp : undefined, + startTimestamp: browsingTimestamp, type: ActivityType.Watching } From b7514b555fd67de26f145bb562a5750b6b190fe1 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 11:25:03 -0300 Subject: [PATCH 07/20] =?UTF-8?q?small=20change=20=C2=B3...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- websites/N/Novel Mania/metadata.json | 2 +- websites/N/Novel Mania/novelmania.json | 106 ++++++++++++------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json index 2b9422a0a967..e2ac14e93707 100644 --- a/websites/N/Novel Mania/metadata.json +++ b/websites/N/Novel Mania/metadata.json @@ -61,4 +61,4 @@ "value": true } ] -} \ No newline at end of file +} diff --git a/websites/N/Novel Mania/novelmania.json b/websites/N/Novel Mania/novelmania.json index a094bd0bab9c..08ba60ff2a6e 100644 --- a/websites/N/Novel Mania/novelmania.json +++ b/websites/N/Novel Mania/novelmania.json @@ -1,54 +1,54 @@ { - "novelmania.volume": { - "description": "Text for when user's reading a novel with volumes.", - "message": "Volume" - }, - "novelmania.book": { - "description": "Text for when user's reading a novel with books.", - "message": "Book" - }, - "novelmania.viewingProfile": { - "description": "Text for when user's visualizing someone's profile.", - "message": "Viewing profile" - }, - "novelmania.lists": { - "description": "Text for when user's visualizing 'lists' page.", - "message": "listas" - }, - "novelmania.news": { - "description": "Text for when user's visualizing 'news' page.", - "message": "news" - }, - "novelmania.genre": { - "description": "Text for when user's browsing genres.", - "message": "genres" - }, - "novelmania.novel": { - "description": "Just a placeholder for a novel title. It doesn't matter what it is, it will be replaced by the actual title.", - "message": "novel" - }, - "novelmania.readNovelButton": { - "description": "", - "message": "Read this novel!" - }, - "novelmania.readChapterButton": { - "description": "Text from 'reading chapter button'. Call to read along!", - "message": "Read this chapter!" - }, - "novelmania.visitWebsiteButton": { - "description": "Text from 'visit website button'.", - "message": "Visit this website!" - }, - "novelmania.readListButton": { - "description": "Text from 'read this list button'.", - "message": "Read this list!" - }, - "novelmania.readNewsButton": { - "description": "Text from 'read this news button'.", - "message": "Read this news!" - }, - "novelmania.visitUserProfileButton": { - "description": "Text from 'spy on this user button'. Naughty, naughty...", - "message": "Spy on this user!" - } -} \ No newline at end of file + "novelmania.volume": { + "description": "Text for when user's reading a novel with volumes.", + "message": "Volume" + }, + "novelmania.book": { + "description": "Text for when user's reading a novel with books.", + "message": "Book" + }, + "novelmania.viewingProfile": { + "description": "Text for when user's visualizing someone's profile.", + "message": "Viewing profile" + }, + "novelmania.lists": { + "description": "Text for when user's visualizing 'lists' page.", + "message": "listas" + }, + "novelmania.news": { + "description": "Text for when user's visualizing 'news' page.", + "message": "news" + }, + "novelmania.genre": { + "description": "Text for when user's browsing genres.", + "message": "genres" + }, + "novelmania.novel": { + "description": "Just a placeholder for a novel title. It doesn't matter what it is, it will be replaced by the actual title.", + "message": "novel" + }, + "novelmania.readNovelButton": { + "description": "", + "message": "Read this novel!" + }, + "novelmania.readChapterButton": { + "description": "Text from 'reading chapter button'. Call to read along!", + "message": "Read this chapter!" + }, + "novelmania.visitWebsiteButton": { + "description": "Text from 'visit website button'.", + "message": "Visit this website!" + }, + "novelmania.readListButton": { + "description": "Text from 'read this list button'.", + "message": "Read this list!" + }, + "novelmania.readNewsButton": { + "description": "Text from 'read this news button'.", + "message": "Read this news!" + }, + "novelmania.visitUserProfileButton": { + "description": "Text from 'spy on this user button'. Naughty, naughty...", + "message": "Spy on this user!" + } +} From 661e52f81eb15b5f383c1d74a6b3713a66565116 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 11:56:39 -0300 Subject: [PATCH 08/20] Fixing lint match pattern error in tags --- websites/N/Novel Mania/metadata.json | 15 ++--- websites/N/Novel Mania/presence.ts | 84 ++++++++++++++-------------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json index e2ac14e93707..e0549df10314 100644 --- a/websites/N/Novel Mania/metadata.json +++ b/websites/N/Novel Mania/metadata.json @@ -22,17 +22,18 @@ "color": "#008cff", "category": "other", "tags": [ - "novel", - "mania", - "translator", + "novel-mania", + "translation", "lightnovel", - "light novel", + "light-novel-translation", "webnovel", - "web novel", + "web-novel-translation", "reader", "scanlator", - "translation", - "book" + "novel-translation", + "book", + "books", + "book-translation" ], "settings": [ { diff --git a/websites/N/Novel Mania/presence.ts b/websites/N/Novel Mania/presence.ts index 925909a0277f..ef4c3d946ea0 100644 --- a/websites/N/Novel Mania/presence.ts +++ b/websites/N/Novel Mania/presence.ts @@ -38,19 +38,18 @@ presence.on('UpdateData', async () => { presence.getSetting('showButtons') || true, presence.getSetting('showTimestamp') || true, presence.getSetting('hideInfo') || false, - presence.getSetting('lang').catch(() => 'pt') - ]); + presence.getSetting('lang').catch(() => 'pt'), + ]) if (oldUserLanguage !== userLanguage) { oldUserLanguage = userLanguage strings = await getStrings() } - const presenceData: PresenceData = { largeImageKey: ActivityAssets.Logo, startTimestamp: browsingTimestamp, - type: ActivityType.Watching + type: ActivityType.Watching, } const { pathname, origin } = window.location @@ -67,7 +66,7 @@ presence.on('UpdateData', async () => { switch (part1) { case '': if (cleanPath === '/') { - presenceData.state = strings.home; + presenceData.state = strings.home buttons = [ { @@ -81,11 +80,11 @@ presence.on('UpdateData', async () => { case 'u': /* Seeing some user profile */ if (part2) { presenceData.details = `${strings.profile}` - presenceData.state = privacyCheck(getPageTitle()); + presenceData.state = privacyCheck(getPageTitle()) buttons = [ { label: strings.visitUserProfileButton, - url: origin + `/u/${part2}`, + url: `${origin}/u/${part2}`, }, ] } @@ -93,61 +92,62 @@ presence.on('UpdateData', async () => { case 'novels': if (!part2) { /* Searching some novel */ - presenceData.details = `${strings.browse}`; - presenceData.state = privacyCheck(getPageTitle()); + presenceData.details = `${strings.browse}` + presenceData.state = privacyCheck(getPageTitle()) - const params = new URLSearchParams(window.location.search); - const searchTerm = (document.querySelector('input[name="q"]') as HTMLInputElement)?.value || params.get('q'); + const params = new URLSearchParams(window.location.search) + const searchTerm = (document.querySelector('input[name="q"]') as HTMLInputElement)?.value || params.get('q') if (searchTerm) { - presenceData.details = `${strings.browse} ${privacyCheck(decodeURIComponent(searchTerm))}`; + presenceData.details = `${strings.browse} ${privacyCheck(decodeURIComponent(searchTerm))}` } // Extrai nomes dos filtros - apenas se houver busca ou query params if (searchTerm || window.location.search) { - const badges = document.querySelectorAll('span.inline-flex.rounded-full.border'); + const badges = document.querySelectorAll('span.inline-flex.rounded-full.border') if (badges.length > 0) { - const visibleFilters: string[] = []; + const visibleFilters: string[] = [] for (let i = 0; i < badges.length; i++) { - const text = badges[i]?.childNodes[0]?.textContent?.trim(); - if (text) visibleFilters.push(text); + const text = badges[i]?.childNodes[0]?.textContent?.trim() + if (text) + visibleFilters.push(text) } if (visibleFilters.length) { - presenceData.state = privacyCheck(visibleFilters.join(', ')); + presenceData.state = privacyCheck(visibleFilters.join(', ')) } } } break } if (part3 === 'capitulos' && part4) { /* Reading some novel's chapter */ - const novelName = document.querySelector("#conteudo-principal > div > header > div > p")?.textContent || part2?.split('-').slice(0, 2).join(' ') || strings.novel; - const noveltype = document.querySelector("#conteudo-principal > div > main > div > div > header > p")?.textContent || part4?.split('-').slice(0, 2).join(' ') || strings.volume; - const currentChapTitle = document.querySelector("#reader-chapter-title")?.textContent || novelName; + const novelName = document.querySelector('#conteudo-principal > div > header > div > p')?.textContent || part2?.split('-').slice(0, 2).join(' ') || strings.novel + const noveltype = document.querySelector('#conteudo-principal > div > main > div > div > header > p')?.textContent || part4?.split('-').slice(0, 2).join(' ') || strings.volume + const currentChapTitle = document.querySelector('#reader-chapter-title')?.textContent || novelName - presenceData.details = `${strings.reading} ${privacyCheck(novelName)}`; - presenceData.state = `${privacyCheck(currentChapTitle)} - ${privacyCheck(noveltype)}`; + presenceData.details = `${strings.reading} ${privacyCheck(novelName)}` + presenceData.state = `${privacyCheck(currentChapTitle)} - ${privacyCheck(noveltype)}` presenceData.smallImageKey = Assets.Reading buttons = [ { label: strings.readNovelButton, - url: origin + `/novels/${part2}`, + url: `${origin}/novels/${part2}`, }, { label: strings.readChapterButton, - url: origin + `/novels/${part2}/capitulos/${part4}`, + url: `${origin}/novels/${part2}/capitulos/${part4}`, }, ] break } if (part2) { /* At some novel's page */ - const novelName = document.querySelector("#main > div > h1")?.textContent || strings.novel - presenceData.state = ` ${strings.view} ${privacyCheck(novelName)}`; + const novelName = document.querySelector('#main > div > h1')?.textContent || strings.novel + presenceData.state = ` ${strings.view} ${privacyCheck(novelName)}` buttons = [ { label: strings.readNovelButton, - url: origin + `/novels/${part2}`, + url: `${origin}/novels/${part2}`, }, ] } @@ -155,53 +155,53 @@ presence.on('UpdateData', async () => { case 'listas': /* Searching some lists */ if (!part2) { - presenceData.state = `${strings.browse} ${strings.lists}`; + presenceData.state = `${strings.browse} ${strings.lists}` presenceData.smallImageKey = Assets.Reading break } - presenceData.details = `${strings.view} ${strings.lists}`; - presenceData.state = `${privacyCheck(getPageTitle())} - ${privacyCheck(document.querySelector("#main > div > div:nth-child(2) > a")?.textContent || strings.novel)}`; + presenceData.details = `${strings.view} ${strings.lists}` + presenceData.state = `${privacyCheck(getPageTitle())} - ${privacyCheck(document.querySelector('#main > div > div:nth-child(2) > a')?.textContent || strings.novel)}` presenceData.smallImageKey = Assets.Reading buttons = [ { label: strings.readListButton, - url: origin + `/listas/${part2}`, + url: `${origin}/listas/${part2}`, }, ] break case 'noticias': - if (!part2) { /* Searching some news */ - presenceData.details = `${strings.reading} ${strings.news}`; /* reading news list */ - presenceData.state = `${privacyCheck(getPageTitle())}`; + if (!part2) { /* Searching some news */ + presenceData.details = `${strings.reading} ${strings.news}` /* reading news list */ + presenceData.state = `${privacyCheck(getPageTitle())}` presenceData.smallImageKey = Assets.Reading break } - presenceData.details = `${strings.reading} ${strings.news}`; /* reading a news */ - presenceData.state = `${privacyCheck(getPageTitle())}`; + presenceData.details = `${strings.reading} ${strings.news}` /* reading a news */ + presenceData.state = `${privacyCheck(getPageTitle())}` presenceData.smallImageKey = Assets.Reading buttons = [ { label: strings.readNewsButton, - url: origin + `/noticias/${part2}`, + url: `${origin}/noticias/${part2}`, }, ] break case 'genero': /* Browsing some genres */ if (part2) { - presenceData.details = `${strings.browse} ${strings.genre}`; - presenceData.state = privacyCheck(getPageTitle()); + presenceData.details = `${strings.browse} ${strings.genre}` + presenceData.state = privacyCheck(getPageTitle()) presenceData.smallImageKey = Assets.Reading } break default: /* At any other page, it doesn't matter */ - presenceData.state = `${strings.view} ${privacyCheck(getPageTitle())}`; + presenceData.state = `${strings.view} ${privacyCheck(getPageTitle())}` presenceData.smallImageKey = Assets.Reading break } @@ -215,8 +215,8 @@ presence.on('UpdateData', async () => { if (!showTime) delete presenceData.startTimestamp - - } else { + } + else { presence.clearActivity() } }) From 70724745f8b17d7d9180a4aa13e0e8a43d5a5202 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 12:01:13 -0300 Subject: [PATCH 09/20] Translating a comment --- websites/N/Novel Mania/presence.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websites/N/Novel Mania/presence.ts b/websites/N/Novel Mania/presence.ts index ef4c3d946ea0..41eec86d417c 100644 --- a/websites/N/Novel Mania/presence.ts +++ b/websites/N/Novel Mania/presence.ts @@ -102,7 +102,7 @@ presence.on('UpdateData', async () => { presenceData.details = `${strings.browse} ${privacyCheck(decodeURIComponent(searchTerm))}` } - // Extrai nomes dos filtros - apenas se houver busca ou query params + /* Extract query params while searching for some novel */ if (searchTerm || window.location.search) { const badges = document.querySelectorAll('span.inline-flex.rounded-full.border') if (badges.length > 0) { From 9ddf4662b96e6e8b30639343f6638b50707a8986 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 12:25:46 -0300 Subject: [PATCH 10/20] Fix timestamp. Not in the mood to make one for each state --- websites/N/Novel Mania/presence.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/websites/N/Novel Mania/presence.ts b/websites/N/Novel Mania/presence.ts index 41eec86d417c..aa6cf3b0be02 100644 --- a/websites/N/Novel Mania/presence.ts +++ b/websites/N/Novel Mania/presence.ts @@ -3,7 +3,8 @@ import { ActivityType, Assets } from 'premid' const presence = new Presence({ clientId: '738522217221980222', }) -const browsingTimestamp = Math.floor(Date.now() / 1000) +let browsingTimestamp = Math.floor(Date.now() / 1000) + enum ActivityAssets { Logo = 'https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png', } @@ -94,7 +95,6 @@ presence.on('UpdateData', async () => { if (!part2) { /* Searching some novel */ presenceData.details = `${strings.browse}` presenceData.state = privacyCheck(getPageTitle()) - const params = new URLSearchParams(window.location.search) const searchTerm = (document.querySelector('input[name="q"]') as HTMLInputElement)?.value || params.get('q') @@ -127,7 +127,6 @@ presence.on('UpdateData', async () => { presenceData.details = `${strings.reading} ${privacyCheck(novelName)}` presenceData.state = `${privacyCheck(currentChapTitle)} - ${privacyCheck(noveltype)}` presenceData.smallImageKey = Assets.Reading - buttons = [ { label: strings.readNovelButton, @@ -143,7 +142,6 @@ presence.on('UpdateData', async () => { if (part2) { /* At some novel's page */ const novelName = document.querySelector('#main > div > h1')?.textContent || strings.novel presenceData.state = ` ${strings.view} ${privacyCheck(novelName)}` - buttons = [ { label: strings.readNovelButton, @@ -157,6 +155,7 @@ presence.on('UpdateData', async () => { if (!part2) { presenceData.state = `${strings.browse} ${strings.lists}` presenceData.smallImageKey = Assets.Reading + break } @@ -206,15 +205,14 @@ presence.on('UpdateData', async () => { break } - if (showButtons && buttons && (cleanPath === '/' || !hideInfo)) - presenceData.buttons = buttons - // Set the activity if (presenceData.state) { - presence.setActivity(presenceData) - if (!showTime) delete presenceData.startTimestamp + if (showButtons && buttons && (cleanPath === '/' || !hideInfo)) + presenceData.buttons = buttons + presence.setActivity(presenceData) + } else { presence.clearActivity() From 774fb8102285adb24ef7227138ff9872105376f1 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 12:52:29 -0300 Subject: [PATCH 11/20] Fix more lint errors --- websites/N/Novel Mania/presence.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/websites/N/Novel Mania/presence.ts b/websites/N/Novel Mania/presence.ts index aa6cf3b0be02..cdd226369f74 100644 --- a/websites/N/Novel Mania/presence.ts +++ b/websites/N/Novel Mania/presence.ts @@ -3,7 +3,7 @@ import { ActivityType, Assets } from 'premid' const presence = new Presence({ clientId: '738522217221980222', }) -let browsingTimestamp = Math.floor(Date.now() / 1000) +const browsingTimestamp = Math.floor(Date.now() / 1000) enum ActivityAssets { Logo = 'https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png', @@ -207,12 +207,13 @@ presence.on('UpdateData', async () => { // Set the activity if (presenceData.state) { - if (!showTime) + if (!showTime) { delete presenceData.startTimestamp - if (showButtons && buttons && (cleanPath === '/' || !hideInfo)) + } + if (showButtons && buttons && (cleanPath === '/' || !hideInfo)) { presenceData.buttons = buttons + } presence.setActivity(presenceData) - } else { presence.clearActivity() From 9e6b65648fd2cf28abd616a99592cac6cddf3991 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 13:02:56 -0300 Subject: [PATCH 12/20] I forgor my discord name have changed... --- websites/N/Novel Mania/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json index e0549df10314..2b933388751e 100644 --- a/websites/N/Novel Mania/metadata.json +++ b/websites/N/Novel Mania/metadata.json @@ -2,7 +2,7 @@ "$schema": "https://schemas.premid.app/metadata/1.17", "apiVersion": 1, "author": { - "name": "Shuichi", + "name": "obudista", "id": "179440483796385792" }, "service": "Novel Mania", @@ -62,4 +62,4 @@ "value": true } ] -} +} \ No newline at end of file From 877623fb26d7c9ad4cd76578d34f58848ecd7af7 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 13:18:14 -0300 Subject: [PATCH 13/20] This bot's nuts --- websites/N/Novel Mania/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json index 2b933388751e..65fd4ac93d75 100644 --- a/websites/N/Novel Mania/metadata.json +++ b/websites/N/Novel Mania/metadata.json @@ -62,4 +62,4 @@ "value": true } ] -} \ No newline at end of file +} From 0e87e1637e7551f97174fa578c841c5968821f74 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Fri, 3 Jul 2026 16:32:40 -0300 Subject: [PATCH 14/20] it went unnoticed... --- websites/N/Novel Mania/novelmania.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/websites/N/Novel Mania/novelmania.json b/websites/N/Novel Mania/novelmania.json index 08ba60ff2a6e..bdce914c113f 100644 --- a/websites/N/Novel Mania/novelmania.json +++ b/websites/N/Novel Mania/novelmania.json @@ -13,7 +13,7 @@ }, "novelmania.lists": { "description": "Text for when user's visualizing 'lists' page.", - "message": "listas" + "message": "lists" }, "novelmania.news": { "description": "Text for when user's visualizing 'news' page.", @@ -28,7 +28,7 @@ "message": "novel" }, "novelmania.readNovelButton": { - "description": "", + "description": "Text from 'viewing novel's button'.", "message": "Read this novel!" }, "novelmania.readChapterButton": { From 3f05b2b53e3c2730a77751862c836a1041fe0670 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Tue, 7 Jul 2026 16:31:25 -0300 Subject: [PATCH 15/20] Fixing buttons, tags and some selections. Removing unnecessary translations and replacing some others. --- websites/N/Novel Mania/metadata.json | 3 +- websites/N/Novel Mania/novelmania.json | 20 --- websites/N/Novel Mania/presence.ts | 182 ++++++++++++------------- 3 files changed, 87 insertions(+), 118 deletions(-) diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json index 65fd4ac93d75..8d3c4f520fad 100644 --- a/websites/N/Novel Mania/metadata.json +++ b/websites/N/Novel Mania/metadata.json @@ -22,7 +22,6 @@ "color": "#008cff", "category": "other", "tags": [ - "novel-mania", "translation", "lightnovel", "light-novel-translation", @@ -52,7 +51,7 @@ "icon": "fas fa-compress-arrows-alt", "value": true, "if": { - "showInfo": false + "hideInfo": false } }, { diff --git a/websites/N/Novel Mania/novelmania.json b/websites/N/Novel Mania/novelmania.json index bdce914c113f..df5b8d605a2d 100644 --- a/websites/N/Novel Mania/novelmania.json +++ b/websites/N/Novel Mania/novelmania.json @@ -1,20 +1,4 @@ { - "novelmania.volume": { - "description": "Text for when user's reading a novel with volumes.", - "message": "Volume" - }, - "novelmania.book": { - "description": "Text for when user's reading a novel with books.", - "message": "Book" - }, - "novelmania.viewingProfile": { - "description": "Text for when user's visualizing someone's profile.", - "message": "Viewing profile" - }, - "novelmania.lists": { - "description": "Text for when user's visualizing 'lists' page.", - "message": "lists" - }, "novelmania.news": { "description": "Text for when user's visualizing 'news' page.", "message": "news" @@ -35,10 +19,6 @@ "description": "Text from 'reading chapter button'. Call to read along!", "message": "Read this chapter!" }, - "novelmania.visitWebsiteButton": { - "description": "Text from 'visit website button'.", - "message": "Visit this website!" - }, "novelmania.readListButton": { "description": "Text from 'read this list button'.", "message": "Read this list!" diff --git a/websites/N/Novel Mania/presence.ts b/websites/N/Novel Mania/presence.ts index cdd226369f74..0d5965c1ce1e 100644 --- a/websites/N/Novel Mania/presence.ts +++ b/websites/N/Novel Mania/presence.ts @@ -3,8 +3,15 @@ import { ActivityType, Assets } from 'premid' const presence = new Presence({ clientId: '738522217221980222', }) -const browsingTimestamp = Math.floor(Date.now() / 1000) +let browsingTimestamp = Math.floor(Date.now() / 1000) +let lastSlug: string = '' +function updateTimestampBySlug(slug: string) { + if (lastSlug !== slug) { + lastSlug = slug + browsingTimestamp = Math.floor(Date.now() / 1000) + } +} enum ActivityAssets { Logo = 'https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png', } @@ -12,18 +19,22 @@ async function getStrings() { return presence.getStrings({ browse: 'general.browsing', home: 'general.viewHome', + page: 'general.page', + viewPage: 'general.buttonViewPage', privacy: 'general.privacy', reading: 'general.reading', view: 'general.view', + chapter: 'general.chapter', volume: 'novelmania.volume', profile: 'general.viewProfile', - lists: 'novelmania.lists', + lists: 'general.viewAList', + search: 'general.search', + searchfor: 'general.searchFor', news: 'novelmania.news', genre: 'novelmania.genre', novel: 'novelmania.novel', readNovelButton: 'novelmania.readNovelButton', readChapterButton: 'novelmania.readChapterButton', - visitWebsiteButton: 'novelmania.visitWebsiteButton', readListButton: 'novelmania.readListButton', readNewsButton: 'novelmania.readNewsButton', visitUserProfileButton: 'novelmania.visitUserProfileButton', @@ -33,8 +44,6 @@ let oldUserLanguage: string | null = null let strings: Awaited> presence.on('UpdateData', async () => { - strings = await getStrings() - const [showButtons, showTime, hideInfo, userLanguage] = await Promise.all([ presence.getSetting('showButtons') || true, presence.getSetting('showTimestamp') || true, @@ -49,18 +58,12 @@ presence.on('UpdateData', async () => { const presenceData: PresenceData = { largeImageKey: ActivityAssets.Logo, - startTimestamp: browsingTimestamp, type: ActivityType.Watching, } const { pathname, origin } = window.location const cleanPath = pathname.replace(/\/$/, '') || '/' const [part1, part2, part3, part4] = cleanPath.slice(1).split('/') // page, slug (if any), chapter (if any), volume/book (if any) - - const privacyCheck = (foo: any): string => { - return !hideInfo ? foo : strings.privacy - } - const getPageTitle = (): string => document.querySelector('#main h1')?.textContent || strings.novel let buttons: [ButtonData, ButtonData?] | undefined @@ -68,38 +71,34 @@ presence.on('UpdateData', async () => { case '': if (cleanPath === '/') { presenceData.state = strings.home - - buttons = [ - { - label: strings.visitWebsiteButton, - url: origin, - }, - ] } break - case 'u': /* Seeing some user profile */ + updateTimestampBySlug('u') + if (hideInfo) { + presenceData.state = `${strings.profile} ${strings.privacy}` + break + } if (part2) { presenceData.details = `${strings.profile}` - presenceData.state = privacyCheck(getPageTitle()) - buttons = [ - { - label: strings.visitUserProfileButton, - url: `${origin}/u/${part2}`, - }, - ] + presenceData.state = getPageTitle() + buttons = [{ label: strings.visitUserProfileButton, url: `${origin}/u/${part2}` }] } break - case 'novels': if (!part2) { /* Searching some novel */ - presenceData.details = `${strings.browse}` - presenceData.state = privacyCheck(getPageTitle()) + updateTimestampBySlug('novel-searching') + if (hideInfo) { + presenceData.state = `${strings.browse} ${strings.novel}` + break + } + presenceData.details = `${strings.search}` + presenceData.state = getPageTitle() const params = new URLSearchParams(window.location.search) - const searchTerm = (document.querySelector('input[name="q"]') as HTMLInputElement)?.value || params.get('q') + const searchTerm = (document.querySelector('input[name="q"]'))?.value || params.get('q') if (searchTerm) { - presenceData.details = `${strings.browse} ${privacyCheck(decodeURIComponent(searchTerm))}` + presenceData.details = `${strings.searchfor} ${decodeURIComponent(searchTerm)}` } /* Extract query params while searching for some novel */ @@ -113,104 +112,95 @@ presence.on('UpdateData', async () => { visibleFilters.push(text) } if (visibleFilters.length) { - presenceData.state = privacyCheck(visibleFilters.join(', ')) + presenceData.state = visibleFilters.join(', ') } } } - break } if (part3 === 'capitulos' && part4) { /* Reading some novel's chapter */ - const novelName = document.querySelector('#conteudo-principal > div > header > div > p')?.textContent || part2?.split('-').slice(0, 2).join(' ') || strings.novel - const noveltype = document.querySelector('#conteudo-principal > div > main > div > div > header > p')?.textContent || part4?.split('-').slice(0, 2).join(' ') || strings.volume + updateTimestampBySlug(`novel-${part2}-chapter-${part4}`) + if (hideInfo) { + presenceData.state = `${strings.reading} ${strings.novel} ${strings.chapter}` + break + } + const novelName = document.querySelector('#conteudo-principal > div > header > div > p')?.textContent || part2?.split('-').slice(0, 2).join(' ') || part2?.split('-').join(' ') + const noveltype = document.querySelector('#conteudo-principal > div > main > div > div > header > p')?.textContent || part4?.split('-').slice(0, 2).join(' ') || part4?.split('-').join(' ') const currentChapTitle = document.querySelector('#reader-chapter-title')?.textContent || novelName - presenceData.details = `${strings.reading} ${privacyCheck(novelName)}` - presenceData.state = `${privacyCheck(currentChapTitle)} - ${privacyCheck(noveltype)}` - presenceData.smallImageKey = Assets.Reading - buttons = [ - { - label: strings.readNovelButton, - url: `${origin}/novels/${part2}`, - }, - { - label: strings.readChapterButton, - url: `${origin}/novels/${part2}/capitulos/${part4}`, - }, - ] - break + presenceData.details = `${strings.reading} ${novelName}` + presenceData.state = `${currentChapTitle} - ${noveltype}` + + buttons = [{ label: strings.readNovelButton, url: `${origin}/novels/${part2}` }, { label: strings.readChapterButton, url: `${origin}/novels/${part2}/capitulos/${part4}` }] } - if (part2) { /* At some novel's page */ - const novelName = document.querySelector('#main > div > h1')?.textContent || strings.novel - presenceData.state = ` ${strings.view} ${privacyCheck(novelName)}` - buttons = [ - { - label: strings.readNovelButton, - url: `${origin}/novels/${part2}`, - }, - ] + + if (part2 && !part3) { /* At some novel's page */ + updateTimestampBySlug(`novel-${part2}`) + const novelName = document.querySelector('#main > div > h1')?.textContent || part2.split('-').join(' ') + presenceData.state = `${strings.view} ${novelName}` + buttons = [{ label: strings.readNewsButton, url: `${origin}/novels/${part2}` }] } break - case 'listas': /* Searching some lists */ - if (!part2) { - presenceData.state = `${strings.browse} ${strings.lists}` - presenceData.smallImageKey = Assets.Reading - + updateTimestampBySlug('list-browsing') + if (!hideInfo && part2) { + updateTimestampBySlug('list-reading') + const listName = document.querySelector('#main > div > div:nth-child(2) > a')?.textContent + presenceData.details = strings.lists + presenceData.state = getPageTitle() || listName + + buttons = [{ label: strings.readListButton, url: `${origin}/listas/${part2}` }] break } - - presenceData.details = `${strings.view} ${strings.lists}` - presenceData.state = `${privacyCheck(getPageTitle())} - ${privacyCheck(document.querySelector('#main > div > div:nth-child(2) > a')?.textContent || strings.novel)}` - presenceData.smallImageKey = Assets.Reading - - buttons = [ - { - label: strings.readListButton, - url: `${origin}/listas/${part2}`, - }, - ] + presenceData.state = `${strings.lists}` break - case 'noticias': + updateTimestampBySlug('news-reading') + if (hideInfo) { + presenceData.state = `${strings.view} ${strings.news}` + break + } if (!part2) { /* Searching some news */ + updateTimestampBySlug('news-browsing') presenceData.details = `${strings.reading} ${strings.news}` /* reading news list */ - presenceData.state = `${privacyCheck(getPageTitle())}` - presenceData.smallImageKey = Assets.Reading + presenceData.state = `${getPageTitle()}` + break } presenceData.details = `${strings.reading} ${strings.news}` /* reading a news */ - presenceData.state = `${privacyCheck(getPageTitle())}` - presenceData.smallImageKey = Assets.Reading - - buttons = [ - { - label: strings.readNewsButton, - url: `${origin}/noticias/${part2}`, - }, - ] + presenceData.state = `${getPageTitle()}` + buttons = [{ label: strings.readNewsButton, url: `${origin}/noticias/${part2}` }] break - case 'genero': /* Browsing some genres */ + updateTimestampBySlug('genre-searching') + if (hideInfo) { + presenceData.state = `${strings.view} ${strings.genre}` + break + } if (part2) { presenceData.details = `${strings.browse} ${strings.genre}` - presenceData.state = privacyCheck(getPageTitle()) - presenceData.smallImageKey = Assets.Reading + presenceData.state = getPageTitle() } break - default: /* At any other page, it doesn't matter */ - presenceData.state = `${strings.view} ${privacyCheck(getPageTitle())}` - presenceData.smallImageKey = Assets.Reading + updateTimestampBySlug('page-reading') + if (hideInfo) { + presenceData.state = `${strings.reading} ${strings.page}` + break + } + presenceData.state = `${strings.view} ${getPageTitle()}` + + buttons = [{ label: strings.viewPage, url: origin + pathname }] break } + if (showTime) + presenceData.startTimestamp = browsingTimestamp - // Set the activity if (presenceData.state) { - if (!showTime) { + if (!showTime) delete presenceData.startTimestamp - } - if (showButtons && buttons && (cleanPath === '/' || !hideInfo)) { + if (showButtons && buttons && !hideInfo) { + presenceData.smallImageKey = Assets.Reading presenceData.buttons = buttons } presence.setActivity(presenceData) From 21c273b54208dec7962ce61f3a6e5424a3975a74 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Tue, 7 Jul 2026 18:55:27 -0300 Subject: [PATCH 16/20] Adding novel cover as largeImageKey https://i.imgur.com/o0i5WB0.png --- websites/N/Novel Mania/presence.ts | 106 ++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 24 deletions(-) diff --git a/websites/N/Novel Mania/presence.ts b/websites/N/Novel Mania/presence.ts index 0d5965c1ce1e..724fc20e62e1 100644 --- a/websites/N/Novel Mania/presence.ts +++ b/websites/N/Novel Mania/presence.ts @@ -12,31 +12,34 @@ function updateTimestampBySlug(slug: string) { browsingTimestamp = Math.floor(Date.now() / 1000) } } + enum ActivityAssets { Logo = 'https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png', } +let largeImage: any = ActivityAssets.Logo + async function getStrings() { return presence.getStrings({ - browse: 'general.browsing', - home: 'general.viewHome', page: 'general.page', - viewPage: 'general.buttonViewPage', + view: 'general.view', + news: 'novelmania.news', + home: 'general.viewHome', + search: 'general.search', + genre: 'novelmania.genre', + novel: 'novelmania.novel', + browse: 'general.browsing', privacy: 'general.privacy', reading: 'general.reading', - view: 'general.view', chapter: 'general.chapter', + lists: 'general.viewAList', volume: 'novelmania.volume', profile: 'general.viewProfile', - lists: 'general.viewAList', - search: 'general.search', searchfor: 'general.searchFor', - news: 'novelmania.news', - genre: 'novelmania.genre', - novel: 'novelmania.novel', - readNovelButton: 'novelmania.readNovelButton', - readChapterButton: 'novelmania.readChapterButton', + viewPage: 'general.buttonViewPage', readListButton: 'novelmania.readListButton', readNewsButton: 'novelmania.readNewsButton', + readNovelButton: 'novelmania.readNovelButton', + readChapterButton: 'novelmania.readChapterButton', visitUserProfileButton: 'novelmania.visitUserProfileButton', }) } @@ -57,15 +60,15 @@ presence.on('UpdateData', async () => { } const presenceData: PresenceData = { - largeImageKey: ActivityAssets.Logo, + largeImageKey: largeImage, type: ActivityType.Watching, - } const { pathname, origin } = window.location const cleanPath = pathname.replace(/\/$/, '') || '/' const [part1, part2, part3, part4] = cleanPath.slice(1).split('/') // page, slug (if any), chapter (if any), volume/book (if any) const getPageTitle = (): string => document.querySelector('#main h1')?.textContent || strings.novel let buttons: [ButtonData, ButtonData?] | undefined + let currentImage: any switch (part1) { case '': @@ -79,11 +82,17 @@ presence.on('UpdateData', async () => { presenceData.state = `${strings.profile} ${strings.privacy}` break } - if (part2) { - presenceData.details = `${strings.profile}` - presenceData.state = getPageTitle() - buttons = [{ label: strings.visitUserProfileButton, url: `${origin}/u/${part2}` }] + presenceData.details = `${strings.profile}` + presenceData.state = getPageTitle() + buttons = [{ label: strings.visitUserProfileButton, url: `${origin}/u/${part2}` }] + + currentImage = document.querySelector('#main img')?.src + if (currentImage) { + largeImage = currentImage + presenceData.largeImageKey = largeImage + presenceData.largeImageText = getPageTitle() || strings.profile } + break case 'novels': if (!part2) { /* Searching some novel */ @@ -120,7 +129,7 @@ presence.on('UpdateData', async () => { if (part3 === 'capitulos' && part4) { /* Reading some novel's chapter */ updateTimestampBySlug(`novel-${part2}-chapter-${part4}`) if (hideInfo) { - presenceData.state = `${strings.reading} ${strings.novel} ${strings.chapter}` + presenceData.state = `${strings.reading} ${strings.novel} ${strings.chapter.toLowerCase()}` break } const novelName = document.querySelector('#conteudo-principal > div > header > div > p')?.textContent || part2?.split('-').slice(0, 2).join(' ') || part2?.split('-').join(' ') @@ -131,13 +140,30 @@ presence.on('UpdateData', async () => { presenceData.state = `${currentChapTitle} - ${noveltype}` buttons = [{ label: strings.readNovelButton, url: `${origin}/novels/${part2}` }, { label: strings.readChapterButton, url: `${origin}/novels/${part2}/capitulos/${part4}` }] + + if (largeImage) { + presenceData.largeImageKey = largeImage + presenceData.largeImageText = novelName + } } if (part2 && !part3) { /* At some novel's page */ updateTimestampBySlug(`novel-${part2}`) + if (hideInfo) { + presenceData.state = `${strings.view} ${strings.novel}` + break + } const novelName = document.querySelector('#main > div > h1')?.textContent || part2.split('-').join(' ') presenceData.state = `${strings.view} ${novelName}` - buttons = [{ label: strings.readNewsButton, url: `${origin}/novels/${part2}` }] + buttons = [{ label: strings.readNovelButton, url: `${origin}/novels/${part2}` }] + + currentImage = document.querySelector('#main img')?.src + if (currentImage && currentImage !== largeImage) { + largeImage = currentImage + presenceData.largeImageKey = largeImage + delete presenceData.largeImageText + presenceData.largeImageText = novelName // Its not showing and idk why + } } break case 'listas': /* Searching some lists */ @@ -149,6 +175,14 @@ presence.on('UpdateData', async () => { presenceData.state = getPageTitle() || listName buttons = [{ label: strings.readListButton, url: `${origin}/listas/${part2}` }] + + currentImage = document.querySelector('#main img')?.src + if (currentImage && currentImage !== largeImage) { + largeImage = currentImage + presenceData.largeImageKey = largeImage + presenceData.largeImageText = getPageTitle() || listName + } + break } presenceData.state = `${strings.lists}` @@ -162,7 +196,7 @@ presence.on('UpdateData', async () => { if (!part2) { /* Searching some news */ updateTimestampBySlug('news-browsing') presenceData.details = `${strings.reading} ${strings.news}` /* reading news list */ - presenceData.state = `${getPageTitle()}` + presenceData.state = getPageTitle() break } @@ -170,6 +204,14 @@ presence.on('UpdateData', async () => { presenceData.details = `${strings.reading} ${strings.news}` /* reading a news */ presenceData.state = `${getPageTitle()}` buttons = [{ label: strings.readNewsButton, url: `${origin}/noticias/${part2}` }] + + currentImage = document.querySelector('#main img')?.src + if (currentImage) { + largeImage = currentImage + presenceData.largeImageKey = largeImage + presenceData.largeImageText = getPageTitle() || largeImage + } + break case 'genero': /* Browsing some genres */ updateTimestampBySlug('genre-searching') @@ -191,18 +233,34 @@ presence.on('UpdateData', async () => { presenceData.state = `${strings.view} ${getPageTitle()}` buttons = [{ label: strings.viewPage, url: origin + pathname }] + + currentImage = document.querySelector('#main img')?.src + if (currentImage && currentImage !== largeImage) { + largeImage = currentImage + presenceData.largeImageKey = largeImage + presenceData.largeImageText = getPageTitle() || largeImage + } + break } - if (showTime) - presenceData.startTimestamp = browsingTimestamp - + // Someday I'll make it better. + if (!lastSlug.includes(`novel-${part2}`) && !lastSlug.includes('list-reading') && !lastSlug.includes('news-reading') && !lastSlug.includes('page-reading') && part1 === '/') { + largeImage = ActivityAssets.Logo + presenceData.largeImageKey = largeImage + delete presenceData.largeImageText + } if (presenceData.state) { - if (!showTime) + if (!showTime) { delete presenceData.startTimestamp + } + else { + presenceData.startTimestamp = browsingTimestamp + } if (showButtons && buttons && !hideInfo) { presenceData.smallImageKey = Assets.Reading presenceData.buttons = buttons } + presence.setActivity(presenceData) } else { From ff9c6d265cc817704acfaab2875505278d860772 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Sat, 11 Jul 2026 23:13:29 -0300 Subject: [PATCH 17/20] updating username --- websites/N/Novel Mania/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json index 8d3c4f520fad..762bcc9b55e1 100644 --- a/websites/N/Novel Mania/metadata.json +++ b/websites/N/Novel Mania/metadata.json @@ -2,7 +2,7 @@ "$schema": "https://schemas.premid.app/metadata/1.17", "apiVersion": 1, "author": { - "name": "obudista", + "name": "hitwizard", "id": "179440483796385792" }, "service": "Novel Mania", @@ -61,4 +61,4 @@ "value": true } ] -} +} \ No newline at end of file From 60ae4fbf37d04f1f34170556583b8efa4927d5e0 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Sun, 12 Jul 2026 11:42:31 -0300 Subject: [PATCH 18/20] fix linting --- websites/N/Novel Mania/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json index 762bcc9b55e1..2a1c165a72f5 100644 --- a/websites/N/Novel Mania/metadata.json +++ b/websites/N/Novel Mania/metadata.json @@ -61,4 +61,4 @@ "value": true } ] -} \ No newline at end of file +} From 3bd5677f2d14140945f16d1c1f05f3ab765df089 Mon Sep 17 00:00:00 2001 From: Shulchl Date: Wed, 29 Jul 2026 17:21:56 -0300 Subject: [PATCH 19/20] Fix largeImageText not working, applying suggestions and improving code a bit --- .../{novelmania.json => Novel Mania.json} | 0 websites/N/Novel Mania/metadata.json | 4 +- websites/N/Novel Mania/presence.ts | 137 +++++++++--------- 3 files changed, 70 insertions(+), 71 deletions(-) rename websites/N/Novel Mania/{novelmania.json => Novel Mania.json} (100%) diff --git a/websites/N/Novel Mania/novelmania.json b/websites/N/Novel Mania/Novel Mania.json similarity index 100% rename from websites/N/Novel Mania/novelmania.json rename to websites/N/Novel Mania/Novel Mania.json diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json index 2a1c165a72f5..c6a7d489652d 100644 --- a/websites/N/Novel Mania/metadata.json +++ b/websites/N/Novel Mania/metadata.json @@ -42,7 +42,7 @@ { "id": "hideInfo", "title": "Privacy Mode", - "icon": "fas fa-compress-arrows-alt", + "icon": "fad fa-user-secret", "value": false }, { @@ -57,7 +57,7 @@ { "id": "showTimestamp", "title": "Show Timestamp", - "icon": "fas fa-clock", + "icon": "fad fa-stopwatch", "value": true } ] diff --git a/websites/N/Novel Mania/presence.ts b/websites/N/Novel Mania/presence.ts index 724fc20e62e1..e13c29537227 100644 --- a/websites/N/Novel Mania/presence.ts +++ b/websites/N/Novel Mania/presence.ts @@ -16,7 +16,7 @@ function updateTimestampBySlug(slug: string) { enum ActivityAssets { Logo = 'https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png', } -let largeImage: any = ActivityAssets.Logo +let defaultCover: ActivityAssets = ActivityAssets.Logo async function getStrings() { return presence.getStrings({ @@ -28,14 +28,13 @@ async function getStrings() { genre: 'novelmania.genre', novel: 'novelmania.novel', browse: 'general.browsing', - privacy: 'general.privacy', reading: 'general.reading', chapter: 'general.chapter', lists: 'general.viewAList', - volume: 'novelmania.volume', profile: 'general.viewProfile', searchfor: 'general.searchFor', viewPage: 'general.buttonViewPage', + privacyProfile: 'general.viewAProfile', readListButton: 'novelmania.readListButton', readNewsButton: 'novelmania.readNewsButton', readNovelButton: 'novelmania.readNovelButton', @@ -46,6 +45,9 @@ async function getStrings() { let oldUserLanguage: string | null = null let strings: Awaited> +// As there's no way to select a cover while reading, +// I'm storing the novel's name as a variable outside the loop just dont reset + presence.on('UpdateData', async () => { const [showButtons, showTime, hideInfo, userLanguage] = await Promise.all([ presence.getSetting('showButtons') || true, @@ -60,7 +62,7 @@ presence.on('UpdateData', async () => { } const presenceData: PresenceData = { - largeImageKey: largeImage, + largeImageKey: defaultCover, type: ActivityType.Watching, } const { pathname, origin } = window.location @@ -68,33 +70,35 @@ presence.on('UpdateData', async () => { const [part1, part2, part3, part4] = cleanPath.slice(1).split('/') // page, slug (if any), chapter (if any), volume/book (if any) const getPageTitle = (): string => document.querySelector('#main h1')?.textContent || strings.novel let buttons: [ButtonData, ButtonData?] | undefined - let currentImage: any + let currentCover: any + let currentPageTitle: string | undefined switch (part1) { - case '': + case '': { if (cleanPath === '/') { presenceData.state = strings.home } break - case 'u': /* Seeing some user profile */ + } + case 'u':{ /* Seeing some user profile */ updateTimestampBySlug('u') if (hideInfo) { - presenceData.state = `${strings.profile} ${strings.privacy}` + presenceData.state = `${strings.privacyProfile}` break } - presenceData.details = `${strings.profile}` - presenceData.state = getPageTitle() - buttons = [{ label: strings.visitUserProfileButton, url: `${origin}/u/${part2}` }] - currentImage = document.querySelector('#main img')?.src - if (currentImage) { - largeImage = currentImage - presenceData.largeImageKey = largeImage - presenceData.largeImageText = getPageTitle() || strings.profile + currentCover = document.querySelector('#main img')?.src + currentPageTitle = getPageTitle() + presenceData.state = `${strings.profile} ${currentPageTitle}` + + if (currentCover && currentCover !== defaultCover) { + defaultCover = currentCover } + buttons = [{ label: strings.visitUserProfileButton, url: `${origin}/u/${part2}` }] break - case 'novels': + } + case 'novels': { if (!part2) { /* Searching some novel */ updateTimestampBySlug('novel-searching') if (hideInfo) { @@ -141,10 +145,7 @@ presence.on('UpdateData', async () => { buttons = [{ label: strings.readNovelButton, url: `${origin}/novels/${part2}` }, { label: strings.readChapterButton, url: `${origin}/novels/${part2}/capitulos/${part4}` }] - if (largeImage) { - presenceData.largeImageKey = largeImage - presenceData.largeImageText = novelName - } + currentPageTitle = novelName } if (part2 && !part3) { /* At some novel's page */ @@ -155,18 +156,17 @@ presence.on('UpdateData', async () => { } const novelName = document.querySelector('#main > div > h1')?.textContent || part2.split('-').join(' ') presenceData.state = `${strings.view} ${novelName}` - buttons = [{ label: strings.readNovelButton, url: `${origin}/novels/${part2}` }] - currentImage = document.querySelector('#main img')?.src - if (currentImage && currentImage !== largeImage) { - largeImage = currentImage - presenceData.largeImageKey = largeImage - delete presenceData.largeImageText - presenceData.largeImageText = novelName // Its not showing and idk why + currentCover = document.querySelector('#main img')?.src + if (currentCover && currentCover !== defaultCover) { + currentPageTitle = novelName // Its not showing and idk why + defaultCover = currentCover } + buttons = [{ label: strings.readNovelButton, url: `${origin}/novels/${part2}` }] } break - case 'listas': /* Searching some lists */ + } + case 'listas':{ updateTimestampBySlug('list-browsing') if (!hideInfo && part2) { updateTimestampBySlug('list-reading') @@ -174,46 +174,45 @@ presence.on('UpdateData', async () => { presenceData.details = strings.lists presenceData.state = getPageTitle() || listName - buttons = [{ label: strings.readListButton, url: `${origin}/listas/${part2}` }] - - currentImage = document.querySelector('#main img')?.src - if (currentImage && currentImage !== largeImage) { - largeImage = currentImage - presenceData.largeImageKey = largeImage - presenceData.largeImageText = getPageTitle() || listName + currentCover = document.querySelector('#main img')?.src + if (currentCover && currentCover !== defaultCover) { + defaultCover = currentCover + currentPageTitle = getPageTitle() || listName } + buttons = [{ label: strings.readListButton, url: `${origin}/listas/${part2}` }] break } presenceData.state = `${strings.lists}` break - case 'noticias': + } + case 'noticias': { updateTimestampBySlug('news-reading') if (hideInfo) { presenceData.state = `${strings.view} ${strings.news}` break } + const newsTitle = getPageTitle() if (!part2) { /* Searching some news */ updateTimestampBySlug('news-browsing') - presenceData.details = `${strings.reading} ${strings.news}` /* reading news list */ - presenceData.state = getPageTitle() + presenceData.state = `${strings.reading} ${strings.news}` /* reading news list */ break } presenceData.details = `${strings.reading} ${strings.news}` /* reading a news */ - presenceData.state = `${getPageTitle()}` - buttons = [{ label: strings.readNewsButton, url: `${origin}/noticias/${part2}` }] + presenceData.state = `${newsTitle}` - currentImage = document.querySelector('#main img')?.src - if (currentImage) { - largeImage = currentImage - presenceData.largeImageKey = largeImage - presenceData.largeImageText = getPageTitle() || largeImage + currentCover = document.querySelector('#main img')?.src + if (currentCover) { + defaultCover = currentCover + currentPageTitle = newsTitle || defaultCover } + buttons = [{ label: strings.readNewsButton, url: `${origin}/noticias/${part2}` }] break - case 'genero': /* Browsing some genres */ + } + case 'genero':{ updateTimestampBySlug('genre-searching') if (hideInfo) { presenceData.state = `${strings.view} ${strings.genre}` @@ -224,6 +223,7 @@ presence.on('UpdateData', async () => { presenceData.state = getPageTitle() } break + } default: /* At any other page, it doesn't matter */ updateTimestampBySlug('page-reading') if (hideInfo) { @@ -232,35 +232,34 @@ presence.on('UpdateData', async () => { } presenceData.state = `${strings.view} ${getPageTitle()}` - buttons = [{ label: strings.viewPage, url: origin + pathname }] - - currentImage = document.querySelector('#main img')?.src - if (currentImage && currentImage !== largeImage) { - largeImage = currentImage - presenceData.largeImageKey = largeImage - presenceData.largeImageText = getPageTitle() || largeImage + currentCover = document.querySelector('#main img')?.src + if (currentCover && currentCover !== defaultCover) { + defaultCover = currentCover + currentPageTitle = getPageTitle() || defaultCover } + buttons = [{ label: strings.viewPage, url: origin + pathname }] break } - // Someday I'll make it better. - if (!lastSlug.includes(`novel-${part2}`) && !lastSlug.includes('list-reading') && !lastSlug.includes('news-reading') && !lastSlug.includes('page-reading') && part1 === '/') { - largeImage = ActivityAssets.Logo - presenceData.largeImageKey = largeImage + + presenceData.startTimestamp = browsingTimestamp + + if (buttons && showButtons && !hideInfo) { + presenceData.smallImageKey = Assets.Reading + presenceData.buttons = buttons + } + if (!showTime) { + delete presenceData.startTimestamp + } + if (!part1 || !part2) { + defaultCover = ActivityAssets.Logo delete presenceData.largeImageText } + else { + presenceData.largeImageText = currentPageTitle + } + presenceData.largeImageKey = defaultCover if (presenceData.state) { - if (!showTime) { - delete presenceData.startTimestamp - } - else { - presenceData.startTimestamp = browsingTimestamp - } - if (showButtons && buttons && !hideInfo) { - presenceData.smallImageKey = Assets.Reading - presenceData.buttons = buttons - } - presence.setActivity(presenceData) } else { From e51b7d780d6b6c5d4c5625eca9c006e64fe511fc Mon Sep 17 00:00:00 2001 From: Shulchl Date: Wed, 29 Jul 2026 18:04:48 -0300 Subject: [PATCH 20/20] Rearranging tags order --- websites/N/Novel Mania/metadata.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json index c6a7d489652d..0b093b105d98 100644 --- a/websites/N/Novel Mania/metadata.json +++ b/websites/N/Novel Mania/metadata.json @@ -22,15 +22,15 @@ "color": "#008cff", "category": "other", "tags": [ - "translation", + "webnovel", "lightnovel", + "translation", + "book", + "reader", "light-novel-translation", - "webnovel", "web-novel-translation", - "reader", "scanlator", "novel-translation", - "book", "books", "book-translation" ],