diff --git a/web/src/engine/websites/KanMan.ts b/web/src/engine/websites/KanMan.ts new file mode 100644 index 0000000000..95cda509b0 --- /dev/null +++ b/web/src/engine/websites/KanMan.ts @@ -0,0 +1,93 @@ +import { Tags } from '../Tags'; +import icon from './KanMan.webp'; +import * as Common from './decorators/Common'; +import { FetchCSS, FetchJSON } from '../platform/FetchProvider'; +import { Chapter, DecoratableMangaScraper, Manga, Page, type MangaPlugin } from '../providers/MangaPlugin'; +import { Choice } from '../SettingsManager'; +import { EngineResourceKey as E, WebsiteResourceKey as W } from '../../i18n/ILocale'; + +type APIResult = { + data: T, + status: number, + message: string; +} + +type APIManga = { + comic_id: number, + comic_name: string + comic_chapter: APIChapter[] + current_chapter: APIChapter +} + +type APIChapter = { + chapter_name: string, + chapter_newid: string + chapter_img_list: string[] +} + +@Common.ImageAjax(true) +export default class extends DecoratableMangaScraper { + private readonly apiUrl = 'https://www.kanman.com/api/'; + private readonly product = { + id: '1', + name: 'kmh', + platform: 'pc' + }; + public constructor() { + super('kanman', `看漫画 (KanMan)`, 'https://www.kanman.com', Tags.Media.Manhua, Tags.Language.Chinese, Tags.Source.Official); + + this.Settings.format = new Choice('image.format', + W.Plugin_Settings_ImageFormat, + W.Plugin_Settings_ImageFormatInfo, + '1', + { key: '0', label: E.Settings_Global_Format_JPEG }, + { key: '1', label: E.Settings_Global_Format_WEBP }, + ); + + this.Settings.quality = new Choice('image.quality', + W.Plugin_Settings_ImageQuality, + W.Plugin_Settings_ImageQualityInfo, + 'high', + { key: 'high', label: W.Plugin_Settings_ImageQuality_High }, + { key: 'middle', label: W.Plugin_Settings_ImageQuality_Medium }, + { key: 'low', label: W.Plugin_Settings_ImageQuality_Low }, + ); + } + public override get Icon() { + return icon; + } + + public override async FetchManga(provider: MangaPlugin, url: string): Promise { + const [data] = await FetchCSS(new Request(url), 'h1.title'); + return new Manga(this, provider, url.match(/(\d+)\/$/)[1], data.textContent.trim()); + } + public override ValidateMangaURL(url: string): boolean { + return new RegExp(`^${this.URI.origin}/[^/]+/$`).test(url); + } + + public override async FetchMangas(provider: MangaPlugin): Promise { + const { data } = await FetchJSON>(new Request(new URL('getComicList', this.apiUrl))); + return data.map(manga => new Manga(this, provider, manga.comic_id.toString(), manga.comic_name.trim())); + } + + public override async FetchChapters(manga: Manga): Promise { + const uri = new URL(`getComicInfoBody?comic_id=${manga.Identifier}`, this.apiUrl); + const { data: { comic_chapter } } = await FetchJSON>(new Request(uri)); + return comic_chapter.map(chapter => new Chapter(this, manga, chapter.chapter_newid, chapter.chapter_name.trim())); + } + + public override async FetchPages(chapter: Chapter): Promise { + const uri = new URL('getchapterinfov2', this.apiUrl); + uri.search = new URLSearchParams({ + product_id: this.product.id, + productname: this.product.name, + platformname: this.product.platform, + comic_id: chapter.Parent.Identifier, + chapter_newid: chapter.Identifier, + isWebp: this.Settings.format.Value as string, + quality: this.Settings.quality.Value as string + }).toString(); + const { data: { current_chapter: { chapter_img_list } } } = await FetchJSON>(new Request(uri)); + return chapter_img_list.map(page => new Page(this, chapter, new URL(page))); + } +} \ No newline at end of file diff --git a/web/src/engine/websites/legacy/KanMan.webp b/web/src/engine/websites/KanMan.webp similarity index 100% rename from web/src/engine/websites/legacy/KanMan.webp rename to web/src/engine/websites/KanMan.webp diff --git a/web/src/engine/websites/KanMan_e2e.ts b/web/src/engine/websites/KanMan_e2e.ts new file mode 100644 index 0000000000..a33dfb4735 --- /dev/null +++ b/web/src/engine/websites/KanMan_e2e.ts @@ -0,0 +1,24 @@ +import { TestFixture } from '../../../test/WebsitesFixture'; + +const config = { + plugin: { + id: 'kanman', + title: '看漫画 (KanMan)' + }, + container: { + url: 'https://www.kanman.com/17745/', + id: '17745', + title: '凤逆天下' + }, + child: { + id: '1004', + title: '第1话 神秘的黑玉1' + }, + entry: { + index: 3, + size: 246_994, + type: 'image/webp' + } +}; + +new TestFixture(config).AssertWebsite(); \ No newline at end of file diff --git a/web/src/engine/websites/_index.ts b/web/src/engine/websites/_index.ts index d49d61af5d..fa72508fe1 100755 --- a/web/src/engine/websites/_index.ts +++ b/web/src/engine/websites/_index.ts @@ -224,6 +224,7 @@ export { default as KadoComi } from './KadoComi'; export { default as Kakaopage } from './Kakaopage'; export { default as KaliScan } from './KaliScan'; export { default as Kanjiku } from './Kanjiku'; +export { default as KanMan } from './KanMan'; export { default as Kanzenin } from './Kanzenin'; export { default as Katakomik } from './Katakomik'; export { default as KingOfManga } from './KingOfManga'; @@ -749,7 +750,6 @@ export { default as GammaPlus } from './legacy/GammaPlus'; export { default as Guoman8 } from './legacy/Guoman8'; export { default as HeavenManga } from './legacy/HeavenManga'; export { default as HolyManga } from './legacy/HolyManga'; -export { default as KanMan } from './legacy/KanMan'; export { default as KissAway } from './legacy/KissAway'; export { default as kuman5 } from './legacy/kuman5'; export { default as LectorManga } from './legacy/LectorManga'; @@ -775,7 +775,6 @@ export { default as MangaToonTH } from './legacy/MangaToonTH'; export { default as MangaToonVI } from './legacy/MangaToonVI'; export { default as MangaTR } from './legacy/MangaTR'; export { default as MangaZukiRAWS } from './legacy/MangaZukiRAWS'; -export { default as ManhuaTai } from './legacy/ManhuaTai'; export { default as MeioNovel } from './legacy/MeioNovel'; export { default as MoreNovel } from './legacy/MoreNovel'; export { default as MuchoHentai } from './legacy/MuchoHentai'; @@ -806,10 +805,8 @@ export { default as PlotTwistNoFansub } from './legacy/PlotTwistNoFansub'; export { default as ShinobiScans } from './legacy/ShinobiScans'; export { default as SixMH7 } from './legacy/SixMH7'; export { default as SleepyTranslations } from './legacy/SleepyTranslations'; -export { default as SouDongMan } from './legacy/SouDongMan'; export { default as StoriaDash } from './legacy/StoriaDash'; export { default as TAADD } from './legacy/TAADD'; -export { default as TaoManhua } from './legacy/TaoManhua'; export { default as TencentComic } from './legacy/TencentComic'; export { default as TenManga } from './legacy/TenManga'; export { default as ToomicsDE } from './legacy/ToomicsDE'; diff --git a/web/src/engine/websites/legacy/KanMan.ts b/web/src/engine/websites/legacy/KanMan.ts deleted file mode 100755 index f17f652c5e..0000000000 --- a/web/src/engine/websites/legacy/KanMan.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Auto-Generated export from HakuNeko Legacy -// See: https://gist.github.com/ronny1982/0c8d5d4f0bd9c1f1b21dbf9a2ffbfec9 - -//import { Tags } from '../../Tags'; -import icon from './KanMan.webp'; -import { DecoratableMangaScraper } from '../../providers/MangaPlugin'; - -export default class extends DecoratableMangaScraper { - - public constructor() { - super('kanman', `看漫画 (KanMan)`, 'https://www.kanman.com' /*, Tags.Language.English, Tags ... */); - } - - public override get Icon() { - return icon; - } -} - -// Original Source -/* -class KanMan extends MHXK { - - constructor() { - super(); - super.id = 'kanman'; - super.label = '看漫画 (KanMan)'; - this.tags = [ 'manga', 'webtoon', 'chinese' ]; - this.url = 'https://www.kanman.com'; - - this.product = { - id: 1, - name: 'kmh', - platform: 'pc' - }; - } -} -*/ \ No newline at end of file diff --git a/web/src/engine/websites/legacy/ManhuaTai.ts b/web/src/engine/websites/legacy/ManhuaTai.ts deleted file mode 100755 index a0535f2ba5..0000000000 --- a/web/src/engine/websites/legacy/ManhuaTai.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Auto-Generated export from HakuNeko Legacy -// See: https://gist.github.com/ronny1982/0c8d5d4f0bd9c1f1b21dbf9a2ffbfec9 - -//import { Tags } from '../../Tags'; -import icon from './ManhuaTai.webp'; -import { DecoratableMangaScraper } from '../../providers/MangaPlugin'; - -export default class extends DecoratableMangaScraper { - - public constructor() { - // NOTE: Redirects from www.manhuatai.com to www.kanman.com, seems only to be available as app => https://www.manhuatai.com/app/mht-pc.html, https://app.321mh.com/app/scheme?pkgname=com.comic.manhuatai&ckey=CK138297596322, https://sj.qq.com/appdetail/com.comic.manhuatai - super('manhuatai', `ManhuaTai`, 'https://www.kanman.com' /*, Tags.Language.English, Tags ... */); - } - - public override get Icon() { - return icon; - } -} - -// Original Source -/* -class ManhuaTai extends MHXK { - - constructor() { - super(); - super.id = 'manhuatai'; - super.label = 'ManhuaTai'; - this.tags = [ 'webtoon', 'chinese' ]; - this.url = 'https://www.manhuatai.com'; - - // extracted from: https://resource.mhxk.com/manhuatai_pc/static/js/chunk/vendor.a06c71.js - this.product = { - id: 2, - name: 'mht', - platform: 'pc' - }; - } -} -*/ \ No newline at end of file diff --git a/web/src/engine/websites/legacy/ManhuaTai.webp b/web/src/engine/websites/legacy/ManhuaTai.webp deleted file mode 100644 index 98184600c2..0000000000 Binary files a/web/src/engine/websites/legacy/ManhuaTai.webp and /dev/null differ diff --git a/web/src/engine/websites/legacy/SouDongMan.ts b/web/src/engine/websites/legacy/SouDongMan.ts deleted file mode 100755 index 14a3abcd5e..0000000000 --- a/web/src/engine/websites/legacy/SouDongMan.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Auto-Generated export from HakuNeko Legacy -// See: https://gist.github.com/ronny1982/0c8d5d4f0bd9c1f1b21dbf9a2ffbfec9 - -//import { Tags } from '../../Tags'; -import icon from './SouDongMan.webp'; -import { DecoratableMangaScraper } from '../../providers/MangaPlugin'; - -export default class extends DecoratableMangaScraper { - - public constructor() { - super('soudongman', `斗罗大陆 (SouDongMan)`, 'https://www.soudongman.com' /*, Tags.Language.English, Tags ... */); - } - - public override get Icon() { - return icon; - } -} - -// Original Source -/* -class SouDongMan extends MHXK { - - constructor() { - super(); - super.id = 'soudongman'; - super.label = '斗罗大陆 (SouDongMan)'; - this.tags = [ 'webtoon', 'chinese' ]; - this.url = 'https://www.soudongman.com'; - - this.queryMangaTitle = 'div.title-warper h1.title'; - // extracted from: https://resource.mhxk.com/soudongman_pc/static/js/chunk/vendor.23b7d6.js - this.product = { - id: 9, - name: 'soudm', - platform: 'pc' - }; - } -} -*/ \ No newline at end of file diff --git a/web/src/engine/websites/legacy/SouDongMan.webp b/web/src/engine/websites/legacy/SouDongMan.webp deleted file mode 100644 index 976e8aa456..0000000000 Binary files a/web/src/engine/websites/legacy/SouDongMan.webp and /dev/null differ diff --git a/web/src/engine/websites/legacy/TaoManhua.ts b/web/src/engine/websites/legacy/TaoManhua.ts deleted file mode 100755 index 6a35f003f5..0000000000 --- a/web/src/engine/websites/legacy/TaoManhua.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Auto-Generated export from HakuNeko Legacy -// See: https://gist.github.com/ronny1982/0c8d5d4f0bd9c1f1b21dbf9a2ffbfec9 - -//import { Tags } from '../../Tags'; -import icon from './TaoManhua.webp'; -import { DecoratableMangaScraper } from '../../providers/MangaPlugin'; - -export default class extends DecoratableMangaScraper { - - public constructor() { - // NOTE: Redirects from www.taomanhua.com to www.kanman.com, seems only some 404 pages are still shown on the domain => https://www.taomanhua.com/app/mht-pc.html - super('taomanhua', `神漫画 (Tao Manhua)`, 'https://www.kanman.com' /*, Tags.Language.English, Tags ... */); - } - - public override get Icon() { - return icon; - } -} - -// Original Source -/* -class TaoManhua extends MHXK { - - constructor() { - super(); - super.id = 'taomanhua'; - super.label = '神漫画 (Tao Manhua)'; - this.tags = [ 'webtoon', 'chinese' ]; - this.url = 'https://www.taomanhua.com'; - - // extracted from: https://resource.mhxk.com/shenmanhua_pc/static/js/chunk/vendor.d9c425.js - this.product = { - id: 3, - name: 'smh', - platform: 'pc' - }; - } -} -*/ \ No newline at end of file diff --git a/web/src/engine/websites/legacy/TaoManhua.webp b/web/src/engine/websites/legacy/TaoManhua.webp deleted file mode 100644 index ce85d65085..0000000000 Binary files a/web/src/engine/websites/legacy/TaoManhua.webp and /dev/null differ diff --git a/web/src/i18n/ILocale.ts b/web/src/i18n/ILocale.ts index 67b565113b..528efdafdb 100644 --- a/web/src/i18n/ILocale.ts +++ b/web/src/i18n/ILocale.ts @@ -120,7 +120,7 @@ export enum FrontendResourceKey { Frontend_Setting = 'Frontend_Setting', Frontend_Settings = 'Frontend_Settings', Frontend_Help = 'Frontend_Help', - Frontend_About= 'Frontend_About', + Frontend_About = 'Frontend_About', Frontend_Plugin = 'Frontend_Plugin', Frontend_Plugins = 'Frontend_Plugins', Frontend_Plugin_List = 'Frontend_Plugin_List', @@ -324,6 +324,11 @@ export enum WebsiteResourceKey { Plugin_Settings_UrlOverrideInfo = 'Plugin_Settings_UrlOverrideInfo', Plugin_Settings_ImageFormat = 'Plugin_Settings_ImageFormat', Plugin_Settings_ImageFormatInfo = 'Plugin_Settings_ImageFormatInfo', + Plugin_Settings_ImageQuality = 'Plugin_Settings_ImageQuality', + Plugin_Settings_ImageQualityInfo = 'Plugin_Settings_ImageQualityInfo', + Plugin_Settings_ImageQuality_High = 'Plugin_Settings_ImageQuality_High', + Plugin_Settings_ImageQuality_Medium = 'Plugin_Settings_ImageQuality_Medium', + Plugin_Settings_ImageQuality_Low = 'Plugin_Settings_ImageQuality_Low', Plugin_Common_MangaIndex_NotSupported = 'Plugin_Common_MangaIndex_NotSupported', Plugin_Common_Chapter_InvalidError = 'Plugin_Common_Chapter_InvalidError', Plugin_Common_Chapter_UnavailableError = 'Plugin_Common_Chapter_UnavailableError', diff --git a/web/src/i18n/locales/en_US.ts b/web/src/i18n/locales/en_US.ts index cb8e629599..adb96768ae 100644 --- a/web/src/i18n/locales/en_US.ts +++ b/web/src/i18n/locales/en_US.ts @@ -298,6 +298,12 @@ const translations: VariantResource = { Plugin_Settings_ImageFormat: 'Preferred Image Format', Plugin_Settings_ImageFormatInfo: 'Download pictures using this file format if possible', + Plugin_Settings_ImageQuality: 'Image quality', + Plugin_Settings_ImageQualityInfo: 'Download pictures using this quality if possible', + Plugin_Settings_ImageQuality_High: 'High', + Plugin_Settings_ImageQuality_Medium: 'Medium', + Plugin_Settings_ImageQuality_Low: 'Low', + Plugin_Common_MangaIndex_NotSupported: 'Unable to create the content index for this website. Use the Copy & Paste feature to directly access the content of a specific URL!', Plugin_Common_Chapter_UnavailableError: 'The chapter is not available (not purchased/unlocked/public)!', Plugin_Common_Chapter_InvalidError: 'Failed to extract the pages from the chapter content!',