From a504a48e6eaeae9f50a5aa9ed3c2ee85bdd46ba6 Mon Sep 17 00:00:00 2001 From: masi Date: Fri, 25 Sep 2026 12:26:34 -0300 Subject: [PATCH] feat(english): add WeTriedTLS plugin HeanCMS JSON API source. Paid chapters are labelled and show an unlock notice instead of content. Co-Authored-By: Claude Opus 5.5 --- plugins/english/wetriedtls.ts | 131 +++++++++++++++++++++++ public/static/src/en/wetriedtls/icon.png | Bin 0 -> 7006 bytes 2 files changed, 131 insertions(+) create mode 100644 plugins/english/wetriedtls.ts create mode 100644 public/static/src/en/wetriedtls/icon.png diff --git a/plugins/english/wetriedtls.ts b/plugins/english/wetriedtls.ts new file mode 100644 index 0000000..8b3f492 --- /dev/null +++ b/plugins/english/wetriedtls.ts @@ -0,0 +1,131 @@ +import { fetchApi } from '@libs/fetch'; +import { Plugin } from '@/types/plugin'; +import { defaultCover } from '@libs/defaultCover'; +import { NovelStatus } from '@libs/novelStatus'; + +// WeTriedTLS runs HeanCMS; everything comes from its JSON API. +const API = 'https://api.wetriedtls.com'; + +type Series = { + id: number; + title: string; + series_slug: string; + thumbnail?: string; + description?: string; + author?: string; + studio?: string; + status?: string; + alternative_names?: string; + tags?: { name: string }[]; +}; + +type Chapter = { + chapter_name: string; + chapter_title?: string | null; + chapter_slug: string; + index?: string; + price: number; + created_at?: string; +}; + +type Page = { data: T[]; meta: { last_page: number } }; + +const STATUS: Record = { + Ongoing: NovelStatus.Ongoing, + Completed: NovelStatus.Completed, + Hiatus: NovelStatus.OnHiatus, + Dropped: NovelStatus.Cancelled, +}; + +class WeTriedTLS implements Plugin.PluginBase { + id = 'wetriedtls'; + name = 'WeTriedTLS'; + icon = 'src/en/wetriedtls/icon.png'; + site = 'https://wetriedtls.com'; + version = '1.0.0'; + + async api(path: string): Promise { + const res = await fetchApi(API + path, { headers: { Referer: this.site } }); + if (!res.ok) throw new Error(`WeTriedTLS API ${res.status} for ${path}`); + return res.json(); + } + + toItem = (s: Series): Plugin.NovelItem => ({ + name: s.title, + path: s.series_slug, + cover: s.thumbnail || defaultCover, + }); + + async query(params: string, pageNo: number): Promise { + const res = await this.api>( + `/query?series_type=Novel&perPage=20&page=${pageNo}&${params}`, + ); + return res.data.map(this.toItem); + } + + popularNovels( + pageNo: number, + { showLatestNovels }: Plugin.PopularNovelsOptions, + ): Promise { + const order = showLatestNovels ? 'latest' : 'total_views'; + return this.query(`orderBy=${order}&order=desc&query_string=`, pageNo); + } + + searchNovels(searchTerm: string, pageNo: number) { + return this.query(`query_string=${encodeURIComponent(searchTerm)}`, pageNo); + } + + async parseNovel(novelPath: string): Promise { + const s = await this.api(`/series/${novelPath}`); + + const chapters: Plugin.ChapterItem[] = []; + for (let page = 1, last = 1; page <= last; page++) { + const res = await this.api>( + `/chapters/${s.id}?page=${page}&perPage=1000`, + ); + last = res.meta.last_page; + for (const c of res.data) { + const title = c.chapter_title + ? `${c.chapter_name} - ${c.chapter_title}` + : c.chapter_name; + chapters.push({ + // Paid chapters stay paid: they are labelled and parseChapter shows a notice. + name: c.price > 0 ? `🔒 ${title}` : title, + path: `${novelPath}/${c.chapter_slug}`, + releaseTime: c.created_at, + chapterNumber: c.index ? Number(c.index) : undefined, + }); + } + } + chapters.reverse(); // API lists newest first + + return { + path: novelPath, + name: s.title, + cover: s.thumbnail || defaultCover, + author: s.author, + artist: s.studio || undefined, + summary: s.description?.replace(/<[^>]+>/g, '').trim(), + genres: s.tags?.map(t => t.name).join(', '), + status: (s.status && STATUS[s.status]) || NovelStatus.Unknown, + chapters, + }; + } + + async parseChapter(chapterPath: string): Promise { + const res = await this.api<{ + chapter: { chapter_content?: string; price: number }; + }>(`/chapter/${chapterPath}`); + const content = res.chapter.chapter_content; + if (!content) { + return res.chapter.price > 0 + ? `

This chapter is paid on WeTriedTLS. Unlock it at ${this.site} to read it.

` + : '

Chapter content is unavailable.

'; + } + return content; + } + + resolveUrl = (path: string) => `${this.site}/series/${path}`; +} + +export default new WeTriedTLS(); diff --git a/public/static/src/en/wetriedtls/icon.png b/public/static/src/en/wetriedtls/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..027df50767ea98df79664058ab8bdec027ebe9d2 GIT binary patch literal 7006 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 O1g@&9@iYlW3 literal 0 HcmV?d00001