Repository navigation
feat(english): add novelarchive.cc plugin - #2669
apauruseya7866er wants to merge 2 commits into
Conversation
Co-Authored-By: Agent <agent@example.com>
|
| .split(/\n\s*\n/) | ||
| .map(p => p.trim()) | ||
| .filter(Boolean) | ||
| .map(p => `<p>${p.replace(/\n/g, '<br>')}</p>`) |
There was a problem hiding this comment.
parseChapter inserts remote chapter text into HTML without escaping it. If the API returns text such as <img src=x onerror=alert(1)>, it survives this conversion and reaches the playground's dangerouslySetInnerHTML, where it can run code in the page. Escape the text before adding paragraph and line-break tags.
How this was verified: The API's content field flows unchanged into paragraph markup, and the chapter view inserts that result directly into the page.
| .map(p => `<p>${p.replace(/\n/g, '<br>')}</p>`) | |
| .map(p => { | |
| const escaped = p | |
| .replace(/&/g, '&') | |
| .replace(/</g, '<') | |
| .replace(/>/g, '>') | |
| .replace(/"/g, '"') | |
| .replace(/'/g, '''); | |
| return `<p>${escaped.replace(/\n/g, '<br>')}</p>`; | |
| }) |
| return data.novels.map(novel => this.mapNovel(novel)); | ||
| } | ||
|
|
||
| resolveUrl = (path: string) => `${this.site}/api/novels/${path}`; |
There was a problem hiding this comment.
resolveUrl prefixes absolute URLs with /api/novels/. The EPUB exporter passes sourceNovel.cover to this method, but parseNovel already returns an absolute cover URL. For example, https://novelarchive.cc/cover.jpg becomes https://novelarchive.cc/api/novels/https://novelarchive.cc/cover.jpg. The exporter requests the wrong address and can lose the cover image. Return absolute URLs unchanged.
| resolveUrl = (path: string) => `${this.site}/api/novels/${path}`; | |
| resolveUrl = (path: string) => | |
| /^https?:\/\//i.test(path) ? path : `${this.site}/api/novels/${path}`; |
| sort: { | ||
| label: 'Sort By', | ||
| type: FilterTypes.Picker, | ||
| value: 'recent', |
There was a problem hiding this comment.
The default sort is recent, and the caller copies that default into filters even when the user selects Popular. popularNovels then sends sort=recent, so Latest and Popular show the same ordering until the user changes the filter. Default the sort filter to popular; the existing showLatestNovels branch can still force recent.
| value: 'recent', | |
| value: 'popular', |
| status: | ||
| novel.release_status === 'completed' | ||
| ? NovelStatus.Completed | ||
| : NovelStatus.Ongoing, |
There was a problem hiding this comment.
parseNovel labels every status except completed as Ongoing, including hiatus novels supported by this plugin's own filter. Readers opening those novels see the wrong status. Map hiatus novels to NovelStatus.OnHiatus instead.
| status: | |
| novel.release_status === 'completed' | |
| ? NovelStatus.Completed | |
| : NovelStatus.Ongoing, | |
| status: | |
| novel.release_status === 'completed' | |
| ? NovelStatus.Completed | |
| : novel.release_status === 'hiatus' | |
| ? NovelStatus.OnHiatus | |
| : NovelStatus.Ongoing, |
A review of the novelarchive.cc PR (lnreader#2669) flagged that a hiatus novel was reported as still ongoing. It did not reach that conclusion directly - the mapping fell through to Unknown - but it landed in the same wrong place: any status that is neither "completed" nor "ongoing" was reported as something the reader would misread. NovelStatus.OnHiatus already existed and nothing used it here. A novel that stopped updating is not being written, and telling a reader otherwise sends them after a chapter that is not coming. Also covers the other spellings the API uses for the same state. Chapter-text escaping was already correct in this file and is left alone.
Addresses the review on lnreader#2669. Chapter text is remote content and was being wrapped in <p> unescaped, so a stray `<` in the prose - or anything the site chose to serve - was read as a markup tag. Escaped before wrapping. The playground renders chapter text with dangerouslySetInnerHTML, so this was reachable there; the app's renderers do not execute it, but a plugin should not emit unescaped remote markup. release_status only ever mapped to completed or ongoing, so a novel on hiatus reported itself as still being written. NovelStatus.OnHiatus already existed and is now used, along with the other spellings the API uses for that state.
Summary
Adds a new plugin for novelarchive.cc, a web novel reading site.
Implementation
Testing
pm run check:plugin -- plugins/english/novelarchive.ts passes:
🤖 Generated with Claude Code