diff --git a/scripts/multisrc/madara/generator.js b/scripts/multisrc/madara/generator.js index a57061904..cb3fa09c7 100644 --- a/scripts/multisrc/madara/generator.js +++ b/scripts/multisrc/madara/generator.js @@ -27,10 +27,7 @@ const generator = function generator(source) { }); const pluginScript = ` -${madaraTemplate.replace( - '// CustomJS HERE', - source.options?.customJs || '', -)} +${madaraTemplate.replace('// CustomJS HERE', source.options?.customJs || '')} const plugin = new MadaraPlugin(${JSON.stringify(source)}); export default plugin; `.trim(); diff --git a/src/plugins/english/novelupdates.ts b/src/plugins/english/novelupdates.ts index c8b4ca421..c49650a31 100644 --- a/src/plugins/english/novelupdates.ts +++ b/src/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@typings/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.7.16'; + version = '0.8.0'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -990,6 +990,38 @@ class NovelUpdates implements Plugin.PluginBase { return chapterText; } + async syncChapterProgress( + novel: Plugin.SourceNovel, + chapter: Plugin.ChapterItem, + ): Promise { + try { + // Get HTML content + const result = await fetchApi(this.site + novel.path); + const loadedCheerio = parseHTML(await result.text()); + + // Extract IDs + const shortlink = loadedCheerio('link[rel="shortlink"]').attr('href'); + const novelId = shortlink?.match(/\?p=(\d+)/)?.[1]; + const chapterId = chapter.path.match(/\/(\d+)\//)?.[1]; + + if (!novelId || !chapterId) { + throw new Error( + `Invalid novel path (${novel.path}) or chapter path (${chapter.path})`, + ); + } + + // Update reading progress + await fetchApi( + `${this.site}readinglist_update.php?rid=${chapterId}&sid=${novelId}&checked=yes`, + ); + return true; + } catch (error) { + throw new Error( + `Failed to sync chapter progress: ${error instanceof Error ? error.message : 'Unknown error'}`, + ); + } + } + async searchNovels( searchTerm: string, page: number, @@ -1004,7 +1036,7 @@ class NovelUpdates implements Plugin.PluginBase { ); searchTerm = longestSearchTerm.replace(/[‘’]/g, "'").replace(/\s+/g, '+'); - const url = `${this.site}series-finder/?sf=1&sh=${encodeURIComponent(searchTerm)}&sort=srank&order=asc&pg=${page}`; + const url = `${this.site}series-finder/?sf=1&sh=${searchTerm}&sort=srank&order=asc&pg=${page}`; const result = await fetchApi(url); const body = await result.text(); @@ -1147,6 +1179,14 @@ class NovelUpdates implements Plugin.PluginBase { type: FilterTypes.CheckboxGroup, }, } satisfies Filters; + + pluginSettings = { + syncChapterProgress: { + value: '', + label: 'Sync chapter progress with plugin site', + type: 'Switch', + }, + }; } export default new NovelUpdates(); diff --git a/src/plugins/english/scribblehub.ts b/src/plugins/english/scribblehub.ts index d62e3c749..e4b9dc039 100644 --- a/src/plugins/english/scribblehub.ts +++ b/src/plugins/english/scribblehub.ts @@ -9,7 +9,7 @@ class ScribbleHubPlugin implements Plugin.PluginBase { name = 'Scribble Hub'; icon = 'src/en/scribblehub/icon.png'; site = 'https://www.scribblehub.com/'; - version = '1.0.2'; + version = '1.1.0'; parseNovels(loadedCheerio: CheerioAPI) { const novels: Plugin.NovelItem[] = []; @@ -176,6 +176,34 @@ class ScribbleHubPlugin implements Plugin.PluginBase { return chapterText; } + async trackProgress(novelPath: string, chapterPath: string): Promise { + // Extract the novelId from the novelPath + const novelIdMatch = novelPath.match(/series\/(\d+)\//); + const novelId = novelIdMatch ? novelIdMatch[1] : null; + + // Extract the chapterId from the chapterPath + const chapterIdMatch = chapterPath.match(/chapter\/(\d+)\//); + const chapterId = chapterIdMatch ? chapterIdMatch[1] : null; + + const link = `${this.site}wp-admin/admin-ajax.php`; + + if (novelId && chapterId) { + const formData = new FormData(); + formData.append('action', 'wi_addchangerl'); + formData.append('strCID', chapterId); + formData.append('strSID', novelId); + + await fetchApi(link, { + method: 'POST', + body: formData, + }); + } else { + throw new Error( + `Invalid novelPath (${novelPath}) or chapterPath (${chapterPath}).`, + ); + } + } + async searchNovels(searchTerm: string): Promise { const url = `${this.site}?s=${encodeURIComponent(searchTerm)}&post_type=fictionposts`; const result = await fetchApi(url); diff --git a/src/types/plugin.ts b/src/types/plugin.ts index 83d456ef5..a938b6b83 100644 --- a/src/types/plugin.ts +++ b/src/types/plugin.ts @@ -85,6 +85,10 @@ export namespace Plugin { */ parseNovel(novelPath: string): Promise; parseChapter(chapterPath: string): Promise; + syncChapterProgress?( + novel: Plugin.SourceNovel, + chapter: Plugin.ChapterItem, + ): Promise; searchNovels(searchTerm: string, pageNo: number): Promise; resolveUrl?(path: string, isNovel?: boolean): string; };