diff --git a/public/static/src/en/genesis/customCSS.css b/public/static/src/en/genesis/customCSS.css new file mode 100644 index 0000000..42ebf48 --- /dev/null +++ b/public/static/src/en/genesis/customCSS.css @@ -0,0 +1,365 @@ +.red-box .red-text, +.blue-box .blue-text, +.gold-box .gold-text, +.silver-box .silver-text { + display:inline-flex; + align-items:start; + justify-content:start; + text-align:left; + flex-direction:column; + margin-left:0; + margin-bottom:0; + margin-top:0 +} +.red-box .red-text p, +.blue-box .blue-text p, +.gold-box .gold-text p, +.silver-box .silver-text p { + text-align:left +} +.ta-center { + display:flex; + text-align:center!important; + align-items:center!important; + justify-content:center!important; + margin-left:auto; + margin-right:auto +} +.ta-between { + align-items:center!important +} +.blue-box, +.red-box, +.gold-box, +.silver-box { + overflow-wrap:break-word; + word-wrap:break-word; + word-break:break-word +} +.blue-box { + display:flex; + flex-direction:column; + justify-content:center; + min-width:250px; + max-width:500px!important; + box-sizing:content-box; + width:auto; + margin:15px auto; + box-shadow:0 0 10px 1px #00219b; + border-radius:5px; + background-color:#000; + padding:20px; + font-size:25px; + color:#fff; + font-family: Oswald Variable, serif +} +.blue-box div { + text-indent:0; + max-width:500px!important +} +.blue-box p { + text-align:center; + text-indent:0; + margin-bottom:.75rem; + margin-top:.75rem; + max-width:500px!important +} +.blue-title { + display:flex; + flex-direction:column; + text-align:center; + align-items:center; + justify-content:center; + padding-left:0; + margin-left:0; + color:#fff; + font-weight:700; + text-shadow:-1px 5px 13px #004fff,-1px 0 10px #002775; + border-bottom:3px solid #fff; + padding-bottom:15px; + margin-bottom:15px; + max-width:500px!important +} +.blue-text { + display:flex; + flex-direction:row; + align-items:start; + justify-content:center; + text-shadow:-1px 5px 13px #004fff,-1px 0 10px #002775; + font-weight:400; + max-width:500px!important; + font-family: Quattrocento, serif +} +.blue-text p { + text-align:center; + text-indent:0; + margin-bottom:.25rem; + margin-top:.25rem; + max-width:500px!important +} +.red-box { + display:flex; + flex-direction:column; + justify-content:center; + min-width:250px; + max-width:500px!important; + padding:20px; + box-sizing:content-box; + width:auto; + margin:15px auto; + border:solid #750b00e3; + box-shadow:0 0 20px 3px #750b0070; + border-radius:5px; + background-color:#000; + font-size:25px; + color:#fff; + font-family: Oswald Variable, serif +} +.red-box div { + text-indent:0; + max-width:500px!important +} +.red-box p { + text-align:center; + text-indent:0; + margin-bottom:.25rem; + margin-top:.25rem; + max-width:500px!important +} +.red-title { + display:flex; + flex-direction:column; + text-align:center; + align-items:center; + justify-content:center; + padding-left:0; + margin-left:0; + color:#ff4348; + font-weight:700; + text-shadow:-1px 5px 13px #ff230c,-1px 0px 10px #750b00; + border-bottom:3px solid #fff; + padding-bottom:15px; + margin-bottom:15px; + max-width:500px!important +} +.red-text { + display:flex; + flex-direction:row; + align-items:center; + justify-content:center; + text-shadow:-1px 5px 13px #ff230c,-1px 0px 10px #750b00; + font-weight:400; + max-width:500px!important; + margin-left:auto; + margin-right:auto; + font-family: Quattrocento, serif +} +.red-text p { + text-align:center; + text-indent:0; + margin-bottom:.25rem; + margin-top:.25rem; + max-width:500px!important +} +.gold-box { + display:flex; + flex-direction:column; + justify-content:center; + min-width:250px; + padding:20px; + box-sizing:content-box; + width:auto; + margin:15px auto; + border:solid #e0cc00; + box-shadow:0 0 10px 1px #e0cc00; + border-radius:5px; + background-color:#000; + font-size:25px; + color:#fff; + font-family: Oswald Variable, serif; + max-width:500px!important +} +.gold-box div { + text-indent:0; + max-width:500px!important +} +.gold-box p { + text-align:center; + text-indent:0; + margin-bottom:.75rem; + margin-top:.75rem; + max-width:500px!important +} +.gold-title { + display:flex; + flex-direction:column; + text-align:center; + align-items:center; + justify-content:center; + padding-left:0; + margin-left:0; + color:#fff; + font-weight:700; + text-shadow:-1px 5px 13px #e0cc00,-1px 0px 10px #544d00; + border-bottom:3px solid #fff; + padding-bottom:15px; + margin-bottom:15px; + max-width:500px!important +} +.gold-text { + display:flex; + flex-direction:row; + align-items:center; + justify-content:center; + text-align:center; + text-shadow:-1px 5px 13px #e0cc00,-1px 0 10px #e0cc00; + row-gap:1.5rem; + font-weight:400; + max-width:500px!important; + font-family: Quattrocento, serif +} +.gold-text p { + text-align:center; + text-indent:0; + margin-bottom:.25rem; + margin-top:.25rem; + max-width:500px!important +} +.gold-letter { + display:flex; + flex-direction:column; + align-items:center; + justify-content:center; + text-shadow:-1px 5px 13px #e0cc00,-1px 0 10px #e0cc00; + row-gap:1.5rem; + font-weight:700; + font-family: Amita, serif; + max-width:500px!important; + margin-left:auto; + margin-right:auto +} +.gold-letter p { + text-align:center; + text-indent:0; + margin-bottom:.25rem; + margin-top:.25rem; + max-width:500px!important +} +.silver-box { + display:flex; + flex-direction:column; + justify-content:center; + min-width:250px; + padding:20px; + box-sizing:content-box; + width:auto; + margin:15px auto; + border:solid #c2c2c2; + box-shadow:0 0 10px 1px #737373; + border-radius:5px; + background-color:#000; + font-size:25px; + color:#fff; + font-family: Oswald Variable, serif; + max-width:500px!important +} +.silver-box div { + text-indent:0; + max-width:500px!important +} +.silver-box p { + text-align:center; + text-indent:0; + margin-bottom:.75rem; + margin-top:.75rem; + max-width:500px!important +} +.silver-title { + display:flex; + flex-direction:column; + text-align:center; + align-items:center; + justify-content:center; + padding-left:0; + margin-left:0; + color:#fff; + font-weight:700; + text-shadow:-1px 5px 13px #b4b4b4,-1px 0 10px #737373; + border-bottom:3px solid #fff; + padding-bottom:15px; + margin-bottom:15px; + max-width:500px!important +} +.silver-text { + display:flex; + flex-direction:row; + align-items:center; + justify-content:center; + text-shadow:-1px 5px 13px #b4b4b4,-1px 0 10px #737373; + row-gap:1.5rem; + font-weight:400; + max-width:500px!important; + margin: 2rem auto; + font-family: Quattrocento, serif +} +.silver-text p { + text-align:center; + text-indent:0; + margin-bottom:.25rem; + margin-top:.25rem; + max-width:500px!important +} +.silver-letter { + display:flex; + flex-direction:column; + text-align:center; + align-items:center; + justify-content:center; + text-shadow:-1px 5px 13px #b4b4b4,-1px 0 10px #737373; + row-gap:1.5rem; + font-weight:700; + max-width:500px!important; + margin: 2rem auto; + font-family: MedievalSharp, serif +} +.silver-letter p { + text-align:center; + text-indent:0; + margin-bottom:.25rem; + margin-top:.25rem; + max-width:500px!important +} +.blue-text, +.gold-text, +.silver-text, +.red-text, +.blue-box, +.red-box, +.gold-box, +.silver-box, +.red-title, +.gold-title, +.silver-title, +.blue-title, +.blue-text p, +.gold-text p, +.silver-text p, +.red-text p { + text-indent:0!important +} +div.red-text, +div.gold-text, +div.silver-text, +div.blue-text { + display:flex; + flex-direction:column; + margin-bottom:2.25rem; + margin-top:2.25rem +} +.blue-box .blue-text p, +.red-box .red-text p, +.gold-box .gold-text p, +.silver-box .silver-text p { + display:flex; + flex-direction:column +} diff --git a/public/static/src/en/genesis/icon.png b/public/static/src/en/genesis/icon.png new file mode 100644 index 0000000..e3bb09d Binary files /dev/null and b/public/static/src/en/genesis/icon.png differ diff --git a/scripts/multisrc/lightnovelwp/sources.json b/scripts/multisrc/lightnovelwp/sources.json index 859b6c7..de42924 100644 --- a/scripts/multisrc/lightnovelwp/sources.json +++ b/scripts/multisrc/lightnovelwp/sources.json @@ -93,7 +93,7 @@ { "id": "genesistls", "sourceSite": "https://genesistls.com/", - "sourceName": "GenesisTls", + "sourceName": "GenesisTls (Deprecated)", "options": { "lang": "English", "reverseChapters": false, diff --git a/src/plugins/english/genesis.ts b/src/plugins/english/genesis.ts new file mode 100644 index 0000000..010fe83 --- /dev/null +++ b/src/plugins/english/genesis.ts @@ -0,0 +1,168 @@ +/* eslint-disable no-case-declarations */ +import { fetchApi } from '@libs/fetch'; +import { Filters, FilterTypes } from '@libs/filterInputs'; +import { Plugin } from '@typings/plugin'; + +class Genesis implements Plugin.PluginBase { + id = 'genesistudio'; + name = 'Genesis'; + icon = 'src/en/genesis/icon.png'; + customCSS = 'src/en/genesis/customCSS.css'; + site = 'https://genesistudio.com'; + version = '1.0.0'; + imageRequestInit?: Plugin.ImageRequestInit | undefined = { + headers: { + 'referrer': this.site, + }, + }; + + async parseNovels(json: any[]): Promise { + const assets = 'https://edit.genesistudio.com/assets/'; + const format = '?format=auto&quality=70&width=400&height=600'; + + return json.map((novel: any) => { + return { + name: novel.novel_title, + path: `/novels/${novel.abbreviation}`, + cover: `${assets}${novel.cover}${format}`, + }; + }); + } + + async popularNovels( + pageNo: number, + { + showLatestNovels, + filters, + }: Plugin.PopularNovelsOptions, + ): Promise { + if (pageNo !== 1) return []; + + let link = `https://genesistudio.com/api/search?`; + if (showLatestNovels) { + link += 'sort=Recent'; + } else { + if (filters!.genres.value.length) { + link += filters!.genres.value.join('&'); + } + link += `&${filters!.storyStatus.value}&${filters!.sort.value}`; + } + + const json = await fetchApi(link).then(r => r.json()); + + return this.parseNovels(json); + } + + async parseNovel(novelPath: string): Promise { + const url = `${this.site}${novelPath}/__data.json?x-sveltekit-invalidated=001`; + const json = await fetchApi(url).then(r => r.json()); + + const nodes = json.nodes; + const data = nodes + .filter((node: { type: string }) => node.type === 'data') + .map((node: { data: any }) => node.data)[0]; + const novelData = data[data[0].novel]; + + const assets = 'https://edit.genesistudio.com/assets/'; + const format = '?format=auto&quality=70&width=400&height=600'; + + const novel: Plugin.SourceNovel = { + path: novelPath, + name: data[novelData.novel_title], + cover: `${assets}${data[novelData.cover]}${format}`, + summary: data[novelData.synopsis], + author: data[novelData.author], + status: data[novelData.serialization], + }; + + novel.genres = data[novelData.genres] + .map((index: number) => data[index]) + .join(','); + + const chapterData = data[data[0].chapters]; + const freeChapterData = chapterData.free; + // const premiumChapterData = chapterData.premium; + + novel.chapters = data[freeChapterData] + .map((index: number) => data[index]) + .map((chapter: any) => { + return { + name: `Chapter ${data[chapter.chapter_number]}: ${data[chapter.chapter_title]}`, + path: `/viewer/${data[chapter.id]}`, + releaseTime: data[chapter.date_created], + chapterNumber: data[chapter.chapter_number], + }; + }) + .reverse(); + + return novel; + } + + async parseChapter(chapterPath: string): Promise { + const url = `${this.site}${chapterPath}/__data.json?x-sveltekit-invalidated=001`; + const json = await fetchApi(url).then(r => r.json()); + + const nodes = json.nodes; + const data = nodes + .filter((node: { type: string }) => node.type === 'data') + .map((node: { data: any }) => node.data)[0]; + + return data[data[0].content]; + } + + async searchNovels( + searchTerm: string, + pageNo: number, + ): Promise { + if (pageNo !== 1) return []; + + const url = `${this.site}/api/search?serialization=All&sort=Popular&title=${searchTerm}`; + const json = await fetchApi(url).then(r => r.json()); + + return this.parseNovels(json); + } + + filters = { + sort: { + label: 'Sort Results By', + value: 'sort=Popular', + options: [ + { label: 'Popular', value: 'sort=Popular' }, + { label: 'Recent', value: 'sort=Recent' }, + { label: 'Views', value: 'sort=Views' }, + ], + type: FilterTypes.Picker, + }, + storyStatus: { + label: 'Status', + value: 'serialization=All', + options: [ + { label: 'All', value: 'serialization=All' }, + { label: 'Ongoing', value: 'serialization=Ongoing' }, + { label: 'Completed', value: 'serialization=Completed' }, + ], + type: FilterTypes.Picker, + }, + genres: { + label: 'Genres', + value: [], + options: [ + { label: 'Action', value: 'genres=Action' }, + { label: 'Comedy', value: 'genres=Comedy' }, + { label: 'Drama', value: 'genres=Drama' }, + { label: 'Fantasy', value: 'genres=Fantasy' }, + { label: 'Harem', value: 'genres=Harem' }, + { label: 'Martial Arts', value: 'genres=Martial+Arts' }, + { label: 'Modern', value: 'genres=Modern' }, + { label: 'Mystery', value: 'genres=Mystery' }, + { label: 'Psychological', value: 'genres=Psychological' }, + { label: 'Romance', value: 'genres=Romance' }, + { label: 'Slice of life', value: 'genres=Slice+of+Life' }, + { label: 'Tragedy', value: 'genres=Tragedy' }, + ], + type: FilterTypes.CheckboxGroup, + }, + } satisfies Filters; +} + +export default new Genesis(); diff --git a/src/types/plugin.ts b/src/types/plugin.ts index a97f0da..8f0a547 100644 --- a/src/types/plugin.ts +++ b/src/types/plugin.ts @@ -24,7 +24,7 @@ export namespace Plugin { cover?: string; } export interface SourceNovel extends NovelItem { - /** Comma separated genre list */ + /** Comma separated genre list -> "action,fantasy,romance" */ genres?: string; summary?: string; author?: string;