diff --git a/websites/N/Novel Mania/metadata.json b/websites/N/Novel Mania/metadata.json index 7ad8f79cd1de..65fd4ac93d75 100644 --- a/websites/N/Novel Mania/metadata.json +++ b/websites/N/Novel Mania/metadata.json @@ -1,11 +1,14 @@ { - "$schema": "https://schemas.premid.app/metadata/1.16", + "$schema": "https://schemas.premid.app/metadata/1.17", "apiVersion": 1, "author": { - "name": "Shuichi", + "name": "obudista", "id": "179440483796385792" }, "service": "Novel Mania", + "altnames": [ + "NovelMania" + ], "description": { "en": "Read webnovels in Portuguese anywhere and anytime.", "nl": "Lees overal en altijd webnovels in het Portugees.", @@ -13,14 +16,50 @@ }, "url": "novelmania.com.br", "regExp": "^https?[:][/][/]([a-z0-9-]+[.])*novelmania[.]com[.]br[/]", - "version": "1.1.0", + "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", - "mania", - "translator" + "novel-mania", + "translation", + "lightnovel", + "light-novel-translation", + "webnovel", + "web-novel-translation", + "reader", + "scanlator", + "novel-translation", + "book", + "books", + "book-translation" + ], + "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 + } ] } diff --git a/websites/N/Novel Mania/novelmania.json b/websites/N/Novel Mania/novelmania.json new file mode 100644 index 000000000000..bdce914c113f --- /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": "lists" + }, + "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": "Text from 'viewing novel's button'.", + "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!" + } +} diff --git a/websites/N/Novel Mania/presence.ts b/websites/N/Novel Mania/presence.ts index f3b79755c8d9..cdd226369f74 100644 --- a/websites/N/Novel Mania/presence.ts +++ b/websites/N/Novel Mania/presence.ts @@ -1,78 +1,221 @@ +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: 'https://cdn.rcd.gg/PreMiD/websites/N/Novel%20Mania/assets/logo.png', + largeImageKey: ActivityAssets.Logo, startTimestamp: browsingTimestamp, + type: ActivityType.Watching, + } - 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! */ + 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 } - else if (path.includes('/noticias/')) { - presenceData.details = 'Lendo Notícia:' /* reading a notice */ - presenceData.state = novelName?.textContent + + 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))}` + } + + /* 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) { + 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 } - 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 */ + + // Set the activity + if (presenceData.state) { + if (!showTime) { + delete presenceData.startTimestamp } - else { - presenceData.details = `Lendo${NovelTitle?.textContent}` /* reading *novel**/ - presenceData.state = `Em ${porcent?.textContent}do${currentChapTitle?.textContent}` /* in X% of... novel chapter */ + if (showButtons && buttons && (cleanPath === '/' || !hideInfo)) { + presenceData.buttons = buttons } - } - 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 + presence.setActivity(presenceData) } else { - presenceData.details = 'Buscando...' /* searching... */ - presenceData.state = 'Algo Incrível' /* Something incredible */ + presence.clearActivity() } - presence.setActivity(presenceData) })