├── guide.md
├── .vscode
└── settings.json
├── defaultRes
├── res
│ ├── raw
│ │ └── gradle.properties
│ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_background.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_monochrome.png
│ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_background.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_monochrome.png
│ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_background.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_monochrome.png
│ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_background.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_monochrome.png
│ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_background.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_monochrome.png
│ └── mipmap-anydpi-v26
│ │ └── ic_launcher.xml
├── AndroidManifest.xml
└── build.gradle.kts
├── common
├── src
│ └── jsMain
│ │ └── kotlin
│ │ └── ireader
│ │ └── js
│ │ └── runtime
│ │ └── Utils.kt
└── build.gradle.kts
├── docs
├── source-api
│ ├── .gitignore
│ ├── src
│ │ ├── commonMain
│ │ │ └── kotlin
│ │ │ │ └── ireader
│ │ │ │ └── core
│ │ │ │ ├── source
│ │ │ │ ├── model
│ │ │ │ │ ├── Listing.kt
│ │ │ │ │ ├── PageListEmpty.kt
│ │ │ │ │ ├── DeepLink.kt
│ │ │ │ │ ├── LocalNovelDetails.kt
│ │ │ │ │ ├── FilterList.kt
│ │ │ │ │ └── MangasPageInfo.kt
│ │ │ │ ├── Dependencies.kt
│ │ │ │ ├── DeepLinkSource.kt
│ │ │ │ ├── CatalogSource.kt
│ │ │ │ └── Source.kt
│ │ │ │ ├── http
│ │ │ │ ├── WebViewUtil.kt
│ │ │ │ ├── DEFAULT_USER_AGENT.kt
│ │ │ │ ├── HttpModule.kt
│ │ │ │ ├── NetworkConfig.kt
│ │ │ │ ├── JSFactory.kt
│ │ │ │ ├── RatelimitException.kt
│ │ │ │ ├── SSLConfiguration.kt
│ │ │ │ ├── CookieSynchronizer.kt
│ │ │ │ ├── HttpClients.kt
│ │ │ │ ├── WebViewManger.kt
│ │ │ │ ├── JS.kt
│ │ │ │ └── Exception.kt
│ │ │ │ └── util
│ │ │ │ ├── TimeUtils.kt
│ │ │ │ └── CoroutineExt.kt
│ │ ├── iosMain
│ │ │ └── kotlin
│ │ │ │ └── ireader
│ │ │ │ └── core
│ │ │ │ ├── http
│ │ │ │ ├── HttpModule.ios.kt
│ │ │ │ └── HttpClients.ios.kt
│ │ │ │ └── util
│ │ │ │ └── CoroutineExt.ios.kt
│ │ ├── androidMain
│ │ │ ├── kotlin
│ │ │ │ └── ireader
│ │ │ │ │ └── core
│ │ │ │ │ ├── http
│ │ │ │ │ ├── HttpModule.kt
│ │ │ │ │ ├── OkHttpExtension.kt
│ │ │ │ │ ├── CookieSynchronizer.kt
│ │ │ │ │ └── AndroidCookieJar.kt
│ │ │ │ │ └── util
│ │ │ │ │ └── createCoroutineScope.kt
│ │ │ ├── AndroidManifest.xml
│ │ │ └── res
│ │ │ │ └── values
│ │ │ │ └── string.xml
│ │ ├── jsMain
│ │ │ └── kotlin
│ │ │ │ └── ireader
│ │ │ │ └── core
│ │ │ │ ├── http
│ │ │ │ ├── HttpModule.js.kt
│ │ │ │ ├── SSLConfiguration.js.kt
│ │ │ │ └── CookieSynchronizer.js.kt
│ │ │ │ └── util
│ │ │ │ └── CoroutineExt.js.kt
│ │ ├── desktopMain
│ │ │ └── kotlin
│ │ │ │ └── ireader
│ │ │ │ └── core
│ │ │ │ ├── http
│ │ │ │ ├── HttpModule.kt
│ │ │ │ ├── OkHttpExtension.kt
│ │ │ │ ├── CookieSynchronizer.kt
│ │ │ │ ├── CookieStore.kt
│ │ │ │ ├── BrowserEngine.kt
│ │ │ │ ├── WebViewManger.kt
│ │ │ │ └── PersistentCookieJar.kt
│ │ │ │ ├── util
│ │ │ │ └── createCoroutineScope.kt
│ │ │ │ └── storage
│ │ │ │ └── CacheDir.kt
│ │ └── jvmMain
│ │ │ └── kotlin
│ │ │ └── ireader
│ │ │ └── core
│ │ │ └── http
│ │ │ └── JSFactory.kt
│ └── gradle.properties
├── example-annotated
│ └── build.gradle.kts
├── example-autoid
│ └── build.gradle.kts
├── example-madara
│ └── main
│ │ └── src
│ │ └── ireader
│ │ └── examplemadara
│ │ └── ExampleMadara.kt
└── ADD_SOURCE_GUIDE.md
├── deeplink
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── tachiyomix
│ │ └── deeplink
│ │ └── SourceDeepLinkActivity.java
└── build.gradle.kts
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── sources
├── ar
│ ├── riwyat
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── kolnovel
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── sunovels
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── realmnovel
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── rewayatfans
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ └── novelparadise
│ │ ├── main
│ │ └── assets
│ │ │ └── icon.png
│ │ └── build.gradle.kts
├── cn
│ ├── aixdzs
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ └── sexinsex
│ │ ├── main
│ │ └── assets
│ │ │ └── ic_launcher.png
│ │ └── build.gradle.kts
├── en
│ ├── fanmtl
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── lnmtl
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── lnmtl
│ │ │ │ ├── volume
│ │ │ │ ├── LNMTLVolumeResponse.kt
│ │ │ │ └── LNMTLVolumnResponseItem.kt
│ │ │ │ └── chapters
│ │ │ │ ├── LNMTLResponse.kt
│ │ │ │ └── Data.kt
│ │ └── build.gradle.kts
│ ├── pawread
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── ranobes
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── ranobes
│ │ │ │ ├── Chapter.kt
│ │ │ │ └── ChapterDTO.kt
│ │ └── build.gradle.kts
│ ├── readmtl
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── wnmtl
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ ├── wnmtl
│ │ │ │ ├── chapter
│ │ │ │ │ ├── ChapterDTO.kt
│ │ │ │ │ ├── Data.kt
│ │ │ │ │ └── Result.kt
│ │ │ │ ├── content
│ │ │ │ │ ├── ContentDTO.kt
│ │ │ │ │ └── Data.kt
│ │ │ │ └── explore
│ │ │ │ │ ├── ExploreDTO.kt
│ │ │ │ │ ├── Data.kt
│ │ │ │ │ └── Result.kt
│ │ │ │ └── Constants.kt
│ │ └── build.gradle.kts
│ ├── coolnovel
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── daonovel
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── fastnovel
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── mostnovel
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── mtlnation
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── mtlnation
│ │ │ │ ├── ChapterDTO.kt
│ │ │ │ └── Data.kt
│ │ └── build.gradle.kts
│ ├── novelfire
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ ├── build.gradle.kts
│ │ └── README.md
│ ├── novelfull
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── novelhall
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── novelstic
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── royalroad
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── skynovel
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── allnovelfull
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── boxnovelcom
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── comrademao
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── freewebnovel
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── lightnovels
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── lightnovels
│ │ │ │ ├── detail_dto
│ │ │ │ ├── Query.kt
│ │ │ │ ├── RuntimeConfig.kt
│ │ │ │ ├── Tag.kt
│ │ │ │ ├── Props.kt
│ │ │ │ ├── Author.kt
│ │ │ │ ├── Genre.kt
│ │ │ │ ├── GenreX.kt
│ │ │ │ ├── CachedHotNovel.kt
│ │ │ │ ├── CachedLatestChapter.kt
│ │ │ │ ├── NovelDetail.kt
│ │ │ │ ├── PageProps.kt
│ │ │ │ └── NovelInfo.kt
│ │ │ │ ├── content_dto
│ │ │ │ ├── RuntimeConfig.kt
│ │ │ │ ├── Props.kt
│ │ │ │ ├── Query.kt
│ │ │ │ ├── PageProps.kt
│ │ │ │ ├── Novel.kt
│ │ │ │ ├── ContentDTO.kt
│ │ │ │ └── CachedChapterInfo.kt
│ │ │ │ ├── search_dto
│ │ │ │ ├── SearchDTO.kt
│ │ │ │ ├── SearchResult.kt
│ │ │ │ ├── Result.kt
│ │ │ │ └── ResultX.kt
│ │ │ │ ├── books_dto
│ │ │ │ ├── BookListDTO.kt
│ │ │ │ └── Result.kt
│ │ │ │ └── chapter_dto
│ │ │ │ ├── ChapterDTO.kt
│ │ │ │ └── Result.kt
│ │ └── build.gradle.kts
│ ├── mylovenovel
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ ├── constants
│ │ │ │ └── Constants.kt
│ │ │ │ └── mylovenovel
│ │ │ │ └── merge.kt
│ │ └── build.gradle.kts
│ ├── novelbuddy
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ ├── build.gradle.kts
│ │ └── README.md
│ ├── novelfullme
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── novelowlcom
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── novelscafes
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── noveltop1net
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── novelupdates
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── pandanovel
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── pandanovel
│ │ │ │ └── chapter
│ │ │ │ ├── ChapterDTO.kt
│ │ │ │ ├── Data.kt
│ │ │ │ └── Info.kt
│ │ └── build.gradle.kts
│ ├── realwebnovel
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── reaperscans
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── scribblehub
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── webnovelcom
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── webnovelsite
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── wuxiaworld
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── wuxiaworld
│ │ │ │ ├── PopularDTO.kt
│ │ │ │ └── Item.kt
│ │ └── build.gradle.kts
│ ├── lightnovelpub
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── lightnovelpub
│ │ │ │ └── SearchResponse.kt
│ │ ├── webnovelpub
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── WebNovelPub.kt
│ │ └── build.gradle.kts
│ ├── novelsemperor
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── wuxiaworldsite
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── genesistranslator
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── kissnovellove
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png.png
│ │ └── build.gradle.kts
│ ├── koreanonline
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png.png
│ │ └── build.gradle.kts
│ ├── lightnovelreader
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── lightnovelreader
│ │ │ │ ├── SearchResponse.kt
│ │ │ │ └── Result.kt
│ │ └── build.gradle.kts
│ ├── qidianundergrond
│ │ ├── main
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── qidianundergrond
│ │ │ │ ├── QUGroup.kt
│ │ │ │ ├── ChapterGroup.kt
│ │ │ │ ├── ChapterGroupItem.kt
│ │ │ │ └── QUGroupItem.kt
│ │ └── build.gradle.kts
│ ├── wuxiaworldsiteco
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ ├── README.md
│ ├── fictionzone
│ │ └── build.gradle.kts
│ ├── libread
│ │ └── build.gradle.kts
│ ├── dreambigtl
│ │ └── build.gradle.kts
│ ├── novelonline
│ │ └── build.gradle.kts
│ ├── storyseedling
│ │ └── build.gradle.kts
│ ├── bestlightnovel
│ │ └── build.gradle.kts
│ ├── wuxiaclick
│ │ └── build.gradle.kts
│ ├── build.gradle.kts
│ ├── fenrir
│ │ ├── build.gradle.kts
│ │ └── README.md
│ └── mydramanovel
│ │ └── build.gradle.kts
├── fa
│ └── uptvs
│ │ ├── main
│ │ └── assets
│ │ │ └── icon.png
│ │ └── build.gradle.kts
├── fr
│ ├── chireads
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ └── noveldeglace
│ │ ├── main
│ │ └── assets
│ │ │ └── icon.png
│ │ └── build.gradle.kts
├── tu
│ ├── epiknovel
│ │ ├── main
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ └── build.gradle.kts
│ └── novelgecesi
│ │ ├── main
│ │ └── assets
│ │ │ └── icon.png
│ │ └── build.gradle.kts
├── in
│ └── indowebnovel
│ │ ├── main
│ │ └── assets
│ │ │ └── icon.png
│ │ └── build.gradle.kts
├── multisrc
│ ├── madara
│ │ ├── armtl
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── armtl
│ │ │ │ ├── ArMtl.kt
│ │ │ │ └── Constants.kt
│ │ ├── azora
│ │ │ └── assets
│ │ │ │ └── icon.png
│ │ ├── hizomanga
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── hizomang
│ │ │ │ ├── HizoManga.kt
│ │ │ │ └── Constants.kt
│ │ ├── arnovel
│ │ │ └── assets
│ │ │ │ └── arnovel.png
│ │ ├── freenovel
│ │ │ ├── assets
│ │ │ │ └── freenovel.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── freenovel
│ │ │ │ └── Freenovel.kt
│ │ ├── meionovel
│ │ │ ├── assets
│ │ │ │ └── meionovel.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── meionovel
│ │ │ │ └── MeioNovel.kt
│ │ ├── morenovel
│ │ │ ├── assets
│ │ │ │ └── morenovel.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── morenovel
│ │ │ │ └── MoreNovel.kt
│ │ ├── novel4up
│ │ │ ├── assets
│ │ │ │ └── novel4up.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── novel4up
│ │ │ │ └── Novel4Up.kt
│ │ ├── zinnovel
│ │ │ ├── assets
│ │ │ │ └── zinnovel.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── zinnovel
│ │ │ │ └── Zinnovel.kt
│ │ ├── clicknovel
│ │ │ ├── assets
│ │ │ │ └── clicknovel.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── clicknovel
│ │ │ │ └── ClickNovel.kt
│ │ ├── mtlnovelclub
│ │ │ ├── assets
│ │ │ │ ├── zinnovel.png
│ │ │ │ └── mtlnovelclub.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── mtlnovelclub
│ │ │ │ └── MTLNovelClub.kt
│ │ ├── lunarletters
│ │ │ ├── assets
│ │ │ │ └── lunarletters.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── lunarletters
│ │ │ │ └── LunarLetters.kt
│ │ ├── webnovelover
│ │ │ ├── assets
│ │ │ │ └── webnovellover.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── webnovelover
│ │ │ │ └── WebNovelLover.kt
│ │ ├── firstkissnovel
│ │ │ ├── assets
│ │ │ │ └── 1stkissnovel.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── firstkissnovel
│ │ │ │ └── FirstKissNovel.kt
│ │ ├── mysticalseries
│ │ │ ├── assets
│ │ │ │ └── mysticalseries.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── mysticalseries
│ │ │ │ └── MysticalSeries.kt
│ │ ├── noveltranslate
│ │ │ ├── assets
│ │ │ │ └── noveltranslate.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── noveltranslate
│ │ │ │ └── NovelTranslate.kt
│ │ ├── readwebnovels
│ │ │ ├── assets
│ │ │ │ └── readwebnovels.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── readwebnovels
│ │ │ │ └── ReadWebNovels.kt
│ │ ├── main
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── madara
│ │ │ │ └── Path.kt
│ │ ├── novelmultiverse
│ │ │ ├── assets
│ │ │ │ └── novelmultiverse.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── novelmultiverse
│ │ │ │ └── NovelMultiverse.kt
│ │ ├── lightnovelheaven
│ │ │ ├── assets
│ │ │ │ └── lightnovelheaven.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── lightnovelheaven
│ │ │ │ └── LightNovelHeaven.kt
│ │ ├── turkcelightnovels
│ │ │ ├── assets
│ │ │ │ └── turkcelightnovels.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── turkcelightnovels
│ │ │ │ └── TurkceLightNovels.kt
│ │ ├── sleepytranslations
│ │ │ ├── assets
│ │ │ │ └── sleepytranslations.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── sleepytranslations
│ │ │ │ └── SleepyTranslations.kt
│ │ ├── neosekaitranslations
│ │ │ ├── assets
│ │ │ │ └── noobchantranslation.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── neosekaitranslations
│ │ │ │ ├── Constants.kt
│ │ │ │ └── Neosekaitranslations.kt
│ │ ├── sonicmtl
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── sonicmtl
│ │ │ │ └── SonicMTL.kt
│ │ └── cratenovel
│ │ │ └── src
│ │ │ └── ireader
│ │ │ └── cratenovel
│ │ │ └── CrateNovel.kt
│ ├── readwn
│ │ ├── readwn
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── readwn
│ │ │ │ └── Readwn.kt
│ │ ├── ltnovel
│ │ │ ├── assets
│ │ │ │ └── ltnovel.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── ltnovel
│ │ │ │ └── Ltnovel.kt
│ │ ├── novelmt
│ │ │ ├── assets
│ │ │ │ └── novelmt.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── novelmt
│ │ │ │ └── Readwn.kt
│ │ └── build.gradle.kts
│ ├── skynovel
│ │ ├── wbnovel
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── wbnovel
│ │ │ │ └── WbNovel.kt
│ │ ├── skynovel
│ │ │ ├── assets
│ │ │ │ └── icon.png
│ │ │ └── src
│ │ │ │ └── ireader
│ │ │ │ └── skynovel
│ │ │ │ └── SkyNovel.kt
│ │ └── build.gradle.kts
│ └── mtlnovel
│ │ ├── mtlnovelen
│ │ ├── assets
│ │ │ └── icon.png
│ │ └── src
│ │ │ └── ireader
│ │ │ └── mtlnovelen
│ │ │ └── MtlNovelEn.kt
│ │ ├── mtlnoveles
│ │ ├── assets
│ │ │ └── icon.png
│ │ └── src
│ │ │ └── ireader
│ │ │ └── mtlnoveles
│ │ │ └── MtlNovelEs.kt
│ │ ├── mtlnovelfr
│ │ ├── assets
│ │ │ └── icon.png
│ │ └── src
│ │ │ └── ireader
│ │ │ └── mtlnovelfr
│ │ │ └── MtlNovelFr.kt
│ │ ├── mtlnovelin
│ │ ├── assets
│ │ │ └── icon.png
│ │ └── src
│ │ │ └── ireader
│ │ │ └── mtlnovelin
│ │ │ └── MtlNovelIn.kt
│ │ ├── main
│ │ └── src
│ │ │ └── ireader
│ │ │ └── mtlnovelmodel
│ │ │ ├── mtlSearchItem.kt
│ │ │ ├── Item.kt
│ │ │ └── Result.kt
│ │ └── build.gradle.kts
└── common
│ └── build.gradle.kts
├── extensions
├── res
│ └── mipmap-mdpi
│ │ └── ic_launcher.png
└── AndroidManifest.xml
├── test-extensions
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── ireader
│ │ │ ├── app
│ │ │ └── Constatns.kt
│ │ │ └── constants
│ │ │ └── Constants.kt
│ ├── androidTest
│ │ └── java
│ │ │ └── ireader
│ │ │ └── app
│ │ │ ├── README.md
│ │ │ ├── tests
│ │ │ ├── ContentChecker.kt
│ │ │ ├── BookListChecker.kt
│ │ │ └── InfoChecked.kt
│ │ │ └── Constants.kt
│ └── test
│ │ └── java
│ │ └── ireader
│ │ └── app
│ │ ├── Cosntants.kt
│ │ ├── mockcomponents
│ │ ├── FakeHttpClients.kt
│ │ └── FakePreferencesStore.kt
│ │ └── tests
│ │ ├── ContentChecker.kt
│ │ ├── BookListChecker.kt
│ │ ├── InfoChecked.kt
│ │ └── ChapterChecker.kt
└── build.gradle.kts
├── .idea
└── inspectionProfiles
│ ├── ktlint.xml
│ ├── profiles_settings.xml
│ └── Project_Default.xml
├── scripts
├── converter_v5
│ └── __init__.py
├── bump-version-codes.ps1
└── bump-version-codes.py
├── .github
├── runner-files
│ └── ci-gradle.properties
├── workflows
│ ├── cancel_pull_request.yml
│ └── code_quality.yml
├── scripts
│ ├── sign-apks.sh
│ └── commit-repo.sh
├── PULL_REQUEST_TEMPLATE.md
└── ISSUE_TEMPLATE
│ └── feature_request.yml
├── annotations
├── build.gradle.kts
└── src
│ └── main
│ └── kotlin
│ └── Extension.kt
├── multisrc
├── src
│ └── main
│ │ └── java
│ │ └── ireader
│ │ └── utility
│ │ └── TestConstants.kt
└── build.gradle.kts
├── .editorconfig
├── compiler
├── build.gradle.kts
└── src
│ └── main
│ └── resources
│ └── META-INF
│ └── services
│ └── com.google.devtools.ksp.processing.SymbolProcessorProvider
├── gradle.properties
├── e1be5cc3-0144-44d0-8517-801c039c045f.json
└── js-sources
└── webpack.config.d
└── bundle.js
/guide.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/defaultRes/res/raw/gradle.properties:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/common/src/jsMain/kotlin/ireader/js/runtime/Utils.kt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/defaultRes/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/source-api/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
3 | key.properties
4 | keys.properties
5 |
--------------------------------------------------------------------------------
/deeplink/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/sources/ar/riwyat/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/ar/riwyat/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/cn/aixdzs/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/cn/aixdzs/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/fanmtl/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/fanmtl/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/lnmtl/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/lnmtl/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/pawread/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/pawread/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/ranobes/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/ranobes/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/readmtl/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/readmtl/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/wnmtl/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/wnmtl/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/fa/uptvs/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/fa/uptvs/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/ar/kolnovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/ar/kolnovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/ar/sunovels/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/ar/sunovels/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/coolnovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/coolnovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/daonovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/daonovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/fastnovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/fastnovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/mostnovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/mostnovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/mtlnation/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/mtlnation/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/novelfire/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/novelfire/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/novelfull/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/novelfull/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/novelhall/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/novelhall/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/novelstic/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/novelstic/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/royalroad/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/royalroad/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/skynovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/skynovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/fr/chireads/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/fr/chireads/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/tu/epiknovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/tu/epiknovel/main/assets/icon.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/extensions/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/extensions/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sources/ar/realmnovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/ar/realmnovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/ar/rewayatfans/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/ar/rewayatfans/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/allnovelfull/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/allnovelfull/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/boxnovelcom/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/boxnovelcom/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/comrademao/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/comrademao/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/freewebnovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/freewebnovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/lightnovels/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/mylovenovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/mylovenovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/mylovenovel/main/src/ireader/constants/Constants.kt:
--------------------------------------------------------------------------------
1 | package ireader.constants
2 |
3 | // Test constants removed - not compatible with KMP
4 |
--------------------------------------------------------------------------------
/sources/en/novelbuddy/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/novelbuddy/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/novelfullme/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/novelfullme/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/novelowlcom/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/novelowlcom/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/novelscafes/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/novelscafes/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/noveltop1net/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/noveltop1net/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/novelupdates/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/novelupdates/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/pandanovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/pandanovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/realwebnovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/realwebnovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/reaperscans/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/reaperscans/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/scribblehub/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/scribblehub/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/webnovelcom/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/webnovelcom/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/webnovelsite/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/webnovelsite/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/wuxiaworld/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/wuxiaworld/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/fr/noveldeglace/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/fr/noveldeglace/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/in/indowebnovel/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/in/indowebnovel/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/tu/novelgecesi/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/tu/novelgecesi/main/assets/icon.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sources/ar/novelparadise/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/ar/novelparadise/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/lightnovelpub/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/lightnovelpub/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/novelsemperor/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/novelsemperor/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/wuxiaworldsite/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/wuxiaworldsite/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/armtl/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/armtl/assets/icon.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/azora/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/azora/assets/icon.png
--------------------------------------------------------------------------------
/sources/multisrc/readwn/readwn/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/readwn/readwn/assets/icon.png
--------------------------------------------------------------------------------
/test-extensions/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/sources/cn/sexinsex/main/assets/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/cn/sexinsex/main/assets/ic_launcher.png
--------------------------------------------------------------------------------
/sources/en/genesistranslator/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/genesistranslator/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/kissnovellove/main/assets/icon.png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/kissnovellove/main/assets/icon.png.png
--------------------------------------------------------------------------------
/sources/en/koreanonline/main/assets/icon.png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/koreanonline/main/assets/icon.png.png
--------------------------------------------------------------------------------
/sources/en/lightnovelreader/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/lightnovelreader/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/mtlnation/main/src/ireader/mtlnation/ChapterDTO.kt:
--------------------------------------------------------------------------------
1 | package ireader.mtlnation
2 |
3 | data class ChapterDTO(
4 | val `data`: List
5 | )
6 |
--------------------------------------------------------------------------------
/sources/en/qidianundergrond/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/qidianundergrond/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/wuxiaworldsiteco/main/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/wuxiaworldsiteco/main/assets/icon.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/hizomanga/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/hizomanga/assets/icon.png
--------------------------------------------------------------------------------
/sources/multisrc/skynovel/wbnovel/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/skynovel/wbnovel/assets/icon.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/arnovel/assets/arnovel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/arnovel/assets/arnovel.png
--------------------------------------------------------------------------------
/sources/multisrc/readwn/ltnovel/assets/ltnovel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/readwn/ltnovel/assets/ltnovel.png
--------------------------------------------------------------------------------
/sources/multisrc/readwn/novelmt/assets/novelmt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/readwn/novelmt/assets/novelmt.png
--------------------------------------------------------------------------------
/sources/multisrc/skynovel/skynovel/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/skynovel/skynovel/assets/icon.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-hdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-hdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-hdpi/ic_launcher_monochrome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-hdpi/ic_launcher_monochrome.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-mdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-mdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-mdpi/ic_launcher_monochrome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-mdpi/ic_launcher_monochrome.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xhdpi/ic_launcher_monochrome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xhdpi/ic_launcher_monochrome.png
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/source/model/Listing.kt:
--------------------------------------------------------------------------------
1 |
2 |
3 | package ireader.core.source.model
4 |
5 | abstract class Listing(val name: String)
6 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/source/model/PageListEmpty.kt:
--------------------------------------------------------------------------------
1 |
2 |
3 | package ireader.core.source.model
4 |
5 | class PageListEmpty : Exception()
6 |
--------------------------------------------------------------------------------
/sources/en/lightnovelpub/webnovelpub/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/en/lightnovelpub/webnovelpub/assets/icon.png
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/Query.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class Query(
4 | val slug: String
5 | )
6 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/freenovel/assets/freenovel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/freenovel/assets/freenovel.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/meionovel/assets/meionovel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/meionovel/assets/meionovel.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/morenovel/assets/morenovel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/morenovel/assets/morenovel.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/novel4up/assets/novel4up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/novel4up/assets/novel4up.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/zinnovel/assets/zinnovel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/zinnovel/assets/zinnovel.png
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/mtlnovelen/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/mtlnovel/mtlnovelen/assets/icon.png
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/mtlnoveles/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/mtlnovel/mtlnoveles/assets/icon.png
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/mtlnovelfr/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/mtlnovel/mtlnovelfr/assets/icon.png
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/mtlnovelin/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/mtlnovel/mtlnovelin/assets/icon.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xxhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xxhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xxhdpi/ic_launcher_monochrome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xxhdpi/ic_launcher_monochrome.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xxxhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xxxhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-xxxhdpi/ic_launcher_monochrome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/defaultRes/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/clicknovel/assets/clicknovel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/clicknovel/assets/clicknovel.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/mtlnovelclub/assets/zinnovel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/mtlnovelclub/assets/zinnovel.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/lunarletters/assets/lunarletters.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/lunarletters/assets/lunarletters.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/mtlnovelclub/assets/mtlnovelclub.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/mtlnovelclub/assets/mtlnovelclub.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/webnovelover/assets/webnovellover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/webnovelover/assets/webnovellover.png
--------------------------------------------------------------------------------
/sources/en/lightnovelreader/main/src/ireader/lightnovelreader/SearchResponse.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovelreader
2 |
3 | data class SearchResponse(
4 | val results: List
5 | )
6 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/RuntimeConfig.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class RuntimeConfig(
4 | val SHOW_AD: Boolean
5 | )
6 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/firstkissnovel/assets/1stkissnovel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/firstkissnovel/assets/1stkissnovel.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/mysticalseries/assets/mysticalseries.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/mysticalseries/assets/mysticalseries.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/noveltranslate/assets/noveltranslate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/noveltranslate/assets/noveltranslate.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/readwebnovels/assets/readwebnovels.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/readwebnovels/assets/readwebnovels.png
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/ktlint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/scripts/converter_v5/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Converter V5 - Complete Rewrite
3 | AI-powered with proper TypeScript analysis
4 | """
5 |
6 | __version__ = "5.0.0"
7 | __status__ = "In Development"
8 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/content_dto/RuntimeConfig.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.content_dto
2 |
3 | data class RuntimeConfig(
4 | val SHOW_AD: Boolean
5 | )
6 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/main/src/ireader/madara/Path.kt:
--------------------------------------------------------------------------------
1 | package ireader.madara
2 |
3 | data class Path(
4 | val novel: String,
5 | val novels: String,
6 | val chapter: String,
7 | )
8 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/novelmultiverse/assets/novelmultiverse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/novelmultiverse/assets/novelmultiverse.png
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/Tag.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class Tag(
4 | val tag_name: String,
5 | val tag_slug: String
6 | )
7 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/lightnovelheaven/assets/lightnovelheaven.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/lightnovelheaven/assets/lightnovelheaven.png
--------------------------------------------------------------------------------
/sources/multisrc/madara/turkcelightnovels/assets/turkcelightnovels.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/turkcelightnovels/assets/turkcelightnovels.png
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/content_dto/Props.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.content_dto
2 |
3 | data class Props(
4 | val __N_SSP: Boolean,
5 | val pageProps: PageProps
6 | )
7 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/content_dto/Query.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.content_dto
2 |
3 | data class Query(
4 | val chapterSlug: String,
5 | val novelSlug: String
6 | )
7 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/Props.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class Props(
4 | val __N_SSG: Boolean,
5 | val pageProps: PageProps
6 | )
7 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/sleepytranslations/assets/sleepytranslations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/sleepytranslations/assets/sleepytranslations.png
--------------------------------------------------------------------------------
/.github/runner-files/ci-gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.daemon=false
2 | org.gradle.jvmargs=-Xmx5120m
3 | org.gradle.workers.max=2
4 |
5 | kotlin.incremental=false
6 | kotlin.compiler.execution.strategy=in-process
7 |
--------------------------------------------------------------------------------
/sources/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("jvm")
3 | }
4 |
5 | dependencies {
6 | compileOnly(libs.stdlib)
7 | compileOnly(libs.jsoup)
8 | compileOnly(libs.ireader.core)
9 | }
10 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/neosekaitranslations/assets/noobchantranslation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IReaderorg/IReader-extensions/HEAD/sources/multisrc/madara/neosekaitranslations/assets/noobchantranslation.png
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/Author.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class Author(
4 | val id: Int,
5 | val name: String,
6 | val slug: String
7 | )
8 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/Genre.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class Genre(
4 | val id: Int,
5 | val name: String,
6 | val slug: String
7 | )
8 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/GenreX.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class GenreX(
4 | val id: Int,
5 | val name: String,
6 | val slug: String
7 | )
8 |
--------------------------------------------------------------------------------
/sources/en/qidianundergrond/main/src/ireader/qidianundergrond/QUGroup.kt:
--------------------------------------------------------------------------------
1 | package ireader.qidianundergrond
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | class QUGroup : ArrayList()
7 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/WebViewUtil.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | object WebViewUtil {
4 | const val SPOOF_PACKAGE_NAME = "org.chromium.chrome"
5 | const val MINIMUM_WEBVIEW_VERSION = 99
6 | }
--------------------------------------------------------------------------------
/sources/en/lnmtl/main/src/ireader/lnmtl/volume/LNMTLVolumeResponse.kt:
--------------------------------------------------------------------------------
1 | package ireader.lnmtl.volume
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | class LNMTLVolumeResponse : ArrayList()
--------------------------------------------------------------------------------
/sources/en/qidianundergrond/main/src/ireader/qidianundergrond/ChapterGroup.kt:
--------------------------------------------------------------------------------
1 | package ireader.qidianundergrond
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | class ChapterGroup : ArrayList()
7 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/content_dto/PageProps.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.content_dto
2 |
3 | data class PageProps(
4 | val cachedChapterInfo: CachedChapterInfo,
5 | val pandaLinks: List
6 | )
7 |
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/main/src/ireader/mtlnovelmodel/mtlSearchItem.kt:
--------------------------------------------------------------------------------
1 | package ireader.mtlnovelmodel
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class mtlSearchItem(
7 | val items: List- ,
8 | )
9 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/DEFAULT_USER_AGENT.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | const val DEFAULT_USER_AGENT = "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5304.141 Mobile Safari/537.36"
--------------------------------------------------------------------------------
/sources/en/lightnovelreader/main/src/ireader/lightnovelreader/Result.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovelreader
2 |
3 | data class Result(
4 | val image: String,
5 | val link: String,
6 | val original_title: String,
7 | val overview: String
8 | )
9 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/search_dto/SearchDTO.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.search_dto
2 |
3 | data class SearchDTO(
4 | val index: Int,
5 | val limit: Int,
6 | val results: List,
7 | val total: Int
8 | )
9 |
--------------------------------------------------------------------------------
/test-extensions/src/androidTest/java/ireader/app/README.md:
--------------------------------------------------------------------------------
1 | ## don't use androidTest for now, because gradle version is lower than expected.
2 |
3 | ## if someone can upgrade kotlin version and gradle version of this project without error, I really appreciate it.
--------------------------------------------------------------------------------
/docs/source-api/src/iosMain/kotlin/ireader/core/http/HttpModule.ios.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * iOS HTTP module placeholder
5 | * Note: HttpClients should be created in DomainModule with proper DI
6 | */
7 | actual val httpModule: Any = Unit
8 |
--------------------------------------------------------------------------------
/sources/ar/kolnovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("ar").map { lang ->
2 | Extension(
3 | name = "KolNovel",
4 | versionCode = 16,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false)
9 | }.also(::register)
10 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/search_dto/SearchResult.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.search_dto
2 |
3 | data class SearchResult(
4 | val index: Int,
5 | val limit: Int,
6 | val results: List,
7 | val total: Int
8 | )
9 |
--------------------------------------------------------------------------------
/sources/en/lightnovelpub/main/src/ireader/lightnovelpub/SearchResponse.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovelpub
2 | @kotlinx.serialization.Serializable
3 | data class SearchResponse(
4 | val `$id`: String,
5 | val resultview: String,
6 | val success: Boolean
7 | )
8 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/CachedHotNovel.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class CachedHotNovel(
4 | val genres: List,
5 | val id: Int,
6 | val slug: String,
7 | val title: String
8 | )
9 |
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/main/src/ireader/mtlnovelmodel/Item.kt:
--------------------------------------------------------------------------------
1 | package ireader.mtlnovelmodel
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Item(
7 | val query: String,
8 | val results: List,
9 | )
10 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/HttpModule.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * Common HTTP module declaration
5 | * Platform-specific implementations are provided in androidMain and desktopMain
6 | */
7 | expect val httpModule: Any
8 |
--------------------------------------------------------------------------------
/sources/ar/novelparadise/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("ar").map { lang ->
2 | Extension(
3 | name = "NovelParadise",
4 | versionCode = 4,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false)
9 | }.also(::register)
10 |
--------------------------------------------------------------------------------
/sources/en/README.md:
--------------------------------------------------------------------------------
1 | # ReadNovelFullPlugin
2 |
3 | **100% Accurate Conversion**
4 |
5 | Source:
6 | Version: 1.0.0
7 | Converter: Ultimate V4
8 |
9 | ## Status
10 | ✅ Production Ready
11 | ✅ All selectors validated
12 | ✅ Icon downloaded
13 | ✅ 100% test coverage expected
14 |
--------------------------------------------------------------------------------
/sources/en/qidianundergrond/main/src/ireader/qidianundergrond/ChapterGroupItem.kt:
--------------------------------------------------------------------------------
1 | package ireader.qidianundergrond
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class ChapterGroupItem(
7 | val Href: String,
8 | val Text: String
9 | )
10 |
--------------------------------------------------------------------------------
/sources/en/wnmtl/main/src/ireader/wnmtl/chapter/ChapterDTO.kt:
--------------------------------------------------------------------------------
1 | package ireader.wnmtl.chapter
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class ChapterDTO(
7 | val code: Int,
8 | val `data`: Data,
9 | val message: String
10 | )
11 |
--------------------------------------------------------------------------------
/sources/en/wnmtl/main/src/ireader/wnmtl/content/ContentDTO.kt:
--------------------------------------------------------------------------------
1 | package ireader.wnmtl.content
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class ContentDTO(
7 | val code: Int,
8 | val `data`: Data,
9 | val message: String
10 | )
11 |
--------------------------------------------------------------------------------
/sources/en/wnmtl/main/src/ireader/wnmtl/explore/ExploreDTO.kt:
--------------------------------------------------------------------------------
1 | package ireader.wnmtl.explore
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class ExploreDTO(
7 | val code: Int,
8 | val `data`: Data,
9 | val message: String
10 | )
11 |
--------------------------------------------------------------------------------
/docs/source-api/src/androidMain/kotlin/ireader/core/http/HttpModule.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * Android HTTP module providing network components
5 | * Note: This is a placeholder. HttpClients should be created in DomainModule
6 | */
7 | actual val httpModule: Any = Unit
8 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/books_dto/BookListDTO.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.books_dto
2 |
3 | data class BookListDTO(
4 | val index: Int,
5 | val limit: Int,
6 | val results: List,
7 | val size: Int,
8 | val total: Int
9 | )
10 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/chapter_dto/ChapterDTO.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.chapter_dto
2 |
3 | data class ChapterDTO(
4 | val limit: Int,
5 | val results: List,
6 | val size: Int,
7 | val start: Int,
8 | val total: Int
9 | )
10 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/CachedLatestChapter.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class CachedLatestChapter(
4 | val chapter_index: Int,
5 | val chapter_name: String,
6 | val slug: String,
7 | val updated_at: String
8 | )
9 |
--------------------------------------------------------------------------------
/sources/en/lnmtl/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "LnMtl",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/wnmtl/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "Wnmtl",
4 | versionCode = 6,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/wuxiaworld/main/src/ireader/wuxiaworld/PopularDTO.kt:
--------------------------------------------------------------------------------
1 | package ireader.wuxiaworld
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class PopularDTO(
7 | val items: List
- ,
8 | val result: Boolean,
9 | val total: Int
10 | )
11 |
--------------------------------------------------------------------------------
/sources/fa/uptvs/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("fa").map { lang ->
2 | Extension(
3 | name = "Uptvs",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Apr 23 18:36:03 GMT+03:30 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/sources/ar/riwyat/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("ar").map { lang ->
2 | Extension(
3 | name = "Riwyat",
4 | versionCode = 10,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/ar/sunovels/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("ar").map { lang ->
2 | Extension(
3 | name = "Sunovels",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/cn/aixdzs/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("cn").map { lang ->
2 | Extension(
3 | name = "Aixdzs",
4 | versionCode = 3,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/cn/sexinsex/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("cn").map { lang ->
2 | Extension(
3 | name = "sexinsex",
4 | versionCode = 3,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = true,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/daonovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "DaoNovel",
4 | versionCode = 7,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/fanmtl/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "fanmtl",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/pandanovel/main/src/ireader/pandanovel/chapter/ChapterDTO.kt:
--------------------------------------------------------------------------------
1 | package ireader.pandanovel.chapter
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class ChapterDTO(
7 | val code: Int,
8 | val msg: String,
9 | val data: Data
10 | )
11 |
--------------------------------------------------------------------------------
/sources/en/pawread/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "Pawread",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/ranobes/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "Ranobes",
4 | versionCode = 9,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/readmtl/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "ReadMtl",
4 | versionCode = 3,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/fr/chireads/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("fr").map { lang ->
2 | Extension(
3 | name = "Chireads",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/ar/realmnovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("ar").map { lang ->
2 | Extension(
3 | name = "Realmnovel",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/comrademao/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "Comrademao",
4 | versionCode = 8,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/coolnovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "CoolNovel",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/fastnovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "FastNovel",
4 | versionCode = 4,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/chapter_dto/Result.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.chapter_dto
2 |
3 | data class Result(
4 | val chapter_index: Int,
5 | val chapter_name: String,
6 | val id: Int,
7 | val slug: String,
8 | val updated_at: String
9 | )
10 |
--------------------------------------------------------------------------------
/sources/en/mostnovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "MostNovel",
4 | versionCode = 3,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/mtlnation/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "MtlNation",
4 | versionCode = 10,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/novelfull/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "NovelFull",
4 | versionCode = 9,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/novelhall/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "NovelHall",
4 | versionCode = 6,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/novelstic/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "NovelStic",
4 | versionCode = 3,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/pandanovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "PandaNovel",
4 | versionCode = 5,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/royalroad/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "RoyalRoad",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/wuxiaworld/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "Wuxiaworld",
4 | versionCode = 4,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/tu/epiknovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("tu").map { lang ->
2 | Extension(
3 | name = "EpikNovel",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/test-extensions/src/main/java/ireader/app/Constatns.kt:
--------------------------------------------------------------------------------
1 | package ireader.app
2 |
3 | import ireader.constants.Constants
4 |
5 |
6 | val BOOK_URL = Constants.bookUrl
7 | val BOOK_NAME = Constants.bookName
8 | val CHAPTER_NAME = Constants.chapterName
9 | val CHAPTER_URL = Constants.chapterName
10 |
--------------------------------------------------------------------------------
/sources/ar/rewayatfans/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("ar").map { lang ->
2 | Extension(
3 | name = "Rewayatfans",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/boxnovelcom/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "BoxNovelCom",
4 | versionCode = 5,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/fictionzone/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "FictionZone",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/freewebnovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "FreeWebNovel",
4 | versionCode = 12,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/kissnovellove/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "KissNovelLove",
4 | versionCode = 4,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/koreanonline/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "KoreanOnline",
4 | versionCode = 8,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "LightNovelsMe",
4 | versionCode = 6,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/mylovenovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "MyLoveNovel",
4 | versionCode = 9,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/novelfullme/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "NovelFullMe",
4 | versionCode = 3,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/novelowlcom/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "NovelOwlCom",
4 | versionCode = 4,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/novelscafes/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "NovelsCafes",
4 | versionCode = 3,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/novelsemperor/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "NovelsEmperor",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/noveltop1net/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "NovelTop1Net",
4 | versionCode = 3,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/novelupdates/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "NovelUpdates",
4 | versionCode = 4,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/ranobes/main/src/ireader/ranobes/Chapter.kt:
--------------------------------------------------------------------------------
1 | package ireader.ranobes
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 |
6 | @Serializable
7 | data class Chapter(
8 | val date: String,
9 | val id: String,
10 | val showDate: String,
11 | val title: String
12 | )
13 |
--------------------------------------------------------------------------------
/sources/en/realwebnovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "RealWebNovel",
4 | versionCode = 8,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/reaperscans/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "ReaperScans",
4 | versionCode = 4,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/scribblehub/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "ScribbleHub",
4 | versionCode = 4,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/skynovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "SkyNovel",
4 | versionCode = 6,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 |
10 | icon = DEFAULT_ICON,
11 | )
12 | }.also(::register)
13 |
--------------------------------------------------------------------------------
/sources/en/webnovelcom/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "WebNovelCom",
4 | versionCode = 8,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/webnovelsite/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "WebNovelSite",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/fr/noveldeglace/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("fr").map { lang ->
2 | Extension(
3 | name = "NovelDeGlace",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/in/indowebnovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("in").map { lang ->
2 | Extension(
3 | name = "IndoWebNovel",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/allnovelfull/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "AllNovelFull",
4 | versionCode = 3,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 |
11 | )
12 | }.also(::register)
13 |
--------------------------------------------------------------------------------
/sources/en/wnmtl/main/src/ireader/wnmtl/chapter/Data.kt:
--------------------------------------------------------------------------------
1 | package ireader.wnmtl.chapter
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Data(
7 | val list: List,
8 | val pageSize: Int,
9 | val totalCount: Int,
10 | val totalPages: Int
11 | )
12 |
--------------------------------------------------------------------------------
/sources/en/wnmtl/main/src/ireader/wnmtl/explore/Data.kt:
--------------------------------------------------------------------------------
1 | package ireader.wnmtl.explore
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Data(
7 | val list: List,
8 | val pageSize: Int,
9 | val totalCount: Int,
10 | val totalPages: Int
11 | )
12 |
--------------------------------------------------------------------------------
/sources/en/wuxiaworldsite/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "WuxiaWorldSite",
4 | versionCode = 11,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/genesistranslator/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "GenesisTranslator",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/lightnovelreader/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "LightNovelReader",
4 | versionCode = 5,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/books_dto/Result.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.books_dto
2 |
3 | data class Result(
4 | val chapter_name: String,
5 | val chapter_slug: String,
6 | val novel_image: String,
7 | val novel_name: String,
8 | val novel_slug: String
9 | )
10 |
--------------------------------------------------------------------------------
/sources/en/mylovenovel/main/src/ireader/mylovenovel/merge.kt:
--------------------------------------------------------------------------------
1 | package ireader.mylovenovel
2 |
3 | fun merge(first: List, second: List): List {
4 | return object : ArrayList() {
5 | init {
6 | addAll(first)
7 | addAll(second)
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/sources/en/qidianundergrond/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "QidianUndergrond",
4 | versionCode = 5,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/wnmtl/main/src/ireader/Constants.kt:
--------------------------------------------------------------------------------
1 | package ireader
2 |
3 | const val _BOOK_URL = "https://novel4up.com/novel/soul-land-5/"
4 | const val _BOOK_NAME = "The Legend of the Dragon King"
5 | const val _CHAPTER_NAME = "Awakening Day"
6 | const val _CHAPTER_URL = "https://wnmtl.org/chapter/165721-awakening-day"
7 |
--------------------------------------------------------------------------------
/sources/en/wuxiaworldsiteco/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "WuxiaWorldSiteco",
4 | versionCode = 8,
5 | libVersion = "1.0",
6 | lang = lang,
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/docs/source-api/src/jsMain/kotlin/ireader/core/http/HttpModule.js.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * JavaScript HTTP module.
5 | *
6 | * In JS context, we don't use dependency injection modules like Koin.
7 | * This is a placeholder that returns Unit.
8 | */
9 | actual val httpModule: Any = Unit
10 |
--------------------------------------------------------------------------------
/sources/en/libread/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "LibRead",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Novel source based on libread.com",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/source/model/DeepLink.kt:
--------------------------------------------------------------------------------
1 |
2 |
3 | package ireader.core.source.model
4 |
5 | sealed class DeepLink {
6 | abstract val key: String
7 |
8 | data class Manga(override val key: String) : DeepLink()
9 | data class Chapter(override val key: String) : DeepLink()
10 | }
11 |
--------------------------------------------------------------------------------
/sources/en/dreambigtl/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "DreamBigTL",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Novel source based on dreambigtl.com",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/qidianundergrond/main/src/ireader/qidianundergrond/QUGroupItem.kt:
--------------------------------------------------------------------------------
1 | package ireader.qidianundergrond
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class QUGroupItem(
7 | val ID: String,
8 | val LastUpdated: Int,
9 | val Name: String,
10 | val Status: String
11 | )
12 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/content_dto/Novel.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.content_dto
2 |
3 | data class Novel(
4 | val genre_name: String,
5 | val genre_slug: String,
6 | val novel_id: Int,
7 | val novel_image: String,
8 | val novel_name: String,
9 | val novel_slug: String
10 | )
11 |
--------------------------------------------------------------------------------
/sources/en/novelonline/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "NovelOnline",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Novel source based on novelsonline.net",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/pandanovel/main/src/ireader/pandanovel/chapter/Data.kt:
--------------------------------------------------------------------------------
1 | package ireader.pandanovel.chapter
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Data(
7 | val selectList: List,
8 | val pages: Int,
9 | val list: List,
10 | val selectNumList: List
11 | )
12 |
--------------------------------------------------------------------------------
/sources/multisrc/skynovel/skynovel/src/ireader/skynovel/SkyNovel.kt:
--------------------------------------------------------------------------------
1 | package ireader.skynovel
2 |
3 | import ireader.core.source.Dependencies
4 | import ireader.skynovelmodel.SkyNovelModel
5 | import tachiyomix.annotations.Extension
6 |
7 |
8 | @Extension
9 | abstract class SkyNovel(val deps: Dependencies) : SkyNovelModel(deps,)
10 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/source/Dependencies.kt:
--------------------------------------------------------------------------------
1 |
2 |
3 | package ireader.core.source
4 |
5 | import ireader.core.http.HttpClientsInterface
6 | import ireader.core.prefs.PreferenceStore
7 |
8 | class Dependencies(
9 | val httpClients: HttpClientsInterface,
10 | val preferences: PreferenceStore
11 | )
12 |
--------------------------------------------------------------------------------
/sources/en/storyseedling/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "StorySeedling",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Novel source based on storyseedling.com",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/docs/source-api/src/desktopMain/kotlin/ireader/core/http/HttpModule.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | import ireader.core.prefs.PreferenceStore
4 |
5 | /**
6 | * Desktop HTTP module providing network components
7 | * Note: This is a placeholder. HttpClients should be created in DomainModule
8 | */
9 | actual val httpModule: Any = Unit
10 |
--------------------------------------------------------------------------------
/sources/en/bestlightnovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "BestLightNovel",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Novel source based on bestlightnovel.com",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/source/DeepLinkSource.kt:
--------------------------------------------------------------------------------
1 |
2 |
3 | package ireader.core.source
4 |
5 | import ireader.core.source.model.DeepLink
6 |
7 | interface DeepLinkSource : ireader.core.source.Source {
8 |
9 | fun handleLink(url: String): DeepLink?
10 |
11 | fun findMangaKey(chapterKey: String): String?
12 | }
13 |
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/main/src/ireader/mtlnovelmodel/Result.kt:
--------------------------------------------------------------------------------
1 | package ireader.mtlnovelmodel
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Result(
7 | val cn: String,
8 | val permalink: String,
9 | val shortname: String,
10 | val thumbnail: String,
11 | val title: String,
12 | )
13 |
--------------------------------------------------------------------------------
/annotations/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("multiplatform")
3 | }
4 |
5 | kotlin {
6 | jvm()
7 |
8 | js(IR) {
9 | browser()
10 | nodejs()
11 | }
12 |
13 | sourceSets {
14 | commonMain.dependencies {
15 | // No dependencies needed for annotations
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/sources/tu/novelgecesi/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("tu").map { lang ->
2 | Extension(
3 | name = "NovelGecesi",
4 | versionCode = 1,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Türkçe novel okuma sitesi",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/content_dto/ContentDTO.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.content_dto
2 |
3 | data class ContentDTO(
4 | val buildId: String,
5 | val gssp: Boolean,
6 | val isFallback: Boolean,
7 | val page: String,
8 | val props: Props,
9 | val query: Query,
10 | val runtimeConfig: RuntimeConfig
11 | )
12 |
--------------------------------------------------------------------------------
/docs/example-annotated/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "ExampleSource",
4 | versionCode = 1,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Example source demonstrating KSP annotations",
8 | nsfw = false,
9 | icon = DEFAULT_ICON
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/wuxiaclick/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "WuxiaClick",
4 | versionCode = 1,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Read Wuxia, Light and Korean Novels at WuxiaClick",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/freenovel/src/ireader/freenovel/Freenovel.kt:
--------------------------------------------------------------------------------
1 | package ireader.freenovel
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 📖 FreeNovel - Zero-code Madara source
7 | */
8 | @MadaraSource(
9 | name = "FreeNovel",
10 | baseUrl = "https://freenovel.me",
11 | lang = "en",
12 | id = 59
13 | )
14 | object FreeNovelConfig
15 |
--------------------------------------------------------------------------------
/defaultRes/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/multisrc/src/main/java/ireader/utility/TestConstants.kt:
--------------------------------------------------------------------------------
1 | package ireader.utility
2 |
3 | import ireader.core.source.Dependencies
4 | import ireader.core.source.HttpSource
5 |
6 | interface TestConstants {
7 | val bookUrl:String
8 | val bookName:String
9 | val chapterUrl:String
10 | val chapterName:String
11 | fun getExtension(deps: Dependencies): HttpSource
12 | }
--------------------------------------------------------------------------------
/sources/en/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "ReadNovelFullPlugin",
4 | versionCode = 11,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Read novels from ",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | assetsDir = "en//main/assets",
11 | )
12 | }.also(::register)
13 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/meionovel/src/ireader/meionovel/MeioNovel.kt:
--------------------------------------------------------------------------------
1 | package ireader.meionovel
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 📖 MeioNovel - Zero-code Indonesian Madara source
7 | */
8 | @MadaraSource(
9 | name = "MeioNovel",
10 | baseUrl = "https://meionovel.id",
11 | lang = "in",
12 | id = 64
13 | )
14 | object MeioNovelConfig
15 |
--------------------------------------------------------------------------------
/sources/en/mtlnation/main/src/ireader/mtlnation/Data.kt:
--------------------------------------------------------------------------------
1 | package ireader.mtlnation
2 |
3 | data class Data(
4 | val content: String,
5 | val created_at: String,
6 | val id: Int,
7 | val index: Int,
8 | val novel: String?,
9 | val novel_id: Int,
10 | val slug: String,
11 | val title: String,
12 | val updated_at: String,
13 | val word_count: Int
14 | )
15 |
--------------------------------------------------------------------------------
/.github/workflows/cancel_pull_request.yml:
--------------------------------------------------------------------------------
1 | name: Cancel old pull request workflows
2 |
3 | on:
4 | workflow_run:
5 | workflows: ["PR build check"]
6 | types:
7 | - requested
8 |
9 | jobs:
10 | cancel:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: styfle/cancel-workflow-action@0.9.1
14 | with:
15 | workflow_id: ${{ github.event.workflow.id }}
16 |
--------------------------------------------------------------------------------
/sources/en/fenrir/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "Fenrir",
4 | versionCode = 11,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Read novels from Fenrir Realm",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | assetsDir = "en/fenrir/main/assets",
11 | )
12 | }.also(::register)
13 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/NovelDetail.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class NovelDetail(
4 | val buildId: String,
5 | val gsp: Boolean,
6 | val isFallback: Boolean,
7 | val nextExport: Boolean,
8 | val page: String,
9 | val props: Props,
10 | val query: Query,
11 | val runtimeConfig: RuntimeConfig
12 | )
13 |
--------------------------------------------------------------------------------
/sources/en/novelfire/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "Novelfire",
4 | versionCode = 11,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Read novels from Novel Fire",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | assetsDir = "en/novelfire/main/assets",
11 | )
12 | }.also(::register)
13 |
--------------------------------------------------------------------------------
/sources/en/mydramanovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "MyDramaNovel",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Translating Tomorrow's TV Drama Hits, Straight from the Pages of Chinese Novels",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | )
11 | }.also(::register)
12 |
--------------------------------------------------------------------------------
/sources/en/novelbuddy/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "Novelbuddy",
4 | versionCode = 11,
5 | libVersion = "2",
6 | lang = lang,
7 | description = "Read novels from NovelBuddy.io",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | assetsDir = "en/novelbuddy/main/assets",
11 | )
12 | }.also(::register)
13 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/webnovelover/src/ireader/webnovelover/WebNovelLover.kt:
--------------------------------------------------------------------------------
1 | package ireader.webnovelover
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 💕 WebNovelLover - Zero-code Madara source
7 | */
8 | @MadaraSource(
9 | name = "WebNovelLover",
10 | baseUrl = "https://www.webnovelover.com",
11 | lang = "en",
12 | id = 66
13 | )
14 | object WebNovelLoverConfig
15 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/firstkissnovel/src/ireader/firstkissnovel/FirstKissNovel.kt:
--------------------------------------------------------------------------------
1 | package ireader.firstkissnovel
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 💋 FirstKissNovel - Zero-code Madara source
7 | */
8 | @MadaraSource(
9 | name = "FirstKissNovel",
10 | baseUrl = "https://1stkissnovel.love",
11 | lang = "en",
12 | id = 57
13 | )
14 | object FirstKissNovelConfig
15 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/noveltranslate/src/ireader/noveltranslate/NovelTranslate.kt:
--------------------------------------------------------------------------------
1 | package ireader.noveltranslate
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 📝 NovelTranslate - Zero-code Madara source
7 | */
8 | @MadaraSource(
9 | name = "NovelTranslate",
10 | baseUrl = "https://noveltranslate.com",
11 | lang = "en",
12 | id = 55
13 | )
14 | object NovelTranslateConfig
15 |
--------------------------------------------------------------------------------
/sources/multisrc/readwn/novelmt/src/ireader/novelmt/Readwn.kt:
--------------------------------------------------------------------------------
1 | package ireader.readwn
2 |
3 |
4 | import ireader.core.source.Dependencies
5 | import tachiyomix.annotations.Extension
6 |
7 | @Extension
8 | abstract class Readwn(val deps: Dependencies) : ReaderWnScraper(
9 | deps,
10 | key = "https://www.novelmt.com",
11 | sourceName = "Novelmt",
12 | sourceId = 61,
13 | language = "en",
14 | )
15 |
--------------------------------------------------------------------------------
/sources/multisrc/readwn/readwn/src/ireader/readwn/Readwn.kt:
--------------------------------------------------------------------------------
1 | package ireader.readwn
2 |
3 |
4 | import ireader.core.source.Dependencies
5 | import tachiyomix.annotations.Extension
6 |
7 | @Extension
8 | abstract class Readwn(val deps: Dependencies) : ReaderWnScraper(
9 | deps,
10 | key = "https://www.readwn.com",
11 | sourceName = "Readwn.com",
12 | sourceId = 60,
13 | language = "en",
14 | )
15 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/armtl/src/ireader/armtl/ArMtl.kt:
--------------------------------------------------------------------------------
1 | package ireader.armtl
2 |
3 | import ireader.madara.Madara
4 | import ireader.core.source.Dependencies
5 |
6 | import tachiyomix.annotations.Extension
7 |
8 | @Extension
9 | abstract class ArMtl(val deps: Dependencies) : Madara(
10 | deps,
11 | key = "https://ar-mtl.club",
12 | sourceName = "ArMtl",
13 | sourceId = 46,
14 | language = "ar"
15 | )
16 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/util/TimeUtils.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.util
2 |
3 | import kotlin.time.ExperimentalTime
4 |
5 | /**
6 | * Get current time in milliseconds (epoch).
7 | * This is a KMP-compatible replacement for System.currentTimeMillis()
8 | */
9 | @OptIn(ExperimentalTime::class)
10 | fun currentTimeMillis(): Long {
11 | return kotlin.time.Clock.System.now().toEpochMilliseconds()
12 | }
13 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/novelmultiverse/src/ireader/novelmultiverse/NovelMultiverse.kt:
--------------------------------------------------------------------------------
1 | package ireader.novelmultiverse
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 🌌 NovelMultiverse - Zero-code Madara source
7 | */
8 | @MadaraSource(
9 | name = "NovelMultiverse",
10 | baseUrl = "https://www.novelmultiverse.com",
11 | lang = "en",
12 | id = 83
13 | )
14 | object NovelMultiverseConfig
15 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/PageProps.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class PageProps(
4 | val authors: List,
5 | val cachedChapterResults: String,
6 | val cachedHotNovels: List,
7 | val cachedLatestChapters: List,
8 | val genres: List,
9 | val novelInfo: NovelInfo,
10 | val tags: List
11 | )
12 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/morenovel/src/ireader/morenovel/MoreNovel.kt:
--------------------------------------------------------------------------------
1 | package ireader.morenovel
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 📚 MoreNovel - Zero-code Madara source!
7 | *
8 | * Indonesian novel site using Madara theme.
9 | */
10 | @MadaraSource(
11 | name = "MoreNovel",
12 | baseUrl = "https://morenovel.net",
13 | lang = "in",
14 | id = 73
15 | )
16 | object MoreNovelConfig
17 |
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/mtlnoveles/src/ireader/mtlnoveles/MtlNovelEs.kt:
--------------------------------------------------------------------------------
1 | package ireader.mtlnoveles
2 |
3 | import ireader.core.source.Dependencies
4 | import ireader.mtlnovelmodel.MtlNovelModel
5 | import tachiyomix.annotations.Extension
6 |
7 |
8 | @Extension
9 | abstract class MtlNovelEs(val deps: Dependencies) : MtlNovelModel(deps,) {
10 | override val baseUrl: String
11 | get() = "https://es.mtlnovel.com"
12 | }
13 |
--------------------------------------------------------------------------------
/docs/source-api/src/androidMain/kotlin/ireader/core/http/OkHttpExtension.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | import io.ktor.client.HttpClient
4 | import io.ktor.client.engine.okhttp.OkHttpEngine
5 | import okhttp3.OkHttpClient
6 |
7 | /**
8 | * Extension to get OkHttpClient from Ktor HttpClient on Android.
9 | */
10 | val HttpClient.okhttp: OkHttpClient
11 | get() = (engine as? OkHttpEngine)?.config?.preconfigured ?: OkHttpClient()
12 |
--------------------------------------------------------------------------------
/docs/source-api/src/desktopMain/kotlin/ireader/core/http/OkHttpExtension.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | import io.ktor.client.HttpClient
4 | import io.ktor.client.engine.okhttp.OkHttpEngine
5 | import okhttp3.OkHttpClient
6 |
7 | /**
8 | * Extension to get OkHttpClient from Ktor HttpClient on Desktop.
9 | */
10 | val HttpClient.okhttp: OkHttpClient
11 | get() = (engine as? OkHttpEngine)?.config?.preconfigured ?: OkHttpClient()
12 |
--------------------------------------------------------------------------------
/sources/en/lnmtl/main/src/ireader/lnmtl/volume/LNMTLVolumnResponseItem.kt:
--------------------------------------------------------------------------------
1 | package ireader.lnmtl.volume
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class LNMTLVolumnResponseItem(
7 | val created_at: String?,
8 | val id: Int,
9 | val is_fake: Int?,
10 | val novel_id: Int?,
11 | val number: Int?,
12 | val slug: String?,
13 | val title: String?,
14 | val updated_at: String?
15 | )
--------------------------------------------------------------------------------
/sources/multisrc/madara/hizomanga/src/ireader/hizomang/HizoManga.kt:
--------------------------------------------------------------------------------
1 | package ireader.hizomanga
2 |
3 | import ireader.madara.Madara
4 | import ireader.core.source.Dependencies
5 |
6 | import tachiyomix.annotations.Extension
7 |
8 | @Extension
9 | abstract class HizoManga(val deps: Dependencies) : Madara(
10 | deps,
11 | key = "https://hizomanga.net",
12 | sourceName = "HizoManga",
13 | sourceId = 46,
14 | language = "ar"
15 | )
16 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/sonicmtl/src/ireader/sonicmtl/SonicMTL.kt:
--------------------------------------------------------------------------------
1 | package ireader.sonicmtl
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 🔊 SonicMTL - Zero-code Madara source!
7 | *
8 | * Just define the annotation - KSP generates everything else.
9 | */
10 | @MadaraSource(
11 | name = "SonicMTL",
12 | baseUrl = "https://www.sonicmtl.com",
13 | lang = "en",
14 | id = 77
15 | )
16 | object SonicMTLConfig
17 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/clicknovel/src/ireader/clicknovel/ClickNovel.kt:
--------------------------------------------------------------------------------
1 | package ireader.clicknovel
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 📖 ClickNovel - Zero-code Madara source!
7 | *
8 | * Just define the annotation - KSP generates everything else.
9 | */
10 | @MadaraSource(
11 | name = "ClickNovel",
12 | baseUrl = "https://clicknovel.net",
13 | lang = "en",
14 | id = 68
15 | )
16 | object ClickNovelConfig
17 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/cratenovel/src/ireader/cratenovel/CrateNovel.kt:
--------------------------------------------------------------------------------
1 | package ireader.cratenovel
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 📦 CrateNovel - Zero-code Madara source!
7 | *
8 | * Just define the annotation - KSP generates everything else.
9 | */
10 | @MadaraSource(
11 | name = "CrateNovel",
12 | baseUrl = "https://cratenovel.com",
13 | lang = "en",
14 | id = 67
15 | )
16 | object CrateNovelConfig
17 |
--------------------------------------------------------------------------------
/sources/multisrc/readwn/ltnovel/src/ireader/ltnovel/Ltnovel.kt:
--------------------------------------------------------------------------------
1 | package ireader.ltnovel
2 |
3 |
4 | import ireader.core.source.Dependencies
5 | import ireader.readwn.ReaderWnScraper
6 | import tachiyomix.annotations.Extension
7 |
8 | @Extension
9 | abstract class Ltnovel(val deps: Dependencies) : ReaderWnScraper(
10 | deps,
11 | key = "https://www.ltnovel.com",
12 | sourceName = "Ltnovel",
13 | sourceId = 62,
14 | language = "en",
15 | )
16 |
--------------------------------------------------------------------------------
/sources/en/lnmtl/main/src/ireader/lnmtl/chapters/LNMTLResponse.kt:
--------------------------------------------------------------------------------
1 | package ireader.lnmtl.chapters
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class LNMTLResponse(
7 | val current_page: Int?,
8 | val `data`: List,
9 | val from: Int?,
10 | val last_page: Int,
11 | val next_page_url: String?,
12 | val per_page: Int?,
13 | val prev_page_url: String?,
14 | val to: Int?,
15 | val total: Int?
16 | )
--------------------------------------------------------------------------------
/sources/multisrc/madara/novel4up/src/ireader/novel4up/Novel4Up.kt:
--------------------------------------------------------------------------------
1 | package ireader.novel4up
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 📖 Novel4Up (Madara) - Zero-code Arabic Madara source!
7 | *
8 | * Just define the annotation - KSP generates everything else.
9 | */
10 | @MadaraSource(
11 | name = "Novel4Up",
12 | baseUrl = "https://novel4up.com",
13 | lang = "ar",
14 | id = 74
15 | )
16 | object Novel4UpMadaraConfig
17 |
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/mtlnovelen/src/ireader/mtlnovelen/MtlNovelEn.kt:
--------------------------------------------------------------------------------
1 | package ireader.mtlnovelen
2 |
3 | import ireader.core.source.Dependencies
4 | import ireader.mtlnovelmodel.MtlNovelModel
5 | import tachiyomix.annotations.Extension
6 |
7 |
8 | @Extension
9 | abstract class MtlNovelEn(val deps: Dependencies) : MtlNovelModel(deps,) {
10 | override val baseUrl: String
11 | get() = "https://www.mtlnovel.com"
12 | override val lang = "en"
13 | }
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 |
9 | [*.{kt,kts}]
10 | indent_size = 4
11 | indent_style = space
12 | max_line_length = 120
13 | ij_kotlin_allow_trailing_comma = true
14 | ij_kotlin_allow_trailing_comma_on_call_site = true
15 |
16 | [*.{json,yml,yaml}]
17 | indent_size = 2
18 | indent_style = space
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/mtlnovelclub/src/ireader/mtlnovelclub/MTLNovelClub.kt:
--------------------------------------------------------------------------------
1 | package ireader.mtlnovelclub
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 📖 MTLNovelClub - Zero-code Madara source!
7 | *
8 | * Just define the annotation - KSP generates everything else.
9 | */
10 | @MadaraSource(
11 | name = "MTLNovelClub",
12 | baseUrl = "https://mtlnovel.club",
13 | lang = "en",
14 | id = 78
15 | )
16 | object MTLNovelClubConfig
17 |
--------------------------------------------------------------------------------
/docs/source-api/src/androidMain/kotlin/ireader/core/util/createCoroutineScope.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.util
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlin.coroutines.CoroutineContext
7 |
8 | actual fun createCoroutineScope(coroutineContext: CoroutineContext): CoroutineScope = CoroutineScope(coroutineContext)
9 | actual val DefaultDispatcher: CoroutineDispatcher = Dispatchers.Main
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/content_dto/CachedChapterInfo.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.content_dto
2 |
3 | data class CachedChapterInfo(
4 | val chapter_id: Int,
5 | val chapter_index: Int,
6 | val chapter_name: String,
7 | val chapter_slug: String,
8 | val chapter_source_id: Int,
9 | val content: String,
10 | val novel: Novel,
11 | val novel_id: Int,
12 | val novel_source_id: Int,
13 | val site_id: Int
14 | )
15 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/zinnovel/src/ireader/zinnovel/Zinnovel.kt:
--------------------------------------------------------------------------------
1 | package ireader.zinnovel
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 📖 Zinnovel - Zero-code Madara source with custom paths
7 | */
8 | @MadaraSource(
9 | name = "Zinnovel",
10 | baseUrl = "https://zinnovel.com",
11 | lang = "en",
12 | id = 54,
13 | novelsPath = "manga",
14 | novelPath = "manga",
15 | chapterPath = "novel"
16 | )
17 | object ZinnovelConfig
18 |
--------------------------------------------------------------------------------
/annotations/src/main/kotlin/Extension.kt:
--------------------------------------------------------------------------------
1 | package tachiyomix.annotations
2 | /*
3 | Copyright (C) 2018 The Tachiyomi Open Source Project
4 |
5 | This Source Code Form is subject to the terms of the Mozilla Public
6 | License, v. 2.0. If a copy of the MPL was not distributed with this
7 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 | */
9 |
10 | @Retention(AnnotationRetention.SOURCE)
11 | @Target(AnnotationTarget.CLASS)
12 | annotation class Extension
13 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/detail_dto/NovelInfo.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.detail_dto
2 |
3 | data class NovelInfo(
4 | val created_at: String,
5 | val novel_alternatives: String,
6 | val novel_description: String,
7 | val novel_id: Int,
8 | val novel_image: String,
9 | val novel_name: String,
10 | val novel_score: Double,
11 | val novel_slug: String,
12 | val novel_status: String,
13 | val updated_at: String
14 | )
15 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/readwebnovels/src/ireader/readwebnovels/ReadWebNovels.kt:
--------------------------------------------------------------------------------
1 | package ireader.readwebnovels
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 📚 ReadWebNovels - Zero-code Madara source!
7 | *
8 | * Just define the annotation - KSP generates everything else.
9 | */
10 | @MadaraSource(
11 | name = "ReadWebNovels",
12 | baseUrl = "https://readwebnovels.net",
13 | lang = "en",
14 | id = 69
15 | )
16 | object ReadWebNovelsConfig
17 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/mysticalseries/src/ireader/mysticalseries/MysticalSeries.kt:
--------------------------------------------------------------------------------
1 | package ireader.mysticalseries
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * ✨ MysticalSeries - Zero-code Madara source!
7 | *
8 | * Just define the annotation - KSP generates everything else.
9 | */
10 | @MadaraSource(
11 | name = "MysticalSeries",
12 | baseUrl = "https://mysticalseries.com",
13 | lang = "en",
14 | id = 71
15 | )
16 | object MysticalSeriesConfig
17 |
--------------------------------------------------------------------------------
/test-extensions/src/test/java/ireader/app/Cosntants.kt:
--------------------------------------------------------------------------------
1 | package ireader.app
2 |
3 | import ireader.constants.Constants
4 | import ireader.app.mockcomponents.FakeHttpClients
5 | import ireader.app.mockcomponents.FakePreferencesStore
6 | import ireader.core.source.Dependencies
7 | import ireader.core.source.HttpSource
8 |
9 | val dependencies = Dependencies(
10 | FakeHttpClients(),
11 | FakePreferencesStore()
12 | )
13 | val extension: HttpSource = Constants.getExtension(deps = dependencies)
14 |
--------------------------------------------------------------------------------
/sources/en/wnmtl/main/src/ireader/wnmtl/chapter/Result.kt:
--------------------------------------------------------------------------------
1 | package ireader.wnmtl.chapter
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Result(
7 | val balance: Int,
8 | val bookId: Int,
9 | val chapterOrder: Int,
10 | val createTime: Long,
11 | val id: Int,
12 | val paid: Boolean,
13 | val paywallStatus: String,
14 | val status: String,
15 | val title: String,
16 | val updateTime: Long,
17 | val wordCounts: Int
18 | )
19 |
--------------------------------------------------------------------------------
/docs/example-autoid/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf("en").map { lang ->
2 | Extension(
3 | name = "ExampleAutoId",
4 | versionCode = 1,
5 | libVersion = "1",
6 | lang = lang,
7 | description = "Example source demonstrating @AutoSourceId usage",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | // Note: sourceId is auto-generated! No need to specify manually.
11 | // The ID comes from: hash("exampleautoid/en/1")
12 | )
13 | }.also(::register)
14 |
--------------------------------------------------------------------------------
/sources/en/fenrir/README.md:
--------------------------------------------------------------------------------
1 | # Fenrir Realm
2 |
3 | **Generated by Converter V5 AI**
4 |
5 | ## Information
6 | - **Source**: https://fenrirealm.com
7 | - **Version**: 1.0.12
8 | - **Type**: SCRAPING
9 | - **Generator**: Full AI-Powered
10 |
11 | ## Status
12 | ✅ Generated with Gemini AI
13 | ✅ Complete implementation
14 | ⚠️ Needs testing
15 |
16 | ## Notes
17 | This extension was fully generated by AI based on the TypeScript source code.
18 | The AI analyzed the code and generated working Kotlin implementations.
19 |
--------------------------------------------------------------------------------
/sources/en/lightnovelpub/webnovelpub/src/WebNovelPub.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovelpub
2 |
3 | import ireader.core.source.Dependencies
4 | import tachiyomix.annotations.Extension
5 |
6 | @Extension
7 | abstract class WebNovelPub(private val deps: Dependencies) : LightNovelPub(deps) {
8 |
9 | override val name: String
10 | get() = "WebNovelPub"
11 |
12 | override val id: Long
13 | get() = 48
14 |
15 | override val baseUrl: String
16 | get() = "https://www.webnovelpub.com/"
17 | }
18 |
--------------------------------------------------------------------------------
/sources/en/novelfire/README.md:
--------------------------------------------------------------------------------
1 | # Novel Fire
2 |
3 | **Generated by Converter V5 AI**
4 |
5 | ## Information
6 | - **Source**: https://novelfire.net/
7 | - **Version**: 1.0.4
8 | - **Type**: SCRAPING
9 | - **Generator**: Full AI-Powered
10 |
11 | ## Status
12 | ✅ Generated with Gemini AI
13 | ✅ Complete implementation
14 | ⚠️ Needs testing
15 |
16 | ## Notes
17 | This extension was fully generated by AI based on the TypeScript source code.
18 | The AI analyzed the code and generated working Kotlin implementations.
19 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/lunarletters/src/ireader/lunarletters/LunarLetters.kt:
--------------------------------------------------------------------------------
1 | package ireader.lunarletters
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 🌙 LunarLetters - Zero-code Madara source with custom paths
7 | */
8 | @MadaraSource(
9 | name = "LunarLetters",
10 | baseUrl = "https://www.lunarletters.com",
11 | lang = "en",
12 | id = 81,
13 | novelsPath = "series",
14 | novelPath = "series",
15 | chapterPath = "series"
16 | )
17 | object LunarLettersConfig
18 |
--------------------------------------------------------------------------------
/sources/en/novelbuddy/README.md:
--------------------------------------------------------------------------------
1 | # NovelBuddy.io
2 |
3 | **Generated by Converter V5 AI**
4 |
5 | ## Information
6 | - **Source**: https://novelbuddy.io/
7 | - **Version**: 1.0.3
8 | - **Type**: SCRAPING
9 | - **Generator**: Full AI-Powered
10 |
11 | ## Status
12 | ✅ Generated with Gemini AI
13 | ✅ Complete implementation
14 | ⚠️ Needs testing
15 |
16 | ## Notes
17 | This extension was fully generated by AI based on the TypeScript source code.
18 | The AI analyzed the code and generated working Kotlin implementations.
19 |
--------------------------------------------------------------------------------
/sources/en/ranobes/main/src/ireader/ranobes/ChapterDTO.kt:
--------------------------------------------------------------------------------
1 | package ireader.ranobes
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 |
6 | @Serializable
7 | data class ChapterDTO(
8 | val book_id: Int,
9 | val book_link: String,
10 | val book_title: String,
11 | val chapters: List,
12 | val count_all: Int,
13 | val cstart: Int,
14 | val default: List,
15 | val limit: Int,
16 | val pages_count: Int,
17 | val search: String,
18 | val searchTimeout: Long
19 | )
20 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/neosekaitranslations/src/ireader/neosekaitranslations/Constants.kt:
--------------------------------------------------------------------------------
1 | package neosekaitranslations
2 |
3 | const val _BOOK_URL = "https://www.neosekaitranslations.com/novel/i-quit-the-going-home-club-for-a-girl-with-a-venomous-tongue/"
4 | const val _BOOK_NAME = "I Can Level Up By Staying Idle"
5 | const val _CHAPTER_NAME = "Chapter 886 Ancestor Shen Yin, Temporarily Dead"
6 | const val _CHAPTER_URL = "https://www.neosekaitranslations.com/novel/i-quit-the-going-home-club-for-a-girl-with-a-venomous-tongue/chapter-115/"
7 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/neosekaitranslations/src/ireader/neosekaitranslations/Neosekaitranslations.kt:
--------------------------------------------------------------------------------
1 | package ireader.neosekaitranslations
2 |
3 |
4 | import ireader.madara.Madara
5 | import ireader.core.source.Dependencies
6 | import tachiyomix.annotations.Extension
7 |
8 | @Extension
9 | abstract class Neosekaitranslations(val deps: Dependencies) : Madara(
10 | deps,
11 | key = "https://www.neosekaitranslations.com",
12 | sourceName = "Neosekaitranslations",
13 | sourceId = 45,
14 | language = "en"
15 | )
16 |
--------------------------------------------------------------------------------
/docs/source-api/src/iosMain/kotlin/ireader/core/util/CoroutineExt.ios.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.util
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.IO
7 | import kotlin.coroutines.CoroutineContext
8 |
9 | actual fun createCoroutineScope(coroutineContext: CoroutineContext): CoroutineScope {
10 | return CoroutineScope(coroutineContext)
11 | }
12 |
13 | actual val DefaultDispatcher: CoroutineDispatcher = Dispatchers.IO
14 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/lightnovelheaven/src/ireader/lightnovelheaven/LightNovelHeaven.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovelheaven
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 🌟 LightNovelHeaven - Zero-code Madara source with custom paths
7 | */
8 | @MadaraSource(
9 | name = "LightNovelHeaven",
10 | baseUrl = "https://lightnovelheaven.com",
11 | lang = "en",
12 | id = 63,
13 | novelsPath = "series",
14 | novelPath = "series",
15 | chapterPath = "series"
16 | )
17 | object LightNovelHeavenConfig
18 |
--------------------------------------------------------------------------------
/docs/source-api/src/desktopMain/kotlin/ireader/core/util/createCoroutineScope.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.util
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.SupervisorJob
7 | import kotlin.coroutines.CoroutineContext
8 |
9 | actual fun createCoroutineScope(coroutineContext: CoroutineContext): CoroutineScope = CoroutineScope(SupervisorJob() + DefaultDispatcher)
10 | actual val DefaultDispatcher: CoroutineDispatcher = Dispatchers.IO
--------------------------------------------------------------------------------
/sources/multisrc/madara/sleepytranslations/src/ireader/sleepytranslations/SleepyTranslations.kt:
--------------------------------------------------------------------------------
1 | package ireader.sleepytranslations
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 😴 SleepyTranslations - Zero-code Madara source with custom paths
7 | */
8 | @MadaraSource(
9 | name = "SleepyTranslations",
10 | baseUrl = "https://sleepytranslations.com",
11 | lang = "en",
12 | id = 56,
13 | novelsPath = "series",
14 | novelPath = "series",
15 | chapterPath = "series"
16 | )
17 | object SleepyTranslationsConfig
18 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/search_dto/Result.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.search_dto
2 |
3 | data class Result(
4 | val chapter_name: String,
5 | val chapter_slug: String,
6 | val chapter_updated_at: String,
7 | val id: Int,
8 | val latest_chapter_id: Int,
9 | val novel_alternatives: String,
10 | val novel_image: String,
11 | val novel_name: String,
12 | val novel_slug: String,
13 | val novel_updated_at: String,
14 | val score: Int,
15 | val site_id: Int,
16 | val status: String
17 | )
18 |
--------------------------------------------------------------------------------
/sources/en/lightnovels/main/src/ireader/lightnovels/search_dto/ResultX.kt:
--------------------------------------------------------------------------------
1 | package ireader.lightnovels.search_dto
2 |
3 | data class ResultX(
4 | val chapter_name: String,
5 | val chapter_slug: String,
6 | val chapter_updated_at: String,
7 | val id: Int,
8 | val latest_chapter_id: Int,
9 | val novel_alternatives: String,
10 | val novel_image: Any,
11 | val novel_name: String,
12 | val novel_slug: String,
13 | val novel_updated_at: String,
14 | val score: Double,
15 | val site_id: Int,
16 | val status: String
17 | )
18 |
--------------------------------------------------------------------------------
/sources/en/lightnovelpub/build.gradle.kts:
--------------------------------------------------------------------------------
1 | register(
2 | Extension(
3 | name = "LightNovelPub",
4 | versionCode = 4,
5 | libVersion = "2",
6 | lang = "en",
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON
10 | ),
11 | Extension(
12 | name = "WebNovelPub",
13 | versionCode = 2,
14 | libVersion = "2",
15 | lang = "en",
16 | description = "",
17 | nsfw = false,
18 | icon = DEFAULT_ICON,
19 | assetsDir = "en/lightnovelpub/webnovelpub/assets",
20 | sourceDir = "webnovelpub"
21 | )
22 | )
23 |
24 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/util/CoroutineExt.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.util
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.SupervisorJob
6 | import kotlin.coroutines.CoroutineContext
7 |
8 | expect fun createCoroutineScope(coroutineContext: CoroutineContext) : CoroutineScope
9 | expect val DefaultDispatcher : CoroutineDispatcher
10 | fun createICoroutineScope(dispatcher: CoroutineContext = SupervisorJob() + DefaultDispatcher) : CoroutineScope = createCoroutineScope(dispatcher)
--------------------------------------------------------------------------------
/sources/en/pandanovel/main/src/ireader/pandanovel/chapter/Info.kt:
--------------------------------------------------------------------------------
1 | package ireader.pandanovel.chapter
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Info(
7 | val id: Int,
8 | val bookId: Int,
9 | val author: String,
10 | val name: String,
11 | val content: String,
12 | val status: Int,
13 | val goodNum: Int,
14 | val createdAt: String,
15 | val updatedAt: String,
16 | val chapterUrl: String,
17 | val currentChapterCount: String?,
18 | val isEncryption: Int,
19 | val isSort: Int
20 | )
21 |
--------------------------------------------------------------------------------
/docs/source-api/src/androidMain/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/compiler/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2018 The Tachiyomi Open Source Project
3 |
4 | This Source Code Form is subject to the terms of the Mozilla Public
5 | License, v. 2.0. If a copy of the MPL was not distributed with this
6 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 | */
8 |
9 | plugins {
10 | kotlin("jvm")
11 | kotlin("plugin.serialization")
12 | }
13 |
14 | dependencies {
15 | implementation(libs.kotlinpoet)
16 | implementation(libs.ksp.api)
17 | implementation(libs.serialization.json)
18 |
19 | implementation(project(":annotations"))
20 | }
21 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/NetworkConfig.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * Centralized network configuration
5 | */
6 | data class NetworkConfig(
7 | val connectTimeoutSeconds: Long = 30,
8 | val readTimeoutMinutes: Long = 5,
9 | val callTimeoutMinutes: Long = 5,
10 | val cacheSize: Long = 15L * 1024 * 1024, // 15MB
11 | val cacheDurationMs: Long = 5 * 60 * 1000, // 5 minutes
12 | val userAgent: String = DEFAULT_USER_AGENT,
13 | val enableCaching: Boolean = true,
14 | val enableCookies: Boolean = true,
15 | val enableCompression: Boolean = true
16 | )
17 |
--------------------------------------------------------------------------------
/docs/source-api/src/desktopMain/kotlin/ireader/core/http/CookieSynchronizer.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * Desktop implementation of cookie synchronization
5 | * Desktop doesn't have WebView, so this is a no-op implementation
6 | */
7 | actual class CookieSynchronizer {
8 | actual fun syncFromWebView(url: String) {
9 | // No-op: Desktop doesn't have WebView
10 | }
11 |
12 | actual fun syncToWebView(url: String) {
13 | // No-op: Desktop doesn't have WebView
14 | }
15 |
16 | actual fun clearAll() {
17 | // No-op: Desktop doesn't have WebView
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/deeplink/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2018 The Tachiyomi Open Source Project
3 |
4 | This Source Code Form is subject to the terms of the Mozilla Public
5 | License, v. 2.0. If a copy of the MPL was not distributed with this
6 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 | */
8 |
9 |
10 | plugins {
11 | id("com.android.library")
12 | kotlin("android")
13 | }
14 |
15 | android {
16 | compileSdk = Config.compileSdk
17 | namespace = "tachiyomix.deeplink"
18 | defaultConfig {
19 | minSdk = Config.minSdk
20 | }
21 | }
22 |
23 | dependencies {
24 | compileOnly(libs.stdlib)
25 | }
26 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/turkcelightnovels/src/ireader/turkcelightnovels/TurkceLightNovels.kt:
--------------------------------------------------------------------------------
1 | package ireader.turkcelightnovels
2 |
3 | import tachiyomix.annotations.MadaraSource
4 |
5 | /**
6 | * 🇹🇷 TurkceLightNovels - Zero-code Madara source!
7 | *
8 | * Turkish novel site using Madara theme with custom paths.
9 | */
10 | @MadaraSource(
11 | name = "TurkceLightNovels",
12 | baseUrl = "https://turkcelightnovels.com",
13 | lang = "tu",
14 | id = 76,
15 | // Custom paths for this site
16 | novelsPath = "light-novel",
17 | novelPath = "light-novel",
18 | chapterPath = "light-novel"
19 | )
20 | object TurkceLightNovelsConfig
21 |
--------------------------------------------------------------------------------
/docs/source-api/src/jsMain/kotlin/ireader/core/util/CoroutineExt.js.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.util
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.CoroutineScope
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlin.coroutines.CoroutineContext
7 |
8 | /**
9 | * JavaScript implementation of coroutine utilities.
10 | */
11 | actual fun createCoroutineScope(coroutineContext: CoroutineContext): CoroutineScope {
12 | return CoroutineScope(coroutineContext)
13 | }
14 |
15 | /**
16 | * Default dispatcher for JS - uses Dispatchers.Default
17 | */
18 | actual val DefaultDispatcher: CoroutineDispatcher = Dispatchers.Default
19 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/JSFactory.kt:
--------------------------------------------------------------------------------
1 | ///*
2 | // * Copyright (C) 2018 The Tachiyomi Open Source Project
3 | // *
4 | // * This Source Code Form is subject to the terms of the Mozilla Public
5 | // * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | // * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 | // */
8 | //
9 | //package ireader.core.http
10 | //
11 | ///** NOT USED ANYMORE
12 | // * A factory for creating [JS] instances.
13 | // */
14 | //@Suppress("NO_ACTUAL_FOR_EXPECT")
15 | //expect class JSFactory {
16 | //
17 | // /**
18 | // * Returns a new instance of [JS].
19 | // */
20 | // fun create(): JS
21 | //
22 | //}
23 |
--------------------------------------------------------------------------------
/sources/en/wnmtl/main/src/ireader/wnmtl/content/Data.kt:
--------------------------------------------------------------------------------
1 | package ireader.wnmtl.content
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Data(
7 | val balance: Int,
8 | val bookId: Int,
9 | val bookTitle: String,
10 | val chapterOrder: Int,
11 | val content: String,
12 | val createTime: Long,
13 | val id: Int,
14 | val nextId: Int,
15 | val nextTitle: String,
16 | val orgId: Long?,
17 | val paid: Boolean,
18 | val paywallStatus: String,
19 | val preId: Int,
20 | val preTitle: String,
21 | val status: String,
22 | val title: String,
23 | val updateTime: Long,
24 | val wordCounts: Int
25 | )
26 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/RatelimitException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 The Tachiyomi Open Source Project
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 | */
8 |
9 | package ireader.core.http
10 |
11 | @Suppress("unused")
12 | class RatelimitException : Exception {
13 |
14 | constructor() : super()
15 |
16 | constructor(message: String) : super(message)
17 |
18 | constructor(cause: Exception) : super(cause)
19 |
20 | constructor(message: String, cause: Exception) : super(message, cause)
21 | }
22 |
--------------------------------------------------------------------------------
/sources/multisrc/skynovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf(
2 | Extension(
3 | name = "SkyNovel",
4 | versionCode = 6,
5 | libVersion = "2",
6 | lang = "en",
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | assetsDir = "multisrc/skynovel/skynovel/assets",
11 | sourceDir = "skynovel",
12 | ),
13 | Extension(
14 | name = "WbNovel",
15 | versionCode = 2,
16 | libVersion = "2",
17 | lang = "in",
18 | description = "",
19 | nsfw = false,
20 | icon = DEFAULT_ICON,
21 | assetsDir = "multisrc/skynovel/wbnovel/assets",
22 | sourceDir = "wbnovel",
23 | ),
24 | ).also(::register)
25 |
--------------------------------------------------------------------------------
/compiler/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider:
--------------------------------------------------------------------------------
1 | tachiyomix.compiler.SourceFactoryProcessorProvider
2 | tachiyomix.compiler.SourceBoilerplateProcessorProvider
3 | tachiyomix.compiler.SourceIdProcessorProvider
4 | tachiyomix.compiler.ThemeSourceProcessorProvider
5 | tachiyomix.compiler.ExtensionProcessorFactory
6 | tachiyomix.compiler.JsExtensionProcessorFactory
7 | tachiyomix.compiler.SourceIndexProcessorProvider
8 | tachiyomix.compiler.SelectorValidatorProcessorProvider
9 | tachiyomix.compiler.TestGeneratorProcessorProvider
10 | tachiyomix.compiler.HttpClientProcessorProvider
11 | tachiyomix.compiler.DeepLinkProcessorProvider
12 | tachiyomix.compiler.PackageAutoCorrectProcessorProvider
13 |
--------------------------------------------------------------------------------
/.github/scripts/sign-apks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | TOOLS="$(ls -d ${ANDROID_HOME}/build-tools/* | tail -1)"
5 |
6 | shopt -s globstar nullglob extglob
7 | APKS=( **/*".apk" )
8 |
9 | # Take base64 encoded key input and put it into a file
10 | STORE_PATH=$PWD/signingkey.jks
11 | rm -f $STORE_PATH && touch $STORE_PATH
12 | echo $1 | base64 -d > $STORE_PATH
13 |
14 | STORE_ALIAS=$2
15 | export KEY_STORE_PASSWORD=$3
16 | export KEY_PASSWORD=$4
17 |
18 | # Sign all of the APKs
19 | for APK in ${APKS[@]}; do
20 | ${TOOLS}/zipalign -c -v -p 4 $APK
21 | ${TOOLS}/apksigner sign --ks $STORE_PATH --ks-key-alias $STORE_ALIAS --ks-pass env:KEY_STORE_PASSWORD --key-pass env:KEY_PASSWORD $APK
22 | done
23 |
24 | rm $STORE_PATH
25 | unset KEY_STORE_PASSWORD
26 | unset KEY_PASSWORD
--------------------------------------------------------------------------------
/docs/source-api/src/jvmMain/kotlin/ireader/core/http/JSFactory.kt:
--------------------------------------------------------------------------------
1 | ///*
2 | // * Copyright (C) 2018 The Tachiyomi Open Source Project
3 | // *
4 | // * This Source Code Form is subject to the terms of the Mozilla Public
5 | // * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | // * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 | // */
8 | //
9 | //package ireader.core.http
10 | //
11 | //import app.cash.quickjs.QuickJs
12 | //
13 | //
14 | ///**
15 | // * A factory for creating instances of [JS].
16 | // */
17 | //
18 | //actual class JSFactory internal constructor() {
19 | //
20 | // /**
21 | // * Returns a new instance of [JS].
22 | // */
23 | // actual fun create(): JS {
24 | // return JS(QuickJs.create())
25 | // }
26 | //
27 | //}
28 |
--------------------------------------------------------------------------------
/test-extensions/src/test/java/ireader/app/mockcomponents/FakeHttpClients.kt:
--------------------------------------------------------------------------------
1 | package ireader.app.mockcomponents
2 |
3 | import io.ktor.client.HttpClient
4 | import io.ktor.client.engine.okhttp.OkHttp
5 | import io.ktor.client.plugins.BrowserUserAgent
6 | import ireader.core.http.BrowserEngine
7 | import ireader.core.http.HttpClientsInterface
8 |
9 | class FakeHttpClients : HttpClientsInterface {
10 | override val browser: BrowserEngine
11 | get() = throw Exception("This test need to be run on real app")
12 | override val default: HttpClient
13 | get() = HttpClient(OkHttp) {
14 | BrowserUserAgent()
15 | }
16 | override val cloudflareClient: HttpClient
17 | get() = HttpClient(OkHttp) {
18 | BrowserUserAgent()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/defaultRes/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2018 The Tachiyomi Open Source Project
3 |
4 | This Source Code Form is subject to the terms of the Mozilla Public
5 | License, v. 2.0. If a copy of the MPL was not distributed with this
6 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 | */
8 |
9 |
10 | plugins {
11 | id("com.android.library")
12 | kotlin("android")
13 | }
14 |
15 | android {
16 | namespace = "tachiyomix.defaultres"
17 | compileSdk = Config.compileSdk
18 |
19 | defaultConfig {
20 | minSdk = Config.minSdk
21 | }
22 |
23 | sourceSets {
24 | named("main") {
25 | manifest.srcFile("AndroidManifest.xml")
26 | res.setSrcDirs(listOf("res"))
27 | }
28 | }
29 | }
30 |
31 | dependencies {
32 | compileOnly(libs.stdlib)
33 | }
34 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/armtl/src/ireader/armtl/Constants.kt:
--------------------------------------------------------------------------------
1 | package ireader.armtl
2 |
3 | import ireader.core.source.Dependencies
4 | import ireader.core.source.HttpSource
5 | import ireader.utility.TestConstants
6 |
7 | object Constants : TestConstants {
8 | override val bookUrl: String
9 | get() = "https://ar-mtl.club/novel/astral-pet-store/"
10 | override val bookName: String
11 | get() = "Astral Pet Store"
12 | override val chapterUrl: String
13 | get() = "Astral Pet Store novel - Chapter 1193"
14 | override val chapterName: String
15 | get() = "https://ar-mtl.club/novel/astral-pet-store/chapter-1193/"
16 |
17 | override fun getExtension(deps: Dependencies): HttpSource {
18 | return object : ArMtl(deps) {
19 |
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/sources/multisrc/madara/hizomanga/src/ireader/hizomang/Constants.kt:
--------------------------------------------------------------------------------
1 | package ireader.hizomanga
2 |
3 | import ireader.core.source.Dependencies
4 | import ireader.core.source.HttpSource
5 | import ireader.utility.TestConstants
6 |
7 | object Constants : TestConstants {
8 | override val bookUrl: String
9 | get() = "https://hizomanga.net/serie/astral-pet-store/"
10 | override val bookName: String
11 | get() = "Astral Pet Store"
12 | override val chapterUrl: String
13 | get() = "Astral Pet Store novel - Chapter 1193"
14 | override val chapterName: String
15 | get() = "https://hizomanga.net/serie/astral-pet-store/chapter-1193/"
16 |
17 | override fun getExtension(deps: Dependencies): HttpSource {
18 | return object : HizoManga(deps) {
19 |
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/SSLConfiguration.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * SSL/TLS configuration for HTTP clients.
5 | * Platform-specific implementations handle the actual configuration.
6 | */
7 | expect class SSLConfiguration() {
8 | /**
9 | * Enable certificate pinning for specific hosts
10 | */
11 | fun enableCertificatePinning(pins: Map>)
12 |
13 | /**
14 | * Allow self-signed certificates (for development only)
15 | */
16 | fun allowSelfSignedCertificates()
17 |
18 | /**
19 | * Set minimum TLS version
20 | */
21 | fun setMinimumTlsVersion(version: TlsVersion)
22 | }
23 |
24 | /**
25 | * TLS version enum
26 | */
27 | enum class TlsVersion {
28 | TLS_1_0,
29 | TLS_1_1,
30 | TLS_1_2,
31 | TLS_1_3
32 | }
33 |
--------------------------------------------------------------------------------
/test-extensions/src/test/java/ireader/app/tests/ContentChecker.kt:
--------------------------------------------------------------------------------
1 | package ireader.app.tests
2 |
3 | import com.google.common.truth.Truth.assertThat
4 | import ireader.app.CHAPTER_NAME
5 | import ireader.app.CHAPTER_URL
6 | import ireader.app.extension
7 | import ireader.core.source.model.ChapterInfo
8 | import ireader.core.source.model.Page
9 | import org.junit.Before
10 | import org.junit.Test
11 |
12 | class ContentChecker {
13 | var page: List = emptyList()
14 | @Before
15 | fun setup() {
16 | kotlinx.coroutines.runBlocking {
17 | page = extension.getPageList(ChapterInfo(key = CHAPTER_URL, name = CHAPTER_NAME), emptyList())
18 | print(page)
19 | }
20 | }
21 |
22 | @Test
23 | fun `check whether page list is empty `() {
24 | assertThat(page.isNotEmpty()).isTrue()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/test-extensions/src/main/java/ireader/constants/Constants.kt:
--------------------------------------------------------------------------------
1 | package ireader.constants
2 |
3 | import ireader.core.source.Dependencies
4 | import ireader.core.source.HttpSource
5 | import ireader.chrysanthemumgarden.Chrysanthemumgarden
6 | import ireader.utility.TestConstants
7 |
8 | object Constants : TestConstants {
9 | override val bookUrl: String
10 | get() = "https://chrysanthemumgarden.com/novel/test"
11 | override val bookName: String
12 | get() = "Test Novel"
13 | override val chapterUrl: String
14 | get() = "https://chrysanthemumgarden.com/novel/test/chapter-1"
15 | override val chapterName: String
16 | get() = "Chapter 1"
17 |
18 | override fun getExtension(deps: Dependencies): HttpSource {
19 | return object : Chrysanthemumgarden(deps) {
20 | // Test instance
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/sources/multisrc/skynovel/wbnovel/src/ireader/wbnovel/WbNovel.kt:
--------------------------------------------------------------------------------
1 | package ireader.wbnovel
2 |
3 | import ireader.core.source.Dependencies
4 | import ireader.skynovelmodel.SkyNovelModel
5 | import tachiyomix.annotations.Extension
6 |
7 |
8 | @Extension
9 | abstract class WbNovel(val deps: Dependencies) : SkyNovelModel(deps,) {
10 |
11 | override val id: Long
12 | get() = 51
13 |
14 | override val name: String
15 | get() = "WbNovel"
16 | override val lang: String
17 | get() = "in"
18 |
19 | override val baseUrl: String
20 | get() = "https://wbnovel.com"
21 |
22 | override val mainEndpoint: String
23 | get() = "all-novel"
24 |
25 | override val descriptionSelector: String
26 | get() = "summary__content p"
27 |
28 | override val contentSelector: String
29 | get() = ".reading-content p"
30 | }
31 |
--------------------------------------------------------------------------------
/docs/source-api/gradle.properties:
--------------------------------------------------------------------------------
1 | GROUP=io.github.kazemcodes
2 | POM_ARTIFACT_ID=source-api
3 | VERSION_NAME=1.4.0
4 | POM_NAME=IReader Source API
5 | POM_DESCRIPTION=Source API for IReader with improved error handling, validation, and performance
6 | POM_INCEPTION_YEAR=22
7 | POM_URL=https://github.com/kazemcodes/IReader
8 |
9 | POM_LICENSE_NAME=The Apache Software License, Version 2.0
10 | POM_LICENSE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt
11 | POM_LICENSE_DIST=repo
12 |
13 | POM_SCM_URL=https://github.com/kazemcodes/IReader
14 | POM_SCM_CONNECTION=scm:git:git:github.com:kazemcodes/IReader.git
15 | POM_SCM_DEV_CONNECTION=scm:git:github.com:kazemcodes/IReader.git
16 |
17 | POM_DEVELOPER_ID=kazemcodes
18 | POM_DEVELOPER_NAME=kazem.codes
19 | POM_DEVELOPER_URL=https://github.com/kazemcodes
20 |
21 |
22 | SONATYPE_HOST=S01
23 | RELEASE_SIGNING_ENABLED=false
24 |
25 |
26 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 |
2 | org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC
3 |
4 | org.gradle.parallel=true
5 | org.gradle.caching=true
6 | org.gradle.configureondemand=true
7 |
8 | kotlin.code.style=official
9 | kotlin.incremental=true
10 | kotlin.incremental.java=true
11 |
12 | android.useAndroidX=true
13 | android.enableJetifier=false
14 |
15 | android.defaults.buildfeatures.aidl=false
16 | android.defaults.buildfeatures.buildconfig=false
17 | android.defaults.buildfeatures.renderscript=false
18 | android.defaults.buildfeatures.shaders=false
19 | android.defaults.buildfeatures.resvalues=false
20 |
21 | org.gradle.unsafe.configuration-cache=true
22 | # Use this flag sparingly, in case some of the plugins are not fully compatible
23 | org.gradle.unsafe.configuration-cache-problems=warn
24 |
25 | # Kotlin compiler options
26 | kotlin.daemon.jvmargs=-Xmx2048m
27 |
--------------------------------------------------------------------------------
/docs/source-api/src/desktopMain/kotlin/ireader/core/http/CookieStore.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 The Tachiyomi Open Source Project
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 | */
8 |
9 | package ireader.core.http
10 |
11 | /**
12 | * An interface for a persistent cookie store.
13 | */
14 | interface CookieStore {
15 |
16 | /**
17 | * Returns a map of all the cookies stored by domain.
18 | */
19 | fun load(): Map>
20 |
21 | /**
22 | * Updates the cookies stored for this [domain] with the provided by [cookies].
23 | */
24 | fun update(domain: String, cookies: Set)
25 |
26 | /**
27 | * Clears all the cookies saved in this store.
28 | */
29 | fun clear()
30 | }
31 |
--------------------------------------------------------------------------------
/docs/source-api/src/desktopMain/kotlin/ireader/core/http/BrowserEngine.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * Desktop implementation of BrowserEngine
5 | * Currently a stub - could be enhanced with JavaFX WebView or JCEF
6 | */
7 | actual class BrowserEngine actual constructor() : BrowserEngineInterface {
8 | actual override suspend fun fetch(
9 | url: String,
10 | selector: String?,
11 | headers: Headers,
12 | timeout: Long,
13 | userAgent: String
14 | ): BrowserResult {
15 | return BrowserResult(
16 | responseBody = "",
17 | cookies = emptyList(),
18 | statusCode = 501,
19 | error = "BrowserEngine not available on desktop platform. Consider using JavaFX WebView or JCEF for full browser capabilities."
20 | )
21 | }
22 |
23 | actual override fun isAvailable(): Boolean = false
24 | }
25 |
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/mtlnovelfr/src/ireader/mtlnovelfr/MtlNovelFr.kt:
--------------------------------------------------------------------------------
1 | package ireader.mtlnovelfr
2 |
3 | import ireader.core.source.Dependencies
4 | import ireader.mtlnovelmodel.MtlNovelModel
5 | import tachiyomix.annotations.Extension
6 | import tachiyomix.annotations.AutoSourceId
7 |
8 | /**
9 | * 🇫🇷 MTLNovel French - Machine Translation Source
10 | *
11 | * French version of MTLNovel.
12 | * Uses @AutoSourceId for automatic ID generation.
13 | */
14 | @Extension
15 | @AutoSourceId(seed = "MtlNovelFr")
16 | abstract class MtlNovelFr(val deps: Dependencies) : MtlNovelModel(deps) {
17 |
18 | // ═══════════════════════════════════════════════════════════════
19 | // 📋 BASIC SOURCE INFO - Override base class
20 | // ═══════════════════════════════════════════════════════════════
21 | override val baseUrl: String get() = "https://fr.mtlnovel.com"
22 | override val lang = "fr"
23 | }
24 |
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/mtlnovelin/src/ireader/mtlnovelin/MtlNovelIn.kt:
--------------------------------------------------------------------------------
1 | package ireader.mtlnovelin
2 |
3 | import ireader.core.source.Dependencies
4 | import ireader.mtlnovelmodel.MtlNovelModel
5 | import tachiyomix.annotations.Extension
6 | import tachiyomix.annotations.AutoSourceId
7 |
8 | /**
9 | * 🇮🇩 MTLNovel Indonesian - Machine Translation Source
10 | *
11 | * Indonesian version of MTLNovel.
12 | * Uses @AutoSourceId for automatic ID generation.
13 | */
14 | @Extension
15 | @AutoSourceId(seed = "MtlNovelIn")
16 | abstract class MtlNovelIn(val deps: Dependencies) : MtlNovelModel(deps) {
17 |
18 | // ═══════════════════════════════════════════════════════════════
19 | // 📋 BASIC SOURCE INFO - Override base class
20 | // ═══════════════════════════════════════════════════════════════
21 | override val baseUrl: String get() = "https://id.mtlnovel.com"
22 | override val lang = "in"
23 | }
24 |
--------------------------------------------------------------------------------
/common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("multiplatform")
3 | kotlin("plugin.serialization")
4 | }
5 |
6 | kotlin {
7 | jvm()
8 |
9 | js(IR) {
10 | browser {
11 | webpackTask {
12 | mainOutputFileName = "ireader-common.js"
13 | }
14 | }
15 | nodejs()
16 | binaries.library()
17 | }
18 |
19 | sourceSets {
20 | commonMain.dependencies {
21 | // Use api for KMP dependencies that need to be available to consumers
22 | api(libs.ksoup)
23 | api(libs.ireader.core)
24 | api(libs.ktor.core)
25 | api(libs.kotlinx.datetime)
26 | api(libs.serialization.json)
27 | }
28 |
29 | jvmMain.dependencies {
30 | compileOnly(libs.ktor.cio)
31 | }
32 |
33 | jsMain.dependencies {
34 | api(libs.ktor.client.js)
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/sources/en/wnmtl/main/src/ireader/wnmtl/explore/Result.kt:
--------------------------------------------------------------------------------
1 | package ireader.wnmtl.explore
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Result(
7 | val authorId: Int,
8 | val authorPseudonym: String,
9 | val coverImgUrl: String,
10 | val createTime: String,
11 | val gender: Int,
12 | val genre: Int,
13 | val genreName: String,
14 | val id: Int,
15 | val language: String,
16 | val lastUpdateChapterId: Int,
17 | val lastUpdateChapterOrder: Int,
18 | val lastUpdateChapterTitle: String,
19 | val lastUpdateTime: Long,
20 | val ratingCounts: Double,
21 | val readCounts: Int,
22 | val shareFlag: Boolean,
23 | val source: String,
24 | val sourceOrgId: Int,
25 | val status: Int,
26 | val synopsis: String,
27 | val tag: String,
28 | val title: String,
29 | val type: String,
30 | val updateTime: String,
31 | val version: Int,
32 | val wordCounts: Int
33 | )
34 |
--------------------------------------------------------------------------------
/test-extensions/src/androidTest/java/ireader/app/tests/ContentChecker.kt:
--------------------------------------------------------------------------------
1 | package ireader.app.tests
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import com.google.common.truth.Truth.assertThat
5 | import ireader.app.CHAPTER_NAME
6 | import ireader.app.CHAPTER_URL
7 | import ireader.app.extension
8 | import ireader.core.source.model.ChapterInfo
9 | import ireader.core.source.model.Page
10 | import org.junit.Before
11 | import org.junit.Test
12 | import org.junit.runner.RunWith
13 |
14 | @RunWith(AndroidJUnit4::class)
15 | class ContentChecker {
16 | var page: List = emptyList()
17 | @Before
18 | fun setup() {
19 | kotlinx.coroutines.runBlocking {
20 | page = extension.getPageList(ChapterInfo(key = CHAPTER_URL, name = CHAPTER_NAME), emptyList())
21 | print(page)
22 | }
23 | }
24 |
25 | @Test
26 | fun checkContentListIsNotEmpty() {
27 | assertThat(page.isNotEmpty()).isTrue()
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/CookieSynchronizer.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * Synchronizes cookies between WebView and HTTP client.
5 | * Platform-specific implementations handle the actual synchronization.
6 | */
7 | expect class CookieSynchronizer {
8 | /**
9 | * Sync cookies from WebView to HTTP client storage
10 | * @param url The URL string to sync cookies for
11 | */
12 | fun syncFromWebView(url: String)
13 |
14 | /**
15 | * Sync cookies from HTTP client to WebView
16 | * @param url The URL string to sync cookies for
17 | */
18 | fun syncToWebView(url: String)
19 |
20 | /**
21 | * Clear all cookies
22 | */
23 | fun clearAll()
24 | }
25 |
26 | /**
27 | * Common cookie synchronizer interface for platform implementations
28 | */
29 | interface CookieSynchronizerInterface {
30 | fun syncFromWebView(url: String)
31 | fun syncToWebView(url: String)
32 | fun clearAll()
33 | }
34 |
--------------------------------------------------------------------------------
/sources/multisrc/readwn/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf(
2 | Extension(
3 | name = "Readwn",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = "en",
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | assetsDir = "multisrc/madara/readwn/assets",
11 | sourceDir = "readwn",
12 | ),
13 | Extension(
14 | name = "Novelmt",
15 | versionCode = 2,
16 | libVersion = "2",
17 | lang = "en",
18 | description = "",
19 | nsfw = false,
20 | icon = DEFAULT_ICON,
21 | assetsDir = "multisrc/madara/novelmt/assets",
22 | sourceDir = "novelmt",
23 | ),
24 | Extension(
25 | name = "Ltnovel",
26 | versionCode = 2,
27 | libVersion = "2",
28 | lang = "en",
29 | description = "",
30 | nsfw = false,
31 | icon = DEFAULT_ICON,
32 | assetsDir = "multisrc/madara/ltnovel/assets",
33 | sourceDir = "ltnovel",
34 | ),
35 | ).also(::register)
36 |
--------------------------------------------------------------------------------
/test-extensions/src/androidTest/java/ireader/app/Constants.kt:
--------------------------------------------------------------------------------
1 | package ireader.app
2 |
3 | import android.content.Context
4 | import androidx.test.core.app.ApplicationProvider
5 | import ireader.core.http.AcceptAllCookiesStorage
6 | import ireader.core.http.BrowserEngine
7 | import ireader.core.http.HttpClients
8 | import ireader.core.http.WebViewCookieJar
9 | import ireader.core.http.WebViewManger
10 | import ireader.core.prefs.AndroidPreferenceStore
11 | import ireader.core.source.CatalogSource
12 | import ireader.core.source.Dependencies
13 | import ireader.core.source.TestSource
14 |
15 | val context: Context = ApplicationProvider.getApplicationContext()
16 | val cookie = AcceptAllCookiesStorage()
17 | val cookieJar = WebViewCookieJar(cookie)
18 | val httpClients = HttpClients(context, BrowserEngine(WebViewManger(context), cookieJar), cookie, cookieJar)
19 | val androidPreferenceStore = AndroidPreferenceStore(context, "test-preferences")
20 | val dependencies = Dependencies(httpClients, androidPreferenceStore)
21 |
22 | val extension: CatalogSource = TestSource()
23 |
--------------------------------------------------------------------------------
/.github/scripts/commit-repo.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | # Sync built APKs from master/repo/ to repov2 branch
5 | # Note: The workflow checks out repov2 branch into 'repo' folder
6 | # and master branch into 'master' folder
7 | # Exclude js/ folder to preserve JS sources deployed by build_js.yml workflow
8 | rsync -a --delete --exclude .git --exclude .gitignore --exclude js/ ../master/repo/ .
9 |
10 | # Ensure .gitignore in repo branch doesn't exclude APKs
11 | # Create/update .gitignore to allow all repo files
12 | cat > .gitignore << 'EOF'
13 | # Repo branch - allow all built files
14 | # Only ignore OS/editor files
15 | .DS_Store
16 | Thumbs.db
17 | *.swp
18 | *~
19 | EOF
20 |
21 | git config --global user.email "github-actions[bot]@users.noreply.github.com"
22 | git config --global user.name "github-actions[bot]"
23 |
24 | # Force add all files including APKs (in case .gitignore was excluding them)
25 | git add -f .
26 |
27 | git status
28 | if [ -n "$(git status --porcelain)" ]; then
29 | git commit -m "Update extensions repo"
30 | git push
31 | else
32 | echo "No changes to commit"
33 | fi
34 |
--------------------------------------------------------------------------------
/docs/example-madara/main/src/ireader/examplemadara/ExampleMadara.kt:
--------------------------------------------------------------------------------
1 | package ireader.examplemadara
2 |
3 | import tachiyomix.annotations.MadaraSource
4 | import tachiyomix.annotations.SourceMeta
5 |
6 | /**
7 | * Example Madara-based source using the @MadaraSource annotation.
8 | *
9 | * Instead of manually creating a class that extends Madara,
10 | * KSP generates the source class from this configuration.
11 | *
12 | * This reduces boilerplate from ~20 lines to just annotations!
13 | */
14 | @MadaraSource(
15 | name = "ExampleMadara",
16 | baseUrl = "https://example-madara.com",
17 | lang = "en",
18 | id = 888888,
19 | novelsPath = "novel",
20 | novelPath = "novel",
21 | chapterPath = "novel"
22 | )
23 | @SourceMeta(
24 | description = "Example Madara theme source",
25 | nsfw = false,
26 | tags = ["english", "madara", "example"]
27 | )
28 | object ExampleMadaraConfig
29 |
30 | // The KSP processor will generate:
31 | // - ExampleMadaraGenerated class that extends Madara
32 | // - Proper constructor with Dependencies
33 | // - All required overrides (name, id, lang, baseUrl)
34 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/source/model/LocalNovelDetails.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.source.model
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * Metadata structure for details.json in local novel folders
7 | * Allows users to manually add rich metadata
8 | */
9 | @Serializable
10 | data class LocalNovelDetails(
11 | val title: String? = null,
12 | val author: String? = null,
13 | val artist: String? = null,
14 | val description: String? = null,
15 | val genre: List? = null,
16 | val status: String? = null // "Ongoing", "Completed", "Hiatus", etc.
17 | ) {
18 | fun toStatus(): Long {
19 | return when (status?.lowercase()) {
20 | "ongoing" -> MangaInfo.ONGOING
21 | "completed" -> MangaInfo.COMPLETED
22 | "licensed" -> MangaInfo.LICENSED
23 | "finished", "publishing_finished" -> MangaInfo.PUBLISHING_FINISHED
24 | "cancelled" -> MangaInfo.CANCELLED
25 | "hiatus", "on_hiatus" -> MangaInfo.ON_HIATUS
26 | else -> MangaInfo.UNKNOWN
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/e1be5cc3-0144-44d0-8517-801c039c045f.json:
--------------------------------------------------------------------------------
1 | {
2 | "attributes": {
3 | "chapter-item": "innerHTML",
4 | "chapter-link": "href",
5 | "content": "innerHTML",
6 | "cover": "src",
7 | "explore-cover": "src",
8 | "explore-link": "href",
9 | "explore-title": "title",
10 | "genres": "innerHTML",
11 | "novel-item": "innerHTML"
12 | },
13 | "lang": "en",
14 | "latestUrl": "/latest?page={{page}}",
15 | "name": "NovelBuddy",
16 | "popularUrl": "/top/month",
17 | "searchUrl": "/search?q={{query}}",
18 | "selectors": {
19 | "author": "span",
20 | "chapter-item": "#c-100",
21 | "chapter-link": "#c-100 a",
22 | "chapter-name": ".chapter-title",
23 | "content": "#chapter__content",
24 | "cover": "img",
25 | "description": ".content",
26 | "explore-cover": "img",
27 | "explore-link": "a",
28 | "explore-title": "a",
29 | "genres": "p",
30 | "novel-item": ".book-detailed-item",
31 | "status": "span",
32 | "title": "h1"
33 | },
34 | "baseUrl": "https://novelbuddy.io",
35 | "exportedAt": "2025-11-28T21:41:09.132Z",
36 | "version": "1.0"
37 | }
--------------------------------------------------------------------------------
/sources/en/lnmtl/main/src/ireader/lnmtl/chapters/Data.kt:
--------------------------------------------------------------------------------
1 | package ireader.lnmtl.chapters
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Data(
7 | val comments_count: Int?,
8 | val created_at: String?,
9 | val downloaded: Boolean?,
10 | val hide_ads: Int?,
11 | val id: Int,
12 | val is_special: Int?,
13 | val length: Int?,
14 | val novel_id: Int?,
15 | val number: Int,
16 | val number_special: Int?,
17 | val original: Int?,
18 | val part: Int?,
19 | val position: Int?,
20 | val ratings_count: Int?,
21 | val ratings_negative: Int?,
22 | val ratings_neutral: Int?,
23 | val ratings_positive: Int?,
24 | val retranslated_at: String?,
25 | val retranslations_count: Int?,
26 | val site_url: String?,
27 | val slug: String?,
28 | val title: String,
29 | val title_json: String?,
30 | val title_raw: String?,
31 | val translated_body: Boolean?,
32 | val translated_title: Boolean?,
33 | val updated_at: String?,
34 | val updater_id: Int?,
35 | val url: String,
36 | val volume_id: Int
37 | )
--------------------------------------------------------------------------------
/docs/source-api/src/desktopMain/kotlin/ireader/core/http/WebViewManger.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | import com.fleeksoft.ksoup.nodes.Document
4 |
5 | /**
6 | * Desktop implementation of WebViewManager
7 | * Currently a stub - could be enhanced with JavaFX WebView or JCEF
8 | */
9 | actual class WebViewManger {
10 | actual var isInit: Boolean = false
11 | actual var userAgent: String = DEFAULT_USER_AGENT
12 | actual var selector: String? = null
13 | actual var html: Document? = null
14 | actual var webUrl: String? = null
15 | actual var inProgress: Boolean = false
16 |
17 | actual fun init(): Any {
18 | return 0
19 | }
20 |
21 | actual fun update() {
22 | // No-op on desktop
23 | }
24 |
25 | actual fun destroy() {
26 | // No-op on desktop
27 | }
28 |
29 | actual fun loadInBackground(url: String, selector: String?, onReady: (String) -> Unit) {
30 | // Not supported on desktop
31 | onReady("")
32 | }
33 |
34 | actual fun isProcessingInBackground(): Boolean = false
35 |
36 | actual fun isAvailable(): Boolean = false
37 | }
38 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/HttpClients.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 The Tachiyomi Open Source Project
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 | */
8 |
9 | package ireader.core.http
10 |
11 | import io.ktor.client.*
12 |
13 | /**
14 | * Unified HTTP client interface providing different client configurations
15 | * for various use cases (default, Cloudflare bypass, browser engine)
16 | */
17 | interface HttpClientsInterface {
18 | val browser: BrowserEngine
19 | val default: HttpClient
20 | val cloudflareClient: HttpClient
21 | val config: NetworkConfig
22 | val sslConfig: SSLConfiguration
23 | val cookieSynchronizer: CookieSynchronizer
24 | }
25 |
26 | expect class HttpClients : HttpClientsInterface {
27 | override val browser: BrowserEngine
28 | override val default: HttpClient
29 | override val cloudflareClient: HttpClient
30 | override val config: NetworkConfig
31 | override val sslConfig: SSLConfiguration
32 | override val cookieSynchronizer: CookieSynchronizer
33 | }
34 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/sources/en/wuxiaworld/main/src/ireader/wuxiaworld/Item.kt:
--------------------------------------------------------------------------------
1 | package ireader.wuxiaworld
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Item(
7 | val abbreviation: String,
8 | val active: Boolean,
9 | val authorName: String?,
10 | val chapterGroups: String?,
11 | val coverUrl: String,
12 | val description: String,
13 | val ebooks: List,
14 | val excludedFromVipSelection: Boolean,
15 | val genres: List,
16 | val id: Int,
17 | val isFree: Boolean,
18 | val karmaActive: Boolean,
19 | val language: String,
20 | val languageAbbreviation: String,
21 | val latestAnnouncement: Long?,
22 | val name: String,
23 | val novelHasSponsorPlans: Boolean,
24 | val reviewCount: Int,
25 | val reviewScore: Double?,
26 | val siteCreditsEnabled: Boolean?,
27 | val slug: String,
28 | val sponsorPlans: String?,
29 | val synopsis: String?,
30 | val tags: List?,
31 | val teaserMessage: String?,
32 | val translatorId: String?,
33 | val translatorName: String?,
34 | val translatorUserName: String?,
35 | val userHasEbook: Boolean?,
36 | val userHasNovelUnlocked: Boolean?,
37 | val visible: Boolean?
38 | )
39 |
--------------------------------------------------------------------------------
/.github/workflows/code_quality.yml:
--------------------------------------------------------------------------------
1 | name: Code Quality
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - '**.kt'
7 | - '**.kts'
8 | - 'detekt.yml'
9 | - '.editorconfig'
10 |
11 | jobs:
12 | detekt:
13 | name: Detekt Check
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Checkout code
17 | uses: actions/checkout@v4
18 |
19 | - name: Set up JDK
20 | uses: actions/setup-java@v4
21 | with:
22 | java-version: 21
23 | distribution: adopt
24 |
25 | - name: Run detekt
26 | run: ./gradlew detekt
27 | continue-on-error: true
28 |
29 | - name: Upload detekt report
30 | uses: actions/upload-artifact@v4
31 | if: always()
32 | with:
33 | name: detekt-report
34 | path: build/reports/detekt/
35 |
36 | ktlint:
37 | name: Ktlint Check
38 | runs-on: ubuntu-latest
39 | steps:
40 | - name: Checkout code
41 | uses: actions/checkout@v4
42 |
43 | - name: Set up JDK
44 | uses: actions/setup-java@v4
45 | with:
46 | java-version: 21
47 | distribution: adopt
48 |
49 | - name: Run ktlint
50 | run: ./gradlew ktlintCheck
51 | continue-on-error: true
52 |
--------------------------------------------------------------------------------
/test-extensions/src/test/java/ireader/app/mockcomponents/FakePreferencesStore.kt:
--------------------------------------------------------------------------------
1 | package ireader.app.mockcomponents
2 |
3 | import ireader.core.prefs.Preference
4 | import ireader.core.prefs.PreferenceStore
5 |
6 | class FakePreferencesStore : PreferenceStore {
7 | override fun getString(key: String, defaultValue: String): Preference {
8 | throw Exception()
9 | }
10 |
11 | override fun getLong(key: String, defaultValue: Long): Preference {
12 | throw Exception()
13 | }
14 |
15 | override fun getInt(key: String, defaultValue: Int): Preference {
16 | throw Exception()
17 | }
18 |
19 | override fun getFloat(key: String, defaultValue: Float): Preference {
20 | throw Exception()
21 | }
22 |
23 | override fun getBoolean(key: String, defaultValue: Boolean): Preference {
24 | throw Exception()
25 | }
26 |
27 | override fun getStringSet(key: String, defaultValue: Set): Preference> {
28 | throw Exception()
29 | }
30 |
31 | override fun getObject(
32 | key: String,
33 | defaultValue: T,
34 | serializer: (T) -> String,
35 | deserializer: (String) -> T
36 | ): Preference {
37 | throw Exception()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/test-extensions/src/test/java/ireader/app/tests/BookListChecker.kt:
--------------------------------------------------------------------------------
1 | package ireader.app.tests
2 |
3 | import com.google.common.truth.Truth
4 | import ireader.app.extension
5 | import ireader.core.source.model.Listing
6 | import ireader.core.source.model.MangasPageInfo
7 | import org.junit.Before
8 | import org.junit.Test
9 |
10 | class BookListChecker {
11 | lateinit var books: MangasPageInfo
12 | @Before
13 | fun setup() {
14 | kotlinx.coroutines.runBlocking {
15 |
16 | books = extension.getMangaList(LatestListing(), 1)
17 | print(books)
18 | }
19 | }
20 |
21 | @Test
22 | fun `check whether books list is empty `() {
23 | Truth.assertThat(books.mangas.isNotEmpty()).isTrue()
24 | }
25 | @Test
26 | fun `check whether book has title `() {
27 | Truth.assertThat(books.mangas.any { book -> book.title.isNotBlank() }).isTrue()
28 | }
29 | @Test
30 | fun `check whether book has key `() {
31 | Truth.assertThat(books.mangas.any { book -> book.key.isNotBlank() }).isTrue()
32 | }
33 | @Test
34 | fun `check whether book has cover `() {
35 | Truth.assertThat(books.mangas.any { book -> book.cover.isNotBlank() }).isTrue()
36 | }
37 | }
38 |
39 | class LatestListing() : Listing(name = "Latest")
40 |
--------------------------------------------------------------------------------
/scripts/bump-version-codes.ps1:
--------------------------------------------------------------------------------
1 | # Bump versionCode by +1 in all build.gradle.kts files under sources/
2 | # Usage: .\scripts\bump-version-codes.ps1
3 |
4 | param(
5 | [switch]$DryRun = $false,
6 | [string]$Path = "sources"
7 | )
8 |
9 | $files = Get-ChildItem -Path $Path -Recurse -Filter "build.gradle.kts"
10 | $totalUpdates = 0
11 |
12 | foreach ($file in $files) {
13 | $content = Get-Content $file.FullName -Raw
14 | $updated = $false
15 |
16 | # Match versionCode = X, and increment X
17 | $newContent = [regex]::Replace($content, 'versionCode\s*=\s*(\d+)', {
18 | param($match)
19 | $oldVersion = [int]$match.Groups[1].Value
20 | $newVersion = $oldVersion + 1
21 | $script:updated = $true
22 | $script:totalUpdates++
23 | Write-Host " $($file.Name): versionCode $oldVersion -> $newVersion" -ForegroundColor Cyan
24 | return "versionCode = $newVersion"
25 | })
26 |
27 | if ($updated -and -not $DryRun) {
28 | Set-Content -Path $file.FullName -Value $newContent -NoNewline
29 | }
30 | }
31 |
32 | if ($DryRun) {
33 | Write-Host "`nDry run complete. $totalUpdates version codes would be updated." -ForegroundColor Yellow
34 | } else {
35 | Write-Host "`nDone! Updated $totalUpdates version codes." -ForegroundColor Green
36 | }
37 |
--------------------------------------------------------------------------------
/deeplink/src/main/java/tachiyomix/deeplink/SourceDeepLinkActivity.java:
--------------------------------------------------------------------------------
1 | package tachiyomix.deeplink;
2 |
3 | import android.app.Activity;
4 | import android.content.ActivityNotFoundException;
5 | import android.content.Intent;
6 | import android.net.Uri;
7 | import android.os.Bundle;
8 | import android.util.Log;
9 |
10 | /*
11 | Copyright (C) 2018 The Tachiyomi Open Source Project
12 |
13 | This Source Code Form is subject to the terms of the Mozilla Public
14 | License, v. 2.0. If a copy of the MPL was not distributed with this
15 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
16 | */
17 |
18 | public class SourceDeepLinkActivity extends Activity {
19 |
20 | private static final String TAG = "SourceDeepLinkActivity";
21 |
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 |
26 | Intent forward = new Intent(Intent.ACTION_VIEW);
27 | forward.setData(Uri.parse(String.format(
28 | "tachiyomi://deeplink/%s?url=%s",
29 | getPackageName(),
30 | getIntent().getData().toString()
31 | )));
32 |
33 | try {
34 | startActivity(forward);
35 | } catch (ActivityNotFoundException e) {
36 | Log.e(TAG, e.toString());
37 | }
38 |
39 | finish();
40 | System.exit(0);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/test-extensions/src/androidTest/java/ireader/app/tests/BookListChecker.kt:
--------------------------------------------------------------------------------
1 | package ireader.app.tests
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import com.google.common.truth.Truth
5 | import ireader.app.extension
6 | import ireader.core.source.model.Listing
7 | import ireader.core.source.model.MangasPageInfo
8 | import org.junit.Before
9 | import org.junit.Test
10 | import org.junit.runner.RunWith
11 |
12 | @RunWith(AndroidJUnit4::class)
13 | class BookListChecker {
14 | lateinit var books: MangasPageInfo
15 | @Before
16 | fun setup() {
17 | kotlinx.coroutines.runBlocking {
18 | books = extension.getMangaList(LatestListing(), 1)
19 | print(books)
20 | }
21 | }
22 |
23 | @Test
24 | fun checkBookListIsNotEmpty() {
25 | Truth.assertThat(books.mangas.isNotEmpty()).isTrue()
26 | }
27 | @Test
28 | fun checkBookHasTitle() {
29 | Truth.assertThat(books.mangas.any { book -> book.title.isNotBlank() }).isTrue()
30 | }
31 | @Test
32 | fun checkBookHasKey() {
33 | Truth.assertThat(books.mangas.any { book -> book.key.isNotBlank() }).isTrue()
34 | }
35 | @Test
36 | fun checkBookHasCover() {
37 | Truth.assertThat(books.mangas.any { book -> book.cover.isNotBlank() }).isTrue()
38 | }
39 | }
40 |
41 | class LatestListing() : Listing(name = "Latest")
42 |
--------------------------------------------------------------------------------
/docs/source-api/src/androidMain/res/values/string.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | need webview to view this content
4 | webview is outdated.
5 | Can\'t bypass the cloudflare
6 |
7 |
8 | Fatal error for
9 | Error while (un)installing package
10 | PackageManger: Cancelled
11 | PackageManger: There is another apk installed with higher version than this package
12 | PackageManger: InSufficient Storage
13 | PackageManger: Incompatible with installed package
14 | PackageManger: Conflicted with installed package
15 | PackageManger: Installer was blocked
16 |
17 | Package installer failed to install packages: STATUS_CODE:
18 |
19 | Success
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/docs/source-api/src/jsMain/kotlin/ireader/core/http/SSLConfiguration.js.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * JavaScript implementation of SSL/TLS configuration.
5 | *
6 | * In JavaScript/browser context, SSL is handled by the browser/runtime,
7 | * so this is mostly a no-op implementation.
8 | */
9 | actual class SSLConfiguration actual constructor() {
10 |
11 | /**
12 | * Enable certificate pinning for specific hosts.
13 | * Not supported in JS - browser handles SSL.
14 | */
15 | actual fun enableCertificatePinning(pins: Map>) {
16 | // No-op in JS - browser handles SSL
17 | console.log("SSLConfiguration: Certificate pinning not supported in JS")
18 | }
19 |
20 | /**
21 | * Allow self-signed certificates.
22 | * Not supported in JS - browser handles SSL.
23 | */
24 | actual fun allowSelfSignedCertificates() {
25 | // No-op in JS - browser handles SSL
26 | console.log("SSLConfiguration: Self-signed certificates setting not supported in JS")
27 | }
28 |
29 | /**
30 | * Set minimum TLS version.
31 | * Not supported in JS - browser handles SSL.
32 | */
33 | actual fun setMinimumTlsVersion(version: TlsVersion) {
34 | // No-op in JS - browser handles SSL
35 | console.log("SSLConfiguration: TLS version setting not supported in JS")
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/sources/multisrc/mtlnovel/build.gradle.kts:
--------------------------------------------------------------------------------
1 | listOf(
2 | Extension(
3 | name = "MtlNovelEn",
4 | versionCode = 2,
5 | libVersion = "2",
6 | lang = "en",
7 | description = "",
8 | nsfw = false,
9 | icon = DEFAULT_ICON,
10 | assetsDir = "multisrc/mtlnovel/mtlnovelen/assets",
11 | sourceDir = "mtlnovelen",
12 | ),
13 | Extension(
14 | name = "MtlNovelFr",
15 | versionCode = 2,
16 | libVersion = "2",
17 | lang = "fr",
18 | description = "",
19 | nsfw = false,
20 | icon = DEFAULT_ICON,
21 | assetsDir = "multisrc/mtlnovel/mtlnovelen/assets",
22 | sourceDir = "mtlnovelfr",
23 | ),
24 | Extension(
25 | name = "MtlNovelEs",
26 | versionCode = 2,
27 | libVersion = "2",
28 | lang = "es",
29 | description = "",
30 | nsfw = false,
31 | icon = DEFAULT_ICON,
32 | assetsDir = "multisrc/mtlnovel/mtlnovelen/assets",
33 | sourceDir = "mtlnoveles",
34 | ),
35 | Extension(
36 | name = "MtlNovelIn",
37 | versionCode = 2,
38 | libVersion = "2",
39 | lang = "in",
40 | description = "",
41 | nsfw = false,
42 | icon = DEFAULT_ICON,
43 | assetsDir = "multisrc/mtlnovel/mtlnovelen/assets",
44 | sourceDir = "mtlnovelin",
45 | ),
46 | ).also(::register)
47 |
--------------------------------------------------------------------------------
/test-extensions/src/test/java/ireader/app/tests/InfoChecked.kt:
--------------------------------------------------------------------------------
1 | package ireader.app.tests
2 |
3 | import com.google.common.truth.Truth.assertThat
4 | import ireader.app.BOOK_NAME
5 | import ireader.app.BOOK_URL
6 | import ireader.app.extension
7 | import ireader.core.source.model.MangaInfo
8 | import org.junit.Before
9 | import org.junit.Test
10 |
11 | class InfoChecked {
12 |
13 | var book: MangaInfo = MangaInfo(key = "", title = "")
14 |
15 | @Before
16 | fun setup() {
17 | kotlinx.coroutines.runBlocking {
18 | book = extension.getMangaDetails(MangaInfo(key = BOOK_URL, title = BOOK_NAME), emptyList())
19 | print(book)
20 | }
21 | }
22 |
23 | @Test
24 | fun `check book name`() {
25 | assertThat(book.title.isNotBlank()).isTrue()
26 | }
27 | @Test
28 | fun `check book author`() {
29 | assertThat(book.author.isNotBlank()).isTrue()
30 | }
31 |
32 | @Test
33 | fun `check book cover`() {
34 | assertThat(book.cover.isNotBlank()).isTrue()
35 | }
36 | @Test
37 | fun `check book description`() {
38 | assertThat(book.description.isNotBlank()).isTrue()
39 | }
40 | @Test
41 | fun `check book genres`() {
42 | assertThat(book.genres.isNotEmpty()).isTrue()
43 | }
44 | @Test
45 | fun `check book status`() {
46 | assertThat(book.status != MangaInfo.UNKNOWN).isTrue()
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/WebViewManger.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | import com.fleeksoft.ksoup.nodes.Document
4 |
5 | /**
6 | * WebView manager for handling web content loading and Cloudflare bypass
7 | * Provides platform-specific WebView implementations
8 | */
9 | expect class WebViewManger {
10 | var isInit: Boolean
11 | var userAgent: String
12 | var selector: String?
13 | var html: Document?
14 | var webUrl: String?
15 | var inProgress: Boolean
16 |
17 | /**
18 | * Initialize the WebView
19 | * @return Platform-specific WebView instance
20 | */
21 | fun init(): Any
22 |
23 | /**
24 | * Update WebView state
25 | */
26 | fun update()
27 |
28 | /**
29 | * Destroy and cleanup WebView resources
30 | */
31 | fun destroy()
32 |
33 | /**
34 | * Load URL in background mode (invisible to user)
35 | * @param url The URL to load
36 | * @param selector CSS selector to wait for
37 | * @param onReady Callback when content is ready
38 | */
39 | fun loadInBackground(url: String, selector: String?, onReady: (String) -> Unit)
40 |
41 | /**
42 | * Check if WebView is currently processing in background
43 | */
44 | fun isProcessingInBackground(): Boolean
45 |
46 | /**
47 | * Check if WebView is available on this platform
48 | */
49 | fun isAvailable(): Boolean
50 | }
51 |
--------------------------------------------------------------------------------
/docs/source-api/src/androidMain/kotlin/ireader/core/http/CookieSynchronizer.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | import android.webkit.CookieManager
4 | import okhttp3.Cookie
5 | import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
6 |
7 | /**
8 | * Android implementation of cookie synchronization between WebView and OkHttp
9 | */
10 | actual class CookieSynchronizer(
11 | private val webViewCookieJar: WebViewCookieJar
12 | ) {
13 | private val cookieManager = CookieManager.getInstance()
14 |
15 | actual fun syncFromWebView(url: String) {
16 | val httpUrl = url.toHttpUrlOrNull() ?: return
17 | val webViewCookies = cookieManager.getCookie(url)
18 | if (!webViewCookies.isNullOrEmpty()) {
19 | val cookies = webViewCookies.split(";")
20 | .mapNotNull { Cookie.parse(httpUrl, it.trim()) }
21 | webViewCookieJar.saveFromResponse(httpUrl, cookies)
22 | }
23 | }
24 |
25 | actual fun syncToWebView(url: String) {
26 | val httpUrl = url.toHttpUrlOrNull() ?: return
27 | val cookies = webViewCookieJar.loadForRequest(httpUrl)
28 | cookies.forEach { cookie ->
29 | cookieManager.setCookie(url, cookie.toString())
30 | }
31 | cookieManager.flush()
32 | }
33 |
34 | actual fun clearAll() {
35 | cookieManager.removeAllCookies(null)
36 | cookieManager.flush()
37 | webViewCookieJar.removeAll()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/JS.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 The Tachiyomi Open Source Project
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 | */
8 |
9 | package ireader.core.http
10 |
11 | /**
12 | * A wrapper to allow executing JavaScript code without knowing the implementation details.
13 | */
14 | @Suppress("NO_ACTUAL_FOR_EXPECT")
15 | expect class JS {
16 |
17 | /**
18 | * Evaluates the given JavaScript [script] and returns its result as [String] or throws an
19 | * exception.
20 | */
21 | fun evaluateAsString(script: String): String
22 |
23 | /**
24 | * Evaluates the given JavaScript [script] and returns its result as [Int] or throws an exception.
25 | */
26 | fun evaluateAsInt(script: String): Int
27 |
28 | /**
29 | * Evaluates the given JavaScript [script] and returns its result as [Double] or throws an
30 | * exception.
31 | */
32 | fun evaluateAsDouble(script: String): Double
33 |
34 | /**
35 | * Evaluates the given JavaScript [script] and returns its result as [Boolean] or throws an
36 | * exception.
37 | */
38 | fun evaluateAsBoolean(script: String): Boolean
39 |
40 | /**
41 | * Closes this instance. No evaluations can be made on this instance after calling this method.
42 | */
43 | fun close()
44 | }
45 |
--------------------------------------------------------------------------------
/docs/source-api/src/desktopMain/kotlin/ireader/core/storage/CacheDir.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.storage
2 |
3 | import java.io.File
4 |
5 | val AppDir : File = getCacheDir()
6 |
7 | val ExtensionDir = File(AppDir, "/Extensions/")
8 | val BackupDir = File(AppDir, "/Backup/")
9 |
10 | enum class OperatingSystem {
11 | Android, IOS, Windows, Linux, MacOS, Unknown
12 | }
13 |
14 | private val currentOperatingSystem: OperatingSystem
15 | get() {
16 | val operSys = System.getProperty("os.name").lowercase()
17 | return if (operSys.contains("win")) {
18 | OperatingSystem.Windows
19 | } else if (operSys.contains("nix") || operSys.contains("nux") ||
20 | operSys.contains("aix")
21 | ) {
22 | OperatingSystem.Linux
23 | } else if (operSys.contains("mac")) {
24 | OperatingSystem.MacOS
25 | } else {
26 | OperatingSystem.Unknown
27 | }
28 | }
29 |
30 | private fun getCacheDir(): File {
31 | val ApplicationName = "IReader"
32 | return when (currentOperatingSystem) {
33 | OperatingSystem.Windows -> File(System.getenv("AppData"), "$ApplicationName/cache/")
34 | OperatingSystem.Linux -> File(System.getProperty("user.home"), ".cache/$ApplicationName/")
35 | OperatingSystem.MacOS -> File(System.getProperty("user.home"), "Library/Caches/$ApplicationName/")
36 | else -> throw IllegalStateException("Unsupported operating system")
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/source/model/FilterList.kt:
--------------------------------------------------------------------------------
1 |
2 |
3 | package ireader.core.source.model
4 |
5 | typealias FilterList = List>
6 | typealias CommandList = List>
7 |
8 | /**
9 | * Extension functions for FilterList
10 | */
11 |
12 | /**
13 | * Get all filters that are not at default value
14 | */
15 | fun FilterList.getActiveFilters(): FilterList {
16 | return this.filter { !it.isDefaultValue() }
17 | }
18 |
19 | /**
20 | * Reset all filters to default values
21 | */
22 | fun FilterList.resetAllFilters() {
23 | this.forEach { it.reset() }
24 | }
25 |
26 | /**
27 | * Check if any filter is active
28 | */
29 | fun FilterList.hasActiveFilters(): Boolean {
30 | return this.any { !it.isDefaultValue() }
31 | }
32 |
33 | /**
34 | * Get filter by name
35 | */
36 | fun FilterList.findByName(name: String): Filter<*>? {
37 | return this.firstOrNull { it.name == name }
38 | }
39 |
40 | /**
41 | * Extension functions for CommandList
42 | */
43 |
44 | /**
45 | * Get all commands that are not at default value
46 | */
47 | fun CommandList.getActiveCommands(): CommandList {
48 | return this.filter { !it.isDefaultValue() }
49 | }
50 |
51 | /**
52 | * Reset all commands to default values
53 | */
54 | fun CommandList.resetAllCommands() {
55 | this.forEach { it.reset() }
56 | }
57 |
58 | /**
59 | * Check if any command is active
60 | */
61 | fun CommandList.hasActiveCommands(): Boolean {
62 | return this.any { !it.isDefaultValue() }
63 | }
64 |
--------------------------------------------------------------------------------
/scripts/bump-version-codes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """
3 | Bump versionCode by +1 in all build.gradle.kts files under sources/
4 |
5 | Usage:
6 | python scripts/bump-version-codes.py # Apply changes
7 | python scripts/bump-version-codes.py --dry-run # Preview changes
8 | """
9 |
10 | import re
11 | import sys
12 | from pathlib import Path
13 |
14 | def bump_version_codes(base_path: str = "sources", dry_run: bool = False):
15 | total_updates = 0
16 |
17 | for gradle_file in Path(base_path).rglob("build.gradle.kts"):
18 | content = gradle_file.read_text(encoding="utf-8")
19 |
20 | def replace_version(match):
21 | nonlocal total_updates
22 | old_version = int(match.group(1))
23 | new_version = old_version + 1
24 | total_updates += 1
25 | print(f" {gradle_file.name}: versionCode {old_version} -> {new_version}")
26 | return f"versionCode = {new_version}"
27 |
28 | new_content = re.sub(r'versionCode\s*=\s*(\d+)', replace_version, content)
29 |
30 | if new_content != content and not dry_run:
31 | gradle_file.write_text(new_content, encoding="utf-8")
32 |
33 | if dry_run:
34 | print(f"\nDry run complete. {total_updates} version codes would be updated.")
35 | else:
36 | print(f"\nDone! Updated {total_updates} version codes.")
37 |
38 | if __name__ == "__main__":
39 | dry_run = "--dry-run" in sys.argv
40 | bump_version_codes(dry_run=dry_run)
41 |
--------------------------------------------------------------------------------
/test-extensions/src/androidTest/java/ireader/app/tests/InfoChecked.kt:
--------------------------------------------------------------------------------
1 | package ireader.app.tests
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import com.google.common.truth.Truth.assertThat
5 | import ireader.app.BOOK_NAME
6 | import ireader.app.BOOK_URL
7 | import ireader.app.extension
8 | import ireader.core.source.model.MangaInfo
9 | import org.junit.Before
10 | import org.junit.Test
11 | import org.junit.runner.RunWith
12 |
13 | @RunWith(AndroidJUnit4::class)
14 | class InfoChecked {
15 |
16 | var book: MangaInfo = MangaInfo(key = "", title = "")
17 |
18 | @Before
19 | fun setup() {
20 | kotlinx.coroutines.runBlocking {
21 | book = extension.getMangaDetails(MangaInfo(key = BOOK_URL, title = BOOK_NAME), emptyList())
22 | print(book)
23 | }
24 | }
25 |
26 | @Test
27 | fun checkBookName() {
28 | assertThat(book.title.isNotBlank()).isTrue()
29 | }
30 | @Test
31 | fun checkBookAuthor() {
32 | assertThat(book.author.isNotBlank()).isTrue()
33 | }
34 |
35 | @Test
36 | fun checkBookCover() {
37 | assertThat(book.cover.isNotBlank()).isTrue()
38 | }
39 | @Test
40 | fun checkBookDescription() {
41 | assertThat(book.description.isNotBlank()).isTrue()
42 | }
43 | @Test
44 | fun checkBookGenres() {
45 | assertThat(book.genres.isNotEmpty()).isTrue()
46 | }
47 | @Test
48 | fun checkBookStatus() {
49 | assertThat(book.status != MangaInfo.UNKNOWN).isTrue()
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/source/CatalogSource.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.source
2 |
3 | import ireader.core.source.model.CommandList
4 | import ireader.core.source.model.FilterList
5 | import ireader.core.source.model.Listing
6 | import ireader.core.source.model.MangasPageInfo
7 |
8 | interface CatalogSource : ireader.core.source.Source {
9 |
10 | companion object {
11 | const val TYPE_NOVEL = 0
12 | const val TYPE_MANGA = 1
13 | const val TYPE_MOVIE = 2
14 |
15 | fun getTypeName(type: Int): String {
16 | return when (type) {
17 | TYPE_NOVEL -> "Novel"
18 | TYPE_MANGA -> "Manga"
19 | TYPE_MOVIE -> "Movie"
20 | else -> "Unknown"
21 | }
22 | }
23 | }
24 |
25 | override val lang: String
26 |
27 | suspend fun getMangaList(sort: Listing?, page: Int): MangasPageInfo
28 | suspend fun getMangaList(filters: FilterList, page: Int): MangasPageInfo
29 |
30 | fun getListings(): List
31 |
32 | fun getFilters(): FilterList
33 |
34 | fun getCommands(): CommandList
35 |
36 | fun supportsSearch(): Boolean {
37 | return getFilters().isNotEmpty()
38 | }
39 |
40 | fun supportsLatest(): Boolean {
41 | return getListings().isNotEmpty()
42 | }
43 |
44 | fun hasFilters(): Boolean {
45 | return getFilters().isNotEmpty()
46 | }
47 |
48 | fun hasCommands(): Boolean {
49 | return getCommands().isNotEmpty()
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Pull Request
2 |
3 | ## Description
4 |
5 |
6 | ## Type of Change
7 |
8 |
9 | - [ ] New extension
10 | - [ ] Extension update/fix
11 | - [ ] Core improvement
12 | - [ ] Bug fix
13 | - [ ] Documentation update
14 | - [ ] Other (please describe):
15 |
16 | ## Extension Checklist
17 |
18 |
19 | - [ ] Updated `versionCode` in `build.gradle.kts`
20 | - [ ] Added `isNsfw = true` flag if applicable
21 | - [ ] Tested the extension by compiling and running through Android Studio
22 | - [ ] Extension follows the coding standards (uses common utilities where applicable)
23 | - [ ] No hardcoded strings for common selectors (uses `SelectorConstants` if applicable)
24 | - [ ] Date parsing uses `DateParser` utility
25 | - [ ] Status parsing uses `StatusParser` utility
26 | - [ ] Proper error handling implemented
27 |
28 | ## Testing
29 |
30 |
31 | - [ ] Compiled successfully
32 | - [ ] Tested on device/emulator
33 | - [ ] Verified search functionality
34 | - [ ] Verified chapter list loading
35 | - [ ] Verified content loading
36 | - [ ] Checked for memory leaks
37 |
38 | ## Screenshots (if applicable)
39 |
40 |
41 | ## Additional Notes
42 |
43 |
44 | ## Related Issues
45 |
46 | Closes #
47 |
--------------------------------------------------------------------------------
/js-sources/webpack.config.d/bundle.js:
--------------------------------------------------------------------------------
1 | // Webpack configuration for self-contained bundle
2 | // This ensures all dependencies are bundled and the library is exported properly
3 |
4 | config.output = config.output || {};
5 |
6 | // UMD (Universal Module Definition) - works in browser, Node.js, and AMD
7 | config.output.libraryTarget = 'umd';
8 |
9 | // Global variable name when loaded in browser
10 | config.output.library = 'IReaderSources';
11 |
12 | // Ensure compatibility with both browser and Node.js
13 | config.output.globalObject = 'typeof self !== "undefined" ? self : this';
14 |
15 | // Don't externalize any dependencies - bundle everything
16 | config.externals = [];
17 |
18 | // Optimization settings for production
19 | if (config.mode === 'production') {
20 | config.optimization = config.optimization || {};
21 |
22 | // Keep function names for debugging (sources use reflection)
23 | config.optimization.minimize = true;
24 | config.optimization.minimizer = config.optimization.minimizer || [];
25 |
26 | // Configure terser to keep important names
27 | const TerserPlugin = require('terser-webpack-plugin');
28 | config.optimization.minimizer.push(
29 | new TerserPlugin({
30 | terserOptions: {
31 | keep_classnames: true,
32 | keep_fnames: true,
33 | mangle: {
34 | keep_classnames: true,
35 | keep_fnames: true
36 | }
37 | }
38 | })
39 | );
40 | }
41 |
42 | // Ensure source maps are generated
43 | config.devtool = 'source-map';
44 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/source/Source.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.source
2 |
3 | import ireader.core.source.model.ChapterInfo
4 | import ireader.core.source.model.Command
5 | import ireader.core.source.model.MangaInfo
6 | import ireader.core.source.model.Page
7 |
8 | /**
9 | * A basic interface for creating a source. It could be an online source, a local source, etc...
10 | */
11 | interface Source {
12 |
13 | /**
14 | * Id for the source. Must be unique.
15 | */
16 | val id: Long
17 |
18 | /**
19 | * Name of the source.
20 | */
21 | val name: String
22 |
23 | val lang: String
24 |
25 | /**
26 | * Returns an observable with the updated details for a manga.
27 | */
28 | suspend fun getMangaDetails(manga: MangaInfo, commands: List>): MangaInfo
29 |
30 | /**
31 | * Returns an observable with all the available chapters for a manga.
32 | */
33 | suspend fun getChapterList(manga: MangaInfo, commands: List>): List
34 |
35 | /**
36 | * Returns an observable with the list of pages a chapter has.
37 | */
38 | suspend fun getPageList(chapter: ChapterInfo, commands: List>): List
39 |
40 | /**
41 | * Returns a regex used to determine chapter information.
42 | */
43 | fun getRegex(): Regex {
44 | return Regex("")
45 | }
46 |
47 | fun getSourceKey(): String {
48 | return "$name-$lang-$id"
49 | }
50 |
51 | fun matchesId(sourceId: Long): Boolean {
52 | return this.id == sourceId
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/http/Exception.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * Base exception for HTTP-related errors.
5 | * Replaces okio.IOException for KMP compatibility.
6 | */
7 | open class HttpException(
8 | message: String? = null,
9 | cause: Throwable? = null
10 | ) : Exception(message, cause)
11 |
12 | /**
13 | * Exception thrown when a network request fails
14 | */
15 | class NetworkException(
16 | message: String? = null,
17 | cause: Throwable? = null
18 | ) : HttpException(message, cause)
19 |
20 | /**
21 | * Exception thrown when a request times out
22 | */
23 | class TimeoutException(
24 | message: String? = null,
25 | cause: Throwable? = null
26 | ) : HttpException(message, cause)
27 |
28 | /**
29 | * Exception thrown for SSL/TLS errors
30 | */
31 | class SSLException(
32 | message: String? = null,
33 | cause: Throwable? = null
34 | ) : HttpException(message, cause)
35 |
36 | /**
37 | * Exception thrown when Cloudflare bypass fails
38 | */
39 | class CloudflareBypassFailed(
40 | message: String? = "Cloudflare bypass failed",
41 | cause: Throwable? = null
42 | ) : HttpException(message, cause)
43 |
44 | /**
45 | * Exception thrown when a WebView is required
46 | */
47 | class NeedWebView(
48 | message: String? = "WebView required",
49 | cause: Throwable? = null
50 | ) : HttpException(message, cause)
51 |
52 | /**
53 | * Exception thrown when WebView is out of date
54 | */
55 | class OutOfDateWebView(
56 | message: String? = "WebView is out of date",
57 | cause: Throwable? = null
58 | ) : HttpException(message, cause)
59 |
--------------------------------------------------------------------------------
/docs/source-api/src/commonMain/kotlin/ireader/core/source/model/MangasPageInfo.kt:
--------------------------------------------------------------------------------
1 |
2 | package ireader.core.source.model
3 |
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * Paginated list of manga results.
8 | *
9 | * This class is serializable for iOS JS bridge support.
10 | */
11 | @Serializable
12 | data class MangasPageInfo(
13 | val mangas: List,
14 | val hasNextPage: Boolean
15 | ) {
16 | companion object {
17 | /**
18 | * Create empty page info
19 | */
20 | fun empty(): MangasPageInfo {
21 | return MangasPageInfo(emptyList(), false)
22 | }
23 |
24 | /**
25 | * Create page info with no next page
26 | */
27 | fun lastPage(mangas: List): MangasPageInfo {
28 | return MangasPageInfo(mangas, false)
29 | }
30 | }
31 |
32 | /**
33 | * Check if page is empty
34 | */
35 | fun isEmpty(): Boolean = mangas.isEmpty()
36 |
37 | /**
38 | * Check if page has content
39 | */
40 | fun isNotEmpty(): Boolean = mangas.isNotEmpty()
41 |
42 | /**
43 | * Get manga count
44 | */
45 | fun size(): Int = mangas.size
46 |
47 | /**
48 | * Filter mangas by predicate
49 | */
50 | fun filter(predicate: (MangaInfo) -> Boolean): MangasPageInfo {
51 | return copy(mangas = mangas.filter(predicate))
52 | }
53 |
54 | /**
55 | * Map mangas with transform
56 | */
57 | fun map(transform: (MangaInfo) -> MangaInfo): MangasPageInfo {
58 | return copy(mangas = mangas.map(transform))
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/docs/source-api/src/jsMain/kotlin/ireader/core/http/CookieSynchronizer.js.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | /**
4 | * JavaScript implementation of CookieSynchronizer.
5 | *
6 | * In browser/JS context, cookies are handled automatically by the browser.
7 | * For iOS JavaScriptCore, cookies are managed by the native layer.
8 | */
9 | actual class CookieSynchronizer : CookieSynchronizerInterface {
10 |
11 | /**
12 | * Sync cookies from WebView to HTTP client storage.
13 | * In JS context, this is a no-op as cookies are shared.
14 | */
15 | actual override fun syncFromWebView(url: String) {
16 | // In browser context, cookies are automatically shared
17 | // For iOS JavaScriptCore, the native layer handles this
18 | }
19 |
20 | /**
21 | * Sync cookies from HTTP client to WebView.
22 | * In JS context, this is a no-op as cookies are shared.
23 | */
24 | actual override fun syncToWebView(url: String) {
25 | // In browser context, cookies are automatically shared
26 | // For iOS JavaScriptCore, the native layer handles this
27 | }
28 |
29 | /**
30 | * Clear all cookies.
31 | */
32 | actual override fun clearAll() {
33 | try {
34 | js("""
35 | document.cookie.split(';').forEach(function(c) {
36 | document.cookie = c.replace(/^ +/, '').replace(/=.*/, '=;expires=' + new Date().toUTCString() + ';path=/');
37 | });
38 | """)
39 | } catch (e: Exception) {
40 | // Ignore errors in non-browser context (e.g., JavaScriptCore)
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: Feature Request
2 | description: Suggest a new feature or improvement
3 | title: "[Feature] "
4 | labels: ["enhancement"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thanks for suggesting a new feature!
10 |
11 | - type: dropdown
12 | id: feature-type
13 | attributes:
14 | label: Feature Type
15 | description: What type of feature is this?
16 | options:
17 | - New extension request
18 | - Extension improvement
19 | - Core system improvement
20 | - Developer tooling
21 | - Documentation
22 | - Other
23 | validations:
24 | required: true
25 |
26 | - type: textarea
27 | id: problem
28 | attributes:
29 | label: Problem Description
30 | description: Is your feature request related to a problem? Please describe.
31 | placeholder: I'm always frustrated when...
32 | validations:
33 | required: true
34 |
35 | - type: textarea
36 | id: solution
37 | attributes:
38 | label: Proposed Solution
39 | description: Describe the solution you'd like
40 | placeholder: I would like to see...
41 | validations:
42 | required: true
43 |
44 | - type: textarea
45 | id: alternatives
46 | attributes:
47 | label: Alternatives Considered
48 | description: Describe any alternative solutions or features you've considered
49 |
50 | - type: textarea
51 | id: additional
52 | attributes:
53 | label: Additional Context
54 | description: Add any other context or screenshots about the feature request here
55 |
--------------------------------------------------------------------------------
/docs/ADD_SOURCE_GUIDE.md:
--------------------------------------------------------------------------------
1 | # Adding a New Source
2 |
3 | ## Quick Start
4 |
5 | ```bash
6 | python scripts/add-source.py
7 | ```
8 |
9 | Answer 4 questions:
10 | 1. Source name (e.g., `NovelFull`)
11 | 2. Website URL (e.g., `https://novelfull.com`)
12 | 3. Language code (e.g., `en`)
13 | 4. Is it a Madara site? (y/n)
14 |
15 | ## What Gets Generated
16 |
17 | ### Madara Sites (zero-code)
18 | ```kotlin
19 | @MadaraSource(
20 | name = "MySite",
21 | baseUrl = "https://mysite.com",
22 | lang = "en",
23 | id = 12345L
24 | )
25 | object MySiteConfig
26 | ```
27 | That's it! KSP generates everything else.
28 |
29 | ### Regular Sites (with KSP annotations)
30 | ```kotlin
31 | @Extension
32 | @AutoSourceId(seed = "MySite")
33 | @GenerateFilters(title = true, sort = true, sortOptions = ["Latest", "Popular"])
34 | @GenerateCommands(detailFetch = true, chapterFetch = true, contentFetch = true)
35 | abstract class MySite(deps: Dependencies) : SourceFactory(deps = deps) {
36 | // Just update the CSS selectors
37 | }
38 | ```
39 |
40 | ## Finding CSS Selectors
41 |
42 | 1. Open the website in Chrome/Firefox
43 | 2. Right-click any element → "Inspect"
44 | 3. Find the class name or tag
45 | 4. Test: `document.querySelector(".your-selector")`
46 |
47 | ### Common Selectors
48 |
49 | | Element | Try These |
50 | |---------|-----------|
51 | | Novel card | `.novel-item`, `.book-item`, `.post` |
52 | | Title | `h1`, `.title`, `h3.name` |
53 | | Cover | `img`, `.cover img` |
54 | | Chapters | `.chapter-list li`, `ul.chapters a` |
55 | | Content | `.chapter-content p`, `.text p` |
56 |
57 | ## Build & Test
58 |
59 | ```bash
60 | ./gradlew :sources:en:mysource:assembleDebug
61 | ```
62 |
--------------------------------------------------------------------------------
/docs/source-api/src/iosMain/kotlin/ireader/core/http/HttpClients.ios.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | import io.ktor.client.*
4 | import io.ktor.client.engine.darwin.*
5 | import io.ktor.client.plugins.*
6 | import io.ktor.client.plugins.contentnegotiation.*
7 | import io.ktor.serialization.kotlinx.json.*
8 | import kotlinx.serialization.json.Json
9 |
10 | /**
11 | * iOS implementation of HttpClients using Darwin engine
12 | */
13 | actual class HttpClients : HttpClientsInterface {
14 |
15 | actual override val browser: BrowserEngine = BrowserEngine()
16 | actual override val config: NetworkConfig = NetworkConfig()
17 | actual override val sslConfig: SSLConfiguration = SSLConfiguration()
18 | actual override val cookieSynchronizer: CookieSynchronizer = CookieSynchronizer()
19 |
20 | actual override val default: HttpClient = HttpClient(Darwin) {
21 | engine {
22 | configureRequest {
23 | setAllowsCellularAccess(true)
24 | }
25 | }
26 |
27 | install(HttpTimeout) {
28 | requestTimeoutMillis = config.connectTimeoutSeconds * 1000
29 | connectTimeoutMillis = config.connectTimeoutSeconds * 1000
30 | socketTimeoutMillis = config.readTimeoutMinutes * 60 * 1000
31 | }
32 |
33 | install(ContentNegotiation) {
34 | json(Json {
35 | ignoreUnknownKeys = true
36 | isLenient = true
37 | })
38 | }
39 |
40 | install(UserAgent) {
41 | agent = config.userAgent
42 | }
43 | }
44 |
45 | actual override val cloudflareClient: HttpClient = default
46 | }
47 |
--------------------------------------------------------------------------------
/test-extensions/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.library")
3 | kotlin("android")
4 | }
5 |
6 | android {
7 | compileSdk = Config.compileSdk
8 | namespace = "ireader.test.extensions"
9 | defaultConfig {
10 | minSdk = Config.minSdk
11 | targetSdk = Config.targetSdk
12 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
13 | }
14 | compileOptions {
15 | sourceCompatibility = JavaVersion.VERSION_21
16 | targetCompatibility = JavaVersion.VERSION_21
17 | }
18 | kotlinOptions {
19 | jvmTarget = "21"
20 | }
21 | }
22 |
23 | // Extension dependency is configured dynamically by test scripts
24 | // To test manually:
25 | // 1. Uncomment ONE of the lines below (or add your own)
26 | // 2. Run: ./gradlew :test-extensions:test
27 | //
28 | // Example extensions:
29 | // implementation(project(":extensions:v5:en:novelbuddy"))
30 | // implementation(project(":extensions:individual:en:mylovenovel"))
31 |
32 | dependencies {
33 | // Core dependencies - always included
34 | implementation(project(":multisrc"))
35 | implementation(libs.bundles.common)
36 | implementation(libs.bundles.commonTesting)
37 | testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.2")
38 | implementation(project(":annotations"))
39 | implementation(project(":compiler"))
40 |
41 | // Extension to test - ADD YOUR EXTENSION HERE
42 | // The test scripts will automatically configure this
43 | // For manual testing, uncomment and modify one of these:
44 | // implementation(project(":extensions:v5:en:novelbuddy"))
45 | // implementation(project(":extensions:individual:en:mylovenovel"))
46 | }
47 |
--------------------------------------------------------------------------------
/multisrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 |
2 |
3 | plugins {
4 | id("com.android.library")
5 | kotlin("android")
6 | }
7 |
8 | android {
9 | compileSdk = Config.compileSdk
10 | namespace = "ireader.test_extensions"
11 | defaultConfig {
12 | minSdk = Config.minSdk
13 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
14 | }
15 | compileOptions {
16 | sourceCompatibility = JavaVersion.VERSION_21
17 | targetCompatibility = JavaVersion.VERSION_21
18 | }
19 | kotlin {
20 | compilerOptions {
21 | jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21)
22 | }
23 | }
24 | }
25 |
26 | dependencies {
27 | val libs = project.extensions.getByType()
28 | .named("libs")
29 |
30 | // Core dependencies (KMP-compatible)
31 | compileOnly(libs.findLibrary("ireader-core").get()) { isChanging = true }
32 | compileOnly(libs.findLibrary("stdlib").get())
33 |
34 | // HTML parsing (KMP)
35 | compileOnly(libs.findLibrary("ksoup").get())
36 |
37 | // Date/Time (KMP)
38 | compileOnly(libs.findLibrary("kotlinx-datetime").get())
39 |
40 | // HTTP client (KMP)
41 | compileOnly(libs.findLibrary("ktor-core").get())
42 | compileOnly(libs.findLibrary("ktor-contentNegotiation").get())
43 | compileOnly(libs.findLibrary("ktor-serialization").get())
44 |
45 | // Android-specific Ktor engines
46 | compileOnly(libs.findLibrary("ktor-cio").get())
47 | compileOnly(libs.findLibrary("ktor-android").get())
48 | compileOnly(libs.findLibrary("ktor-okhttp").get())
49 |
50 | compileOnly(project(":annotations"))
51 | compileOnly(project(":common"))
52 | }
53 |
--------------------------------------------------------------------------------
/test-extensions/src/test/java/ireader/app/tests/ChapterChecker.kt:
--------------------------------------------------------------------------------
1 | package ireader.app.tests
2 |
3 | import com.google.common.truth.Truth.assertThat
4 | import ireader.app.BOOK_NAME
5 | import ireader.app.BOOK_URL
6 | import ireader.app.extension
7 | import ireader.core.source.model.ChapterInfo
8 | import ireader.core.source.model.MangaInfo
9 | import org.junit.Before
10 | import org.junit.Test
11 |
12 | class ChapterChecker {
13 | var chepters: List = emptyList()
14 | @Before
15 | fun setup() {
16 | kotlinx.coroutines.runBlocking {
17 | chepters = extension.getChapterList(MangaInfo(key = BOOK_URL, title = BOOK_NAME), emptyList())
18 | print(chepters)
19 | }
20 | }
21 |
22 | @Test
23 | fun `check whether chapter list is empty `() {
24 | assertThat(chepters.isNotEmpty()).isTrue()
25 | }
26 | @Test
27 | fun `check whether chapters has name `() {
28 | assertThat(chepters.any { chapterInfo -> chapterInfo.name.isNotBlank() }).isTrue()
29 | }
30 | @Test
31 | fun `check whether chapters has keys `() {
32 | assertThat(chepters.any { chapterInfo -> chapterInfo.key.isNotBlank() }).isTrue()
33 | }
34 | @Test
35 | fun `check whether chapters has dateUpload `() {
36 | assertThat(chepters.any { chapterInfo -> chapterInfo.dateUpload != 0L }).isTrue()
37 | }
38 | @Test
39 | fun `check whether chapters has number `() {
40 | assertThat(chepters.any { chapterInfo -> chapterInfo.number != -1f }).isTrue()
41 | }
42 | @Test
43 | fun `check whether chapters has translator `() {
44 | assertThat(chepters.any { chapterInfo -> chapterInfo.scanlator.isNotBlank() }).isTrue()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/extensions/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
16 |
17 |
18 |
22 |
23 |
26 |
29 |
32 |
35 |
38 |
41 |
44 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/docs/source-api/src/androidMain/kotlin/ireader/core/http/AndroidCookieJar.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | import android.webkit.CookieManager
4 | import okhttp3.Cookie
5 | import okhttp3.CookieJar
6 | import okhttp3.HttpUrl
7 |
8 | class AndroidCookieJar : CookieJar {
9 |
10 | private val manager = CookieManager.getInstance()
11 |
12 | override fun saveFromResponse(url: HttpUrl, cookies: List) {
13 | val urlString = url.toString()
14 |
15 | cookies.forEach { manager.setCookie(urlString, it.toString()) }
16 | }
17 |
18 | override fun loadForRequest(url: HttpUrl): List {
19 | return get(url)
20 | }
21 |
22 | fun get(url: HttpUrl): List {
23 | val cookies = manager.getCookie(url.toString())
24 |
25 | return if (cookies != null && cookies.isNotEmpty()) {
26 | cookies.split(";").mapNotNull { Cookie.parse(url, it) }
27 | } else {
28 | emptyList()
29 | }
30 | }
31 |
32 | fun remove(url: HttpUrl, cookieNames: List? = null, maxAge: Int = -1): Int {
33 | val urlString = url.toString()
34 | val cookies = manager.getCookie(urlString) ?: return 0
35 |
36 | fun List.filterNames(): List {
37 | return if (cookieNames != null) {
38 | this.filter { it in cookieNames }
39 | } else {
40 | this
41 | }
42 | }
43 |
44 | return cookies.split(";")
45 | .map { it.substringBefore("=") }
46 | .filterNames()
47 | .onEach { manager.setCookie(urlString, "$it=;Max-Age=$maxAge") }
48 | .count()
49 | }
50 |
51 | fun removeAll() {
52 | manager.removeAllCookies {}
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/docs/source-api/src/desktopMain/kotlin/ireader/core/http/PersistentCookieJar.kt:
--------------------------------------------------------------------------------
1 | package ireader.core.http
2 |
3 | import ireader.core.prefs.PreferenceStore
4 | import kotlinx.coroutines.runBlocking
5 | import okhttp3.Cookie
6 | import okhttp3.CookieJar
7 | import okhttp3.HttpUrl
8 |
9 | class PersistentCookieJar(preferencesStore: PreferenceStore) : CookieJar {
10 |
11 | val store = PersistentCookieStore()
12 |
13 | override fun saveFromResponse(url: HttpUrl, cookies: List) {
14 | val commonCookies = cookies.map { it.toCommonCookie() }
15 | runBlocking {
16 | store.addCookies(url.toString(), commonCookies)
17 | }
18 | }
19 |
20 | override fun loadForRequest(url: HttpUrl): List {
21 | return runBlocking {
22 | store.getCookies(url.toString()).mapNotNull { it.toOkHttpCookie(url) }
23 | }
24 | }
25 |
26 | private fun Cookie.toCommonCookie(): ireader.core.http.Cookie {
27 | return ireader.core.http.Cookie(
28 | name = name,
29 | value = value,
30 | domain = domain,
31 | path = path,
32 | expiresAt = expiresAt,
33 | secure = secure,
34 | httpOnly = httpOnly,
35 | persistent = persistent
36 | )
37 | }
38 |
39 | private fun ireader.core.http.Cookie.toOkHttpCookie(url: HttpUrl): Cookie? {
40 | return Cookie.Builder()
41 | .name(name)
42 | .value(value)
43 | .domain(domain)
44 | .path(path)
45 | .apply {
46 | if (expiresAt > 0) expiresAt(expiresAt)
47 | if (secure) secure()
48 | if (httpOnly) httpOnly()
49 | }
50 | .build()
51 | }
52 | }
53 |
--------------------------------------------------------------------------------