Compare commits

...

1912 Commits

Author SHA1 Message Date
devi 5fc0327df7 Clean Broken Fr sources 6 months ago
Koitharu df1cab3f9d
[Komiic] Refactor and fix formatting 6 months ago
skepsun 8effefcd50
Komiic (zh): Add source (#2319)
* Komiic: Add source

* Increment total from 1255 to 1256
6 months ago
dragonx943 7f98a7fb5c
chore (site/vi): Update sources domain 6 months ago
Draken a39721eaf7
DoujinDesu.tv: Fix search query, add author search support (#2326) 6 months ago
Draken 65c61c0c01
Mangafire: Get/generate vrf for ajax + Add referer, fix access denied (#2310)
Co-authored-by: Vicente <vicente@per>
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
Co-authored-by: Koitharu <nvasya95@gmail.com>
Co-authored-by: epikaigle444 <epikaigle444@gmail.com>
Co-authored-by: Max Bethmann <bethibande@gmail.com>
Co-authored-by: vtorres-t <73935362+vtorres-t@users.noreply.github.com>
Co-authored-by: epikaigle444 <epikaigle444@gmail.com>
Co-authored-by: Naga <94557604+NagaYZ@users.noreply.github.com>
7 months ago
dragonx943 ac977a6cbb
HentaiVN.su: Small fixes, remove broken annotation 7 months ago
Draken 8908031eee
Rawkuma: Update domain + Add broken annotation 7 months ago
Draken 4141a8f429
MimiHentai: Update API suffix, fix content not found or removed 7 months ago
Draken ad7f1eddca
Rawkuma: Use old domain, temporary fix nothing found 7 months ago
Draken 472ddab27c
GocTruyenTranhVui: Update token, fix chapters list 7 months ago
Draken 560cf63a5e
CManga: Fix missing content in getPages 7 months ago
dragonx943 d4e1acd515
Cứu Truyện: Allow to choose cover quality in source setting 7 months ago
Draken f66b97edc1
HentaiVN.su: Add source (#2295) 7 months ago
epikaigle444 a084909507
MangaMoins: Add source (#2284)
Co-authored-by: epikaigle444 <epikaigle444@gmail.com>
Co-authored-by: Koitharu <nvasya95@gmail.com>
Co-authored-by: dragonx943 <premieregirl26@gmail.com>
7 months ago
vtorres-t 58357a3745
chore: Add broken annotation for some sources (#2275)
Co-authored-by: Vicente <vicente@per>
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
Co-authored-by: Koitharu <nvasya95@gmail.com>
7 months ago
Max Bethmann 8a6a529023
Mangakakalot fix getListPage (#2231) (#2285) 7 months ago
UC-01-Zer0-ID 1b9ff47ab3
ScanHentai.Menu: Update domain (#2277) 7 months ago
Draken 229f321299
Cứu Truyện: Remove broken 7 months ago
Draken a5233ede48
MangaGeko: Fix missing images in getPages (#2267) 7 months ago
dragonx943 d9025dbf81
AtsuMoe: Delete source that were never usable 7 months ago
vtorres-t 2cc0af17d5
chore: Update domains (#2264)
Co-authored-by: Vicente <vicente@per>
7 months ago
Hoàng Phi Hùng a3f384e3b3
Ổ Truyện: Fix no value for _id in getListPage (#2259)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
7 months ago
nakrovati 5882a5d54d
chore: update domains (#2255)
Close #2249, #2234, #2228, #2127
7 months ago
Koitharu f62809407b
[Madaradex] Fixes 7 months ago
Draken 14c0eaf273
Truyện Tranh Full: Add source (#2252) 7 months ago
Draken 0817a82f36
MadaraDex: Fix access denied errors in getPages
Co-authored-by: Koitharu <nvasya95@gmail.com>
7 months ago
Draken 727e326379
Cứu Truyện: Add broken 7 months ago
Harsh Dev Jha ccaf52b74e
AtsuMoe: Add source (close #2187)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
7 months ago
Harsh Dev Jha fc988cdd1e
ReadManhua: Change source name to Resetscans + Update domain
Close #2201
7 months ago
Harsh Dev Jha cb2e9841ae
YuriLab: Update domain (close #2233) 7 months ago
Draken 775063d36f
MimiHentai: Fix intercept, scrambled images in getPages (#2237) 7 months ago
Naga 86b8642456
XoxoComics: Fixes (#2229) 7 months ago
Draken 185d9b6e6e
MyReadingManga: Fix order when query is empty 7 months ago
dragonx943 e3495fc41f
MadaraDex: Add referer 7 months ago
dragonx943 d8e47b2b52
HiveComic: Remove broken 7 months ago
dragonx943 be64bf2328
MyReadingManga: Fix locale query + tags in getDetails 7 months ago
dragonx943 d85bb64b1c
HiveComic: Use API to fix content not found in getListPage 7 months ago
dragonx943 de2289eb75
FreeMangaTop: Fix access denied in getPages 7 months ago
dragonx943 7cb069d108
MyReadingManga: Improve search query with more filters 7 months ago
dragonx943 8a18df148d
ComicK: Add exception + Fix build for ci 7 months ago
dragonx943 9007852d31
MyReadingManga: Add broken 7 months ago
dragonx943 60e451303e
MadaraParser: Add some values + Fix warn 7 months ago
dragonx943 170a11ae26
ci: Update dependencies for actions 7 months ago
dragonx943 642ac08338
MadaraDex: Fix access denied in getPages 7 months ago
Draken ff256cb3ec
CBHentai: Add state to manga details 7 months ago
Hoàng Phi Hùng 08d46988a3
LXManga: Add referer, fix not found error in getPages (#2220) 7 months ago
Draken a3abc81dfc
Dâm Cô Nương: Fix chapter list not found (#2210) 7 months ago
dragonx943 41a4c90e75
ComicK: Add broken 7 months ago
dragonx943 23fdfbbef9
Madtheme: Fix warns 8 months ago
dragonx943 a4f41e41f8
MTLParser: Fix warns 8 months ago
dragonx943 db586045d7
VioletScans: Fix warns 8 months ago
dragonx943 e636eda840
Remove unused import + Fix warns 8 months ago
dragonx943 406992a6c9
DragonTranslation.org: Add source + Fix warns 8 months ago
Koitharu 30f97c5c82
Improve OkHttp coroutines integration 8 months ago
Koitharu b9ecdb2db6
[Desu] Fixes 8 months ago
Koitharu e044463f91
[Grouple] Fixes 8 months ago
Koitharu a93c760133
Fix tests running 8 months ago
dragonx943 f2b6cab251
site/vi: Update sources domain 8 months ago
dragonx943 07225568ee
BatCave: Add referer, close #2176 8 months ago
dragonx943 860dd32a53
Góc Truyện Tranh Vui: Fix warns 8 months ago
dragonx943 74f5891ffe
Góc Truyện Tranh Vui: Update token, fix chapters list 8 months ago
Draken 7bae2602c7
Merge pull request #2174 from nakrovati/build/improve-buildsrc-gradle-config
Use libs.toml.versions and `kotlin-dsl` in buildSrc build.gradle.kts
8 months ago
Draken 4c697b6406
Jitpack: Add configuration file to use JVM 17 8 months ago
Daniil Zhuravlev b61698d405 chore: use libs.toml.versions in buildSrc 8 months ago
Draken 260e5acd5c
Cứu Truyện: Change pageSize to 24, remove unused state 8 months ago
Koitharu e599f431c5
Merge pull request #2172 from nakrovati/build/remove-gradle-from-buildsrc 8 months ago
Daniil Zhuravlev 0ec4fe42c1 build: migrate to gradle 9 8 months ago
Daniil Zhuravlev c9e4c7273e build: remove gradle from buildSrc dir 8 months ago
Draken 3e2515ac6a
Cứu Truyện: Improve search query with multiple tags (#2171) 8 months ago
Koitharu fe5534b006
Remove DUMMY parser 8 months ago
Sergey Shumov 6ca07aeff7
ZenManga: update site domain (#2168) 8 months ago
Koitharu 19567f9642
Update dependencies 8 months ago
Koitharu 4a854c7a23
Update docs 8 months ago
Draken 312b617b95
Cứu Truyện: Use another publicUrl for better redirection 8 months ago
Koitharu 0a0a869e88
Merge pull request #2166 from nakrovati/refactor/migrate-gradle-scripts-to-kotlin-dsl 8 months ago
Hoàng Phi Hùng 8573921243
GocTruyenTranhVui + KuroNeko: Fixes (#2165)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
8 months ago
Draken b8d3124227
Update Gradle 8.7 to 8.13 (#2133) 8 months ago
Sergey Shumov 14e23694fd
FIx ZenManga manga page url (#2164) 8 months ago
Daniil Zhuravlev 7e25002dba refactor(build): migrate to Kotlin DSL and version catalog 8 months ago
Daniil Zhuravlev a94d9bf8b8 chore: update .editorconfig 8 months ago
nakrovati 7430c7386e
Mark broken parsers and update several domains (#2163)
* chore: update outdated domains

* chore: mark broken parsers
8 months ago
Naga 5f99e694c8
MediocreToons: Fix chapter order (#2162) 8 months ago
dragonx943 25b23854d8
Góc Truyện Tranh Vui: Add source 8 months ago
dragonx943 45d39fe94d
KuroNeko: Add delay to avoid rate limit 8 months ago
dragonx943 21b63c1d77
TruyenQQ: Add delay to avoid rate limit 8 months ago
Draken f64ce0f4a2
Dâm Cô Nương: Remove redundant escape character (#2160) 8 months ago
Naga b3e7d8e8d5
SussyScan: Fix CDN url in getPages (#2156) 8 months ago
Hoàng Phi Hùng 8a147dbdd3
Update getpage for DamCoNuong parser (#2149)
* Update DamCoNuong.kt

* Update DamCoNuong.kt to fix "Can't find fallbackUrls"

* Update DamCoNuong.kt
8 months ago
Hoàng Phi Hùng 5a039acf82
Dâm Cô Nương: Fix unsupported format in getPages (#2148) 8 months ago
Draken e9469ae0e0
Dâm Cô Nương: Update domain 8 months ago
Naga 48cfc3347b
TraduccionesAmistosas: Update domain (#2145) 8 months ago
nakrovati 1566b807b7
Update domains and correct the source language (#2132) 8 months ago
Naga 8bed45dd2f
Komikcast: Fix SortOrder (#2124) 8 months ago
Waranim 36c3a88d63
ZenManga: Add source (#2123) 8 months ago
Koitharu 16f5129b69
[LibSocial] Fix Not found error 8 months ago
dragonx943 674290d029
MimiHentai: Add Broken, need to open WebView to read 8 months ago
bivaly 6cb6873fcb
GhostScan: Update site domain (#2118) 8 months ago
fuckpdf 705a684cc2
UzayManga: Fix getPages (#2086) 8 months ago
Naga de76ccf753
Weeb Central: Update tags selector (#2087) 8 months ago
Naga cbd74dd7df
AnimeSama: Add source (#2080)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
9 months ago
dragonx943 7f39798a8c
Yuri Garden: Add intercept, apply unscrambleImage for getPages 9 months ago
Naga 31999d97a0
Hentaman: Add source (#2078) 9 months ago
dragonx943 fd0df2414e
MimiHentai: Use another UserAgent key, fix query in getListPage 9 months ago
Naga a93140fc36
MediocreToons: Add source (#2077) 9 months ago
Naga 7c264b6af7
SussyScan: Fixes (#2076) 9 months ago
Naga 134656b835
InManga: Add source (#2073) 9 months ago
Naga 8964bb1584
Manhwalist.org: Update domain (#2072) 9 months ago
Naga 8bee71342b
PhenixScans: Fixes (#2071) 9 months ago
Naga a4824a3582
RaijinScans: Fixes (#2070) 9 months ago
Draken 6fb4cc608c
ci: Update dependencies for actions (#2065) 9 months ago
dragonx943 12aa4479cd
ci: Update dependencies for actions 9 months ago
Draken bfdb10d002
Dâm Cô Nương: Fix exclude tags, CSS selectors for parseMangaList (#2064) 9 months ago
Naga f61f5329e3
ArabsHentai: Add source (#2063) 9 months ago
dragonx943 0477fe0659
site/vi: Update sources domain + Favicons 9 months ago
dragonx943 6b3329436f
Làng Geek: Set CHROME_DESKTOP as default UserAgent key 9 months ago
Draken 35fd5042a7
Làng Geek: Fix CSS selector for getPages, attempt 1 9 months ago
Naga d366628a20
HeyToon: Add source (#2060) 9 months ago
Naga 5483453f35
RocksManga: Update site structure (#2059) 9 months ago
Naga 02ac1cb896
Ikiru: Update site structure (#2058) 9 months ago
dragonx943 1563783afe
ci: Use another module for Gradle, try to improve build times 9 months ago
Naga f5436977a9
MangaGg: Fix chapter list (#2056) 9 months ago
dragonx943 b58cf32b7a
ci: Upgrade actions version and split steps 9 months ago
dragonx943 38025a6b35
ci: Change Java version, use compileKotlin instead of assemble 9 months ago
Draken 96a2d9e0ac
ci: Add workflow to automatically test and compile parsers 9 months ago
Draken c9ebbfa1bf
LxManga: Throw Exception for restricted content 9 months ago
Naga 91ec95448c
MyReadingManga: Update site structure (#2054) 9 months ago
Naga b5512e7574
[KdtScan] Update domain + Fixes (#2050)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
Co-authored-by: dragonx943 <premieregirl26@gmail.com>
9 months ago
Naga 8bc7480d84
[PerfScan] Update domain + Fixes (#2047) 9 months ago
Naga db9a7161f4
[PoseidonScans] Add source (#2042)
Close #1641
9 months ago
Koitharu 74d0951d3d
[Remanga] Fix chapters parsing 9 months ago
Koitharu 91d5eff20a
[Remanga] Migrate to api v2 9 months ago
Draken 54d6b2a2bb
[Làng Geek] Add source (#2041) 9 months ago
Naga 66cd27a673
[Manhastro] Fix chapter list (#2040)
Close #1376 #1362
9 months ago
Naga 137490001a
[HuntersScan] Update domain and fixes (#2039)
Close #2038
9 months ago
Naga c85dac5e0e
[ManhuaScan] Fixes (#2037)
Close #1418 #1730 #1922
9 months ago
dragonx943 9a9102fa33
[LXManga] Small fix for scanlator 9 months ago
Naga 0d31adb20b
[HentaiRead] Fix search, tags, rating (#2036)
Close #1945
9 months ago
Draken df7f2d1a89
[LXManga] Fix state + scanlator 9 months ago
dragonx943 eaca421132
[GocTruyenTranh] Use API instead of fixed tags function 9 months ago
Naga 5f1f2df7f0
[ManhwaBreakup] Add source (#2034)
Close #1967
9 months ago
Naga ed21a5b397
[DoujinHentai] Fixes (#2030)
Close #1983
9 months ago
Draken 39703e3ebb
[Ổ Truyện] Small fixes (#2027) 9 months ago
Naga 14cff0d651
[RokariComics] Add source (#2023)
Close #2008
9 months ago
Naga af89f42251
[Komiku] Fixes (#2022)
Close #1986
9 months ago
Naga 036ce155ae
[FlameComics] Fix image loading (#2020)
Close #2014
9 months ago
Naga 177d014b13
[Kiryuu] Update domain (#2021) 9 months ago
dragonx943 7c74ff6177
[EleceedTurkiye] Fix warn 9 months ago
Naga b139586df5
[KomikIndo] Update domain and structure (#2017)
Close #723 #1466
9 months ago
dragonx943 15c7f97432
[HentaiVN.plus] Add author search 9 months ago
Naga f22c8f9e7c
[TemakiMangas] Small fixes
Close #1997
9 months ago
dragonx943 b24baf7d93
[site/vi] Update domain 9 months ago
Draken 98e0f6cd6c
[Ổ Truyện] Add source (#2012) 9 months ago
Naga e7316b5cd0
[InovaScanManga] Remake parser (Close #1988) 9 months ago
Naga 33a0a68ac7
[Klz9] Fixes (close #1949, #1433, #1455) 9 months ago
Koitharu a83baf4c12
[Grouple] Handle restricted state 9 months ago
Koitharu 307f2090f6
[LibSocial] Improvements 9 months ago
Naga e73d636979
[KomikCast] Fixes (Close #1995) 9 months ago
Naga 77fb7270ce
[RimuScans] Update domain (Close #2005) 9 months ago
Koitharu 9d35f26252
Fix warnings (refactor) 9 months ago
Koitharu 4eeb879451
Fix build 9 months ago
Koitharu 06aa701ab1
Update MangaState enum 9 months ago
Draken 3c55b68beb
[EleceedTurkiye] Add source (#2006) 9 months ago
dragonx943 63f37d159a
[MimiHentai] Fix sort orders 9 months ago
dragonx943 cb74c43f71
[MimiHentai] Temporarily remove "POPULARITY_MONTH" 9 months ago
dragonx943 798b7570fe
[MimiHentai] Update favicon 9 months ago
Draken 9197b82e0d
[KuroNeko] Rename source 9 months ago
Draken ad45480439
[BatCave] Small fix for getDetails (#1994) 9 months ago
Draken 9493ff1630
[MyReadingManga] Fix tags + search 9 months ago
Draken 8ee46ad2d1
[TruyenHentai18] Small tweak + Refactor (#1992) 9 months ago
dragonx943 ba1b35522c
[TruyenHentai18] Fix attempt 1 9 months ago
dragonx943 41c37c025a
[site/vi] Update domain 9 months ago
Draken 9995657da5
[TruyenHentai18] Fix getPages 9 months ago
dragonx943 1569275cd4
[TruyenHentai18] Remove unused import + Broken 9 months ago
dragonx943 352d998680
[TruyenHentai18] Fix attempt 1 9 months ago
Draken d0b2002a1b
[KuroNeko + CuuTruyen] Small fixes (#1989) 9 months ago
Koitharu 5962efb7df
Update parsers structure (again) 9 months ago
Draken 8b18d1f0f8
[Com-X] Update domain 10 months ago
Draken 97ca214815
[DoujinDesu.click] Update domain 10 months ago
Draken 5d9b56296b
[HentaiVn.buzz + YuriGarden] Fixes (#1968)
* [HentaiVn.buzz] Fix "Nothing found" errors

* [YuriGarden] Add author search support + Small fixes
10 months ago
Batu 66f25c30fc
[Gafeland] Add source (#1963)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
10 months ago
dragonx943 fd197de86a
[LXManga] Fix CSS selector in altTitles 10 months ago
dragonx943 5f7478b33c
[LXManga] Store altTitles + Revert CSS selector 10 months ago
dragonx943 b058477b67
[LXManga] Small fix for altTitles 10 months ago
dragonx943 149d9e5a81
[LXManga] Remove not null constraint for altTitles 10 months ago
Batu 6420def95a
[site/tr] Update domain + Add Broken (#1956)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
10 months ago
fuckpdf 5dae26ce0b
[MangaTilkisi] Add source (#1940)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
10 months ago
Draken b5fddadb18
[LXManga] Small fixes for getDetails (#1955) 10 months ago
Draken 613d1cd1a1
[PhenixScans] Fix API path 10 months ago
fuckpdf a6a72064bb
[TenshiManga] Add source (#1933)
* [UzayManga + ElderManga] Update order

* [TenshiManga] Add source

---------

Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
10 months ago
dragonx943 73c0a4f606
[YuriGarden] Revert scanlator for getDetails 10 months ago
Draken 1d2ee20d17
[YuriGarden] Fixes (#1934) 10 months ago
dragonx943 67b556c2eb
[YuriGarden] Small fixes 10 months ago
Draken 3962c87e1d
[YuriGarden] Fix line 10 months ago
dragonx943 e6ca455254
[YuriGarden] Fix order / path + Remove rating 10 months ago
Naga d481ff416f
[Flamecomics] Fix search and genre fitlering (#1923) 10 months ago
Draken b0236e492d
[YuriGarden] Add source (#1924) 10 months ago
dragonx943 497321bfa8
[KuroNeko] Add author search support 10 months ago
Draken eff26f6303
[CBHentai] Add author search + Fix duplicate tags (#1918) 10 months ago
Crono b5bd5b8cdf
[Manhastro + LuraToon] Update domain (#1915) 10 months ago
Draken 4cc15daba3
[Koharu] Add Broken 10 months ago
Koitharu 248d51321a
[Koharu] Refactor UserAgent 10 months ago
Koitharu 7cb9a264de
[LibSocial] Allow to change domain 10 months ago
Draken b58e9ff328
[TopTruyen] Update domain 10 months ago
dragonx943 958e0bed5c
[site/vi] Update domain 10 months ago
ViAnh e8d4143d72
[Koharu] Fix getPages (#1914)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
10 months ago
Naga 079b2346f1
[Webtoon] Fixes (#1912) 10 months ago
Iwakurra 553615b3ff
[HentaiLib] Update domain + allow user to set custom (#1908) 10 months ago
Koitharu f8992457dc
Update domains 10 months ago
Draken 06a9209b9e
[DexHentai] Fix getListPage (close #1901) 10 months ago
Draken 4c0b873013
[Hentalk] Update domain 10 months ago
Koitharu dba7f46f73
[Remanga] Update domain (#1891) 10 months ago
Koitharu 8665186426
[LibSocial] Fix chapters branches 10 months ago
Koitharu b5c6a94311
[ExHentai] Fix pagination 10 months ago
Koitharu 1540021f6f
[Remanga] Fix parsing manga without covers 10 months ago
Koitharu 0e1247476e
[LibSocial] Fix tags in details 10 months ago
SrCrono bf83909349
[FlowerManga] Update domain (#1890) 10 months ago
Draken e398a01f14
[AgsComics] Fix cover images
Close #1886
10 months ago
Draken 0e77938ade
[NewTruyen] Fix getPages
Solved #1604
10 months ago
dragonx943 a4a9a1cf67
[NewTruyen] Fix getPages 10 months ago
Draken 3f9544fdb8
[MimiHentai] Small fix for exclude tags 10 months ago
dragonx943 bd30fc35a1
[WestManga] Add broken 11 months ago
Draken b6f82cc282
Merge pull request #1884 from dragonx943/dev
[1stKissManga] Update domain + Small fix
11 months ago
dragonx943 9222079e98
[1stKissManga] Update domain + Small fix 11 months ago
Draken 49158b99d5
[CManga] Fix logic 11 months ago
Draken 3fe61a09b7
[CManga] Improvement getPages 11 months ago
Draken b82ad1356e
[CManga] Fix chap number (#1880) 11 months ago
dragonx943 b825d06b22
[CManga] Fix chap number 11 months ago
Draken 28f5856f9e
[Desu] Update domain 11 months ago
Draken 19751674ee
[TruyenHentai18] Add broken 11 months ago
Draken a78afba15e
[KuroNeko + TruyenHentai18] Fixes (#1879)
Solved #1683 #1604
11 months ago
Draken 0e946cf84c
[site/vi] Update sources domain (#1874) 11 months ago
Draken 6cd29603ac
[CBHentai] Fix duplicated tags (#1873) 11 months ago
Draken d94cf53624
[MimiHentai] Add exclude tags support (#1872) 11 months ago
Draken c81628c027
[YuriGarden] Removal (#1866, close #1863) 11 months ago
Koitharu bcde8ef2a2
Fix build 11 months ago
Koitharu 66df6caa67
[ExHentai] Improvements 11 months ago
Draken 08e53b2f5f
[MimiHentai] Small fix 11 months ago
Draken 7c83730ad0
[MimiHentai] Fix getListPage 11 months ago
dragonx943 d2a026c054
[MimiHentai] Remove state 11 months ago
dragonx943 faf8f6ffd7
[MimiHentai] Fix getListPage 11 months ago
Draken 6c372548db
[site/vi] Update sources domain (#1861) 11 months ago
Draken 0029697350
Fix line 11 months ago
dragonx943 bdcf27b955
[site/vi] Update sources domain 11 months ago
Draken bb7ac26c0c
[MimiHentai] Small fixes (#1860) 11 months ago
Draken 7b42317996
[MimiHentai] Add Broken 11 months ago
Draken 103b0e7eb6
[ZinChanManga] Update domain
Solved https://github.com/KotatsuApp/kotatsu-parsers/issues/1835
11 months ago
Draken fa80598e7c
[DamCoNuong] Update domain
Solved a task in #1604
11 months ago
Draken 3cb5367028
[madtheme/en + wpcomics/vi] Small fixes (#1830)
* [WpComics] Split NetTruyenX + Small improvements

* [MangaBuddy] Add subdomain

* [MangaPuma] Add subdomain

* [MangaForest] Add subdomain

* [MangaCute] Add subdomain

* [Manhuagui] Change UA to `CHROME_DESKTOP`

* [Mangaxyz] Add subdomain
11 months ago
Zhifan Ye 9417d1f973
[Manhuagui] Fix UA and parse errors (#1834) 11 months ago
mozzaru 173d30737b
[MgKomik] Update domain (#1828) 11 months ago
Draken 0db45877b7
[SolarMTL] Add source (#1826) 11 months ago
Draken e7fac812a7
[SnowMTL] Add source (#1823)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
11 months ago
Draken ab26e6b3d0
[TrueManga] Update domain 11 months ago
Draken baafc29967
[MimiHentai] Small fix 11 months ago
Draken b0bd3e99a4
[MimiHentai] Small changes (#1818)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
11 months ago
Draken 6bf377a654
[NhatTruyenVN] Fix list chaps (#1816)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
11 months ago
Zhifan Ye 3d78b9003d
[Manhuagui] Add source (#1812)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
11 months ago
Draken f57722ec54
[Com-X] Small changes (#1814)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
11 months ago
Koitharu 9558a34b00
[Koharu] Fixes 11 months ago
Draken 522e3af41d
[Koharu] Fixes (#1811)
* [Koharu] Debug attempt 1

* [Koharu] Debug attempt 2

* [Koharu] Debug attempt 3

* [Koharu] Fix chapter url

---------

Co-authored-by: Draken <dragonx943@users.noreply.github.com>
11 months ago
Koitharu 7b1a0b8d0d
[LibSocial] Authorization support 11 months ago
Koitharu 5856681753
[LibSocial] Fixes 11 months ago
Draken f066be47fe
[site/vi] Update domain + Small fixes (#1804)
* [VioletScans] Move to "en"

* [site/vi] Update sources domain

* [Madara] Split and allow overwrite for "postData"

--------------------------

Co-authored-by: Draken <dragonx943@users.noreply.github.com>
11 months ago
Draken 884142a15e
[UlasComic] Update domain + Fix getPages (#1803) 11 months ago
Tronik568 d7267b20fd
[MaidScan] Update domain (#1802) 11 months ago
Talkc0n 0537cdd533
[SayHentai] Update domain (#1801) 11 months ago
Draken c5f939df70
[Com-X] Small fixes: Bypass age verification (#1800) 11 months ago
Darwin 6854164e5a
[VioletScans] Add source (#1798)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
11 months ago
Draken 1e942c650b
[HiperDex] Update domain + Remove Broken
Solved #1766
11 months ago
Naga 30a06b67d8
[Hitomi.La] Fix cover images (#1795) 11 months ago
Naga 34a1dabae4
[MagusToon] Move to iken (#1794)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
11 months ago
Draken 23227a0427
[HoloEarth] Fix getDetails + Add locale (#1792)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
11 months ago
Draken 02d1aef77c
[Multporn] Add source (#1791)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
11 months ago
Darwin 39ebdacef9
[HoloEarth] Add source (#1788)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
11 months ago
Draken 276349e23c
[YuriGarden] Replace SAFE with SUGGESTIVE 12 months ago
kerimmkirac 0507ed8d59
[DemonicScans] Add source (#1785)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
12 months ago
Nam Huynh 2f8a0fe775
[HentaiNexus + HentaiRead] Small fixes (#1784)
* [HentaiNexus]
+ Fix package (due to moving)
+ Fix getPages() getPageUrl() (add flag for data reloading)
+ Fix getDetails (tag with quotes)
+ Cleaning

* [HentaiRead]
+ Fix package (due to moving)
+ Fix getListPage() (author searching)
+ Re-write getDetails() (update source)
+ Cleaning
12 months ago
Draken aab7e9360a
[site/vi] Update sources domain (#1780)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
12 months ago
Draken 9db443f7f7
[HangTruyen + GocTruyenTranh] Fix getPages (#1778) 12 months ago
Nam Huynh 70ce9eb008
[HentaiNexus + HentaiRead] Small fixes (#1776)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
12 months ago
Draken a58947376c
[Misskon] Add source (#1772) 12 months ago
Draken cbb70f94dd
[Manhwa210] Add source (#1771) 12 months ago
Koitharu 9408b9ba1b
Update MangaLoaderContext 12 months ago
Koitharu 409828d22f
Update MangaLoaderContext 12 months ago
Nam Huynh c9d32a804c
[Nhentai World + MeHentaiVN] Fixes (#1769)
* [HentaiRead] Fix lines

* [MeHentaiVN] Soft re-write due to source changed (#1604)

* [NHentaiWorld] Fixes fetchTags (due to source changed) and pass MangaParserTest:link (#1604)

* [MeHentaiVN] Small changes

* [MeHentaiVN] Update domain

---------

Solve task #1604

Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
12 months ago
Draken a65cbf4981
[ReaperComics] Add Broken 12 months ago
mozzaru e325063260
[Apkomik] Add Source (#1765) 12 months ago
Koitharu cf0177364c
Add requestDataFromBrowser function to MangaLoaderContext 12 months ago
Draken 8eaad2270b
[Hitomi.La] Small fix for red covers 12 months ago
Nam Huynh 362f9c6786
[HentaiRead] Add Source! (KotatsuApp#1745) (#1762)
Solved https://github.com/KotatsuApp/kotatsu-parsers/issues/1745

---------

Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
12 months ago
Draken 3047949b22
[ru/grouple] Small fixes (#1761)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
12 months ago
Draken 8d7c81c8ef
Merge branch 'master' into master 12 months ago
Nam Huynh 7d6e12d1fc
[HentaiNexus] Add source (#1757)
Solved #1690

---------

Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
12 months ago
spozer 7a0f1af06a
[ReadOnePiece] Add source (#1758)
Solved https://github.com/KotatsuApp/kotatsu-parsers/issues/1040

---------

Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
12 months ago
Draken 21aaa23586
[MimiHentai] Remove deprecated value 12 months ago
Draken 95fcbd44fd [Kiutaku] Debug attempt 1 12 months ago
Draken cc98931147
[Kiutaku + Xiutaku] Add sources (#1755)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
12 months ago
Draken 8f61beadb2
[Hitomi.la] Small fixes (#1754)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
12 months ago
Draken 6d404c6997
[RoliaScan + ElderManga] Add sources (#1753) 12 months ago
kerimmkirac 27060cfef0
[RoliaScan + ElderManga] Add sources (#1753)
* [RoliaScan] Add source

* [ElderManga] Add source

* [UzayManga] Fix search

---------

Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
12 months ago
Draken ca3d8f555a
[BatCave] Remove referer 12 months ago
Draken 7ea59a7bf1
[HeanCms] Small improvements (#1751)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
12 months ago
Draken cea4db9619
[BatCave] Small fixes (#1750)
Solved #1749
12 months ago
Draken e7c3168099
[Readmanga] Add referer (#1748) 12 months ago
Draken b165a0d611
Merge pull request #1744 from dragonx943/debug
[TeamXNovel] Small fixes
1 year ago
Draken 43e6c7c86f [TeamXNovel] Small fixes 1 year ago
Draken 346828402b
[TeamXNovel] Revert: Fix getPages 1 year ago
Draken 01d67eca2e
[site/vi] Update sources domain (#1741)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
1 year ago
Draken 4445b666aa
[site/iken] Small fixes (#1740)
* Vortexscans (Broken): Need to fix list chaps + getPages function

* Temporary fix for #1737, reference: keiyoushi/extensions-source#8645
1 year ago
Draken adf0b4e397
[DayComics] Fix list chaps 1 year ago
Draken e797cabd88
[HotComics] Fix list chaps (#1739)
Solved https://github.com/KotatsuApp/kotatsu-parsers/issues/1717, reference: https://github.com/keiyoushi/extensions-source/pull/5405
1 year ago
Draken 0c32b56378
[NetTruyen1975] Add source (#1736)
* [UzayManga] Fix tags (Attempt 1)

* [NetTruyen1975] Add source

* [NetTruyen*] Fix getPages

---------

Co-authored-by: Draken <dragonx943@users.noreply.github.com>
1 year ago
kerimmkirac 5c9c2c3a7a
[BookManga] Add Source (#1731) 1 year ago
kerimmkirac 7cd7180adb
[ArmoniScans] Add Source (#1729)
Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
1 year ago
kerimmkirac 4b6b585801
[MajorScans] Rename + Update domain (#1725) 1 year ago
Draken 1cf2153df0
[UzayManga] Fixes (#1726) 1 year ago
Draken 6ed104aeca
[AlucardScans] Add source (#1722) 1 year ago
kerimmkirac 9ba1629065
[AlucardScans] Add source (#1722) 1 year ago
kerimmkirac 669709dfbb
[site/tr + en] Add new sources (#1721)
* Add UzayManga, Mangacix, AsuraScansTR, TrMangaOku, MangaKoleji, HiveComic

* [Mangacix] Clean url

* [HiveComic] Add Broken

* [UzayManga] Clean up

* Update summary.yaml

---------

Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
1 year ago
kerimmkirac b5cb310aab
[RagnarScans] Add source (#1715)
* Add files via upload

* Update summary.yaml

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Update Ragnarscans.kt

* Remove old parser

* [Ragnarscans] Add source

---------

Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
1 year ago
Draken 50771771a8
[TeamXNovel] Fix getPages
Reference: https://github.com/keiyoushi/extensions-source/pull/8624
1 year ago
Draken dce24e2005
[MimiHentai] Small change 1 year ago
Draken d3a61f6556
[MimiHentai] Fix chaps order (#1711)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
1 year ago
Dika Ardianta 8cdd7effe5
[site/id] Update sources domain + Rename (#1705)
- Update sources domain
- Remove dead sources
- Remove duplicate site
- Rename title ManhwaList.in to ManhwaList
- Rename title KomikMirror to KomikLokal

* restore removed files with @Broken marker

Related to commit 60a085843d
1 year ago
kerimmkirac e389b01d35
[MangaGezgini] Update domain (#1710) 1 year ago
Draken 2c9632e3e5
[site/vi] Update sources domain (#1709) 1 year ago
Draken 2fff92b023
[HangTruyen] Add source (#1708)
Solved task #1694
1 year ago
Draken c949b95dab
[YuriGarden] Add source (#1707)
Solved task #1704
1 year ago
Draken da145c7f41
[MangaDistrict] Small change 1 year ago
bivaly 16b8bf9328
[FlameComics] Fixing coverurl (#1697) 1 year ago
Koitharu f076c8095a
Misc parsers fixes 1 year ago
Koitharu d2b2578a3a
[Shinigami] Fixes 1 year ago
Koitharu b24741678c
[Koharu] Fixes 1 year ago
dragonx943 fecb1db3be [Shinigami] Fix search with tags + Refactor 1 year ago
Draken 6418422157
[site/vi] Update sources domain 1 year ago
Draken 7718ff28e7
[Shinigami] Remake parser
* Solved task #1676

* Solved #1654 #1638 #1563 #1535 #1402 #1380 #1306 #1289 #1089
1 year ago
devi 3771b8d26b Fix PhenixScans close #1599
close #1675
Fix KeyoappParser close #1640
1 year ago
Draken c1357b46fb
[NetTruyen] Fix list chaps (#1678) 1 year ago
Draken 6c9f6eaae5
[Shinigami] Add Broken 1 year ago
Draken 309b6405f9
[Koharu] Add source (#1667)
[Koharu] Add source (Broken)
1 year ago
Draken 9058ead462
[Com-X] Add source (#1673) 1 year ago
Draken 162436c913
[BatCave] Fix search (#1672) 1 year ago
Draken 876f3a3d3a
[BatCave] Move to Comic sources 1 year ago
Draken 02acc7e9d4
[CuuTruyen] Add new SortOrder (#1669) 1 year ago
Koitharu e874837efb
Improve some parsers 1 year ago
Draken c294f5bb61
[Hentalk] Small fixes 1 year ago
Draken 826c948260
[Hentalk] Add source (#1665) 1 year ago
Draken a5c70f0b51
[WaManga] Set reverse for list chaps 1 year ago
Draken c2f9c18e29
[YuriNeko] Add Broken 1 year ago
Draken 91cd239c97
[BatCave] Add source (#1658) 1 year ago
Draken 4d255d0d53
[MimiHentai] Add source (#1651)
* [MimiHentai] Add source

* [MimiHentai] Fixes

* [MimiHentai] Clean code

---------

Co-authored-by: Draken <dragonx943@users.noreply.github.com>
1 year ago
Draken d5a1e1a52f
[site/vi] Update sources domain (#1653)
Co-authored-by: Draken <dragonx943@users.noreply.github.com>
1 year ago
Koitharu 9abc80880f
[Desu] Update domain 1 year ago
Draken 8d91adbd6e
[Vcomycs] Update domain - #1644 1 year ago
kerimmkirac 20a24db949
Fixing url of turkish extensions (#1642)
* Update MangaGezgini.kt

* Update MangaTr.kt

* Update Timenaight.kt

* Update Mangacim.kt

* Update MilaSub.kt

* Update Grimelek.kt

* Update Grimelek.kt

---------

Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
1 year ago
Koitharu b837659408
[Desu] Fix domains order 1 year ago
MakaMonstir 696e0bc8f6
[Desu] Update domain (#1637)
* Update domain list

* [Desu] Update domain

---------

Co-authored-by: Draken <131387159+dragonx943@users.noreply.github.com>
1 year ago
palaks-1 2ec2484982
[NHentai.xxx] Fix source (#1628) 1 year ago
Draken 738e2649d6
[DocTruyen5s] Small fixes 1 year ago
Draken 8e9da4e39f
[Hitomi.La] Change cdn domain & fix image url logic (#1624)
* [Hitomi.La] Change cdn domain & fix image url logic

* [Hitomi.La] Set null for preview in getPages

* [Hitomi.La] Remove unused function

* [Hitomi.La] Use webp for preview images

* [Hitomi.La] Fix build
1 year ago
Draken da3a9a7d9b
[Phenixscans] Mark as Broken 1 year ago
Draken 8f9c0d93f6
[DocTruyen3Q] Fix chapter images + Improvements (#1621) 1 year ago
Draken 8bb0c4f4f1
[site/vi] Update sources domain 1 year ago
Koitharu dbb04d2051
Fix link resolver 1 year ago
Draken 84562d7b36
[DoujinDesu.click] Update domain
Solved #1590
1 year ago
Draken 29327dafd0
[DamCoNuong] Update domain + Fix images
* [DamCoNuong] Update domain + Fix images
* [DamCoNuong] Fix domain
1 year ago
Draken c0d12b0c83
[CuuTruyen] Fixes + Tweaks
* [Cứu Truyện] Fix chap scanlator

* [Cứu Truyện] Fix search with state + Tags
1 year ago
Koitharu a0041f3c93
Fix build 1 year ago
Koitharu 2175e91db7
[DamCoNuong] Fixes 1 year ago
Draken 5fa7590550
[TopTruyen] Update domain 1 year ago
Draken f3ae64ed1f
[HentaiZ] Update domain 1 year ago
Koitharu cc437b8cd4
[ExHentai] Pages preview support 1 year ago
Draken db0782c61a
Merge pull request #1591 - dragonx943/damconuong/fixes
[DamCoNuong] Fixes
1 year ago
Draken bbf059ff35
[DamCoNuong] Fixes 1 year ago
Draken bebc615376
Merge pull request #1589 - dragonx943/sources/damconuong
[DamCoNuong] Add source
1 year ago
Draken 77616a5404 [DamCoNuong] Add source 1 year ago
Draken 45584f2eea
Merge pull request #1588 - dragonx943/fixes
[TopTruyen] Fix chapter images
1 year ago
dragonx943 1c3e59f9f0 [TopTruyen] Fix chapter images 1 year ago
Draken a88de5b1b6
[TopTruyen] Small changes 1 year ago
Draken 91977ba025
[MyReadingManga] Small changes 1 year ago
Draken 5420290564
[CuuTruyen] Add some tags 1 year ago
Draken 98c21db8da
Merge pull request #1577 - dragonx943/sources/domain
[vi/site] Update sources domain
1 year ago
dragonx943 23953cae11 [vi/site] Update domain 1 year ago
Draken 9cd310ba51
Merge pull request #1576 - dragonx943/sources/doctruyen3q
[TopTruyen + DocTruyen3Q] Fix chapter images
1 year ago
dragonx943 51311a17fb [TopTruyen] Fix chapter images 1 year ago
dragonx943 6799d79754 [DocTruyen3Q] Fix chapter images 1 year ago
Draken e429cdd811
[WeebCentral] Fix title not found 1 year ago
Draken 2e5ce98bac
Merge pull request #1575 - dragonx943/sources/myreadingmanga
[MyReadingManga] Add source
1 year ago
Draken 68c3a7148f [MyReadingManga] Add source 1 year ago
Draken 2a1c5bc6da
[Manga18] Fix title not found 1 year ago
Draken 6fd18543ef
[Manga18] Fix title not found 1 year ago
Koitharu ae76631aaf
[HeanCms] Fix manga ids 1 year ago
Draken ca59e8e577
Merge pull request #1571 - dragonx943/sources/cmanga
[CManga] Small fixes
1 year ago
Draken 3648b36d16
[CManga] Small fixes 1 year ago
Draken 439b7862a3
Merge pull request #1570 - dragonx943/sources/doctruyen5s
[DocTruyen5s] Fixes
1 year ago
dragonx943 f4b975c865 [DocTruyen5s] Fixes 1 year ago
dragonx943 da02a82d74 [DocTruyen5s] Fixes 1 year ago
bivaly 0d5498e60d Update FlameComics.kt 1 year ago
Koitharu 0847baf17b
Fix http request processing 1 year ago
Koitharu adf5794ad3
Expose authorization provider 1 year ago
Draken cfeea09a05
Merge pull request #1567 - dragonx943/sources/domain
[site/vi] Update domain + Tweak
1 year ago
dragonx943 e9bcb1abed [site/vi] Update domain + Tweak 1 year ago
Draken 35cf827c90
Merge pull request #1566 - dragonx943/sources/nhentaiworld
[Nhentai World] Fix tags
1 year ago
dragonx943 a10c34115b [Nhentai World] Fix tags 1 year ago
Draken 2f4e93e391
Merge pull request #1565 - dragonx943/sources/nhentaiworld
[Nhentai World] Fix chapters list
1 year ago
dragonx943 6ed42bc28b [Nhentai World] Fix chapters list 1 year ago
dragonx943 dc7c859344 [Nhentai World] Fix chapters list 1 year ago
Koitharu a82e41aabb
Deprecate Manga.isNsfw 1 year ago
Draken 3c62f4280f
Merge pull request #1557 from dragonx943/kuma/tweak
[Kumapage] Fixes + Tweak
1 year ago
Draken f806cdd118 [Kumapage] Fixes + Tweak 1 year ago
Draken 224c8a6c53 [Kumapage] Fixes + Tweak 1 year ago
Draken 408be7fa3c
Merge pull request #1555 - dragonx943/sources/kuma
[Kumapage] Add source
1 year ago
Draken a02b001768 [Kumapage] Add source + TODO 1 year ago
Draken 5edbdf801a [Kumapage] Fixes 1 year ago
Draken cc3701250a [Kumapage] Fixes 1 year ago
Draken e94d434f92 [Kumapage] Fixes 1 year ago
Draken 4d3adaa143 [Kumapage] Add source 1 year ago
Draken 36eecf781f
Merge pull request #1552 - palaks-1/source/fixes
[NHentai.xxx] Fixes
1 year ago
palaks-1 76f44c98be [NHentai.xxx] Fix source 1 year ago
Draken d660d9a30f
Merge pull request #1551 - dragonx943/sources/domain
[site/vi] Update sources domain
1 year ago
Draken 8326ee8c29 [site/vi] Update domain 1 year ago
Draken 690a004de6
Merge pull request #1550 - dragonx943/sources/dragon
[Dragon Translation] Remake parser
1 year ago
Draken 98c6454eba [Dragon Translation] Fix pages img 1 year ago
Draken 8e42ef1229 [Dragon Translation] Remake attempt 1 1 year ago
Draken 02359a2630
Fix build 1 year ago
Draken 158f6f4232
[Dragon Translation] Fix build 1 year ago
Draken 77de194b83
[Dragon Translation] Small changes 1 year ago
Draken 85cd13c8b2
Merge pull request #1549 - dragonx943/fixes
[MeHentaiVN] Fix "403 Access Denied" errors
1 year ago
Draken bfbd01b1c2
[MeHentaiVN] Fixes 1 year ago
Koitharu e83636edc0
[NhentaiWorld] Fixes 1 year ago
Koitharu 63dc67b6fd
[NhentaiWorld] Draft 1 year ago
Koitharu b0de4c3b66
[ComicK] Improvements 1 year ago
Draken d5a4cf68c6
[NewTruyen] Fix dateText + parse function 1 year ago
Draken 288d6e0b8f
Merge pull request #1543 - dragonx943/fixes
[NewTruyen] Small fixes
1 year ago
Draken aa98db9ac6 [NewTruyen] Fixes 1 year ago
Draken c19e1fd5e5
[Manganato] Update domain 1 year ago
Draken ef10e3cafc
[MangaNelo.com] Update domain 1 year ago
Draken 400c423dde
[Mangakakalot.gg] Update domain 1 year ago
Draken 85ba4fa0de
[Mangakakalot.gg] Update domain 1 year ago
Draken 8cb913d538
[TruyenHentai18] Update domain 1 year ago
Draken 2128bbe1b6
[TruyenHentai18] Fixes 1 year ago
Draken 4df07a50fb
Merge pull request #1538 - dragonx943/sources/truyenhentai18
[TruyenHentai18] Add source
1 year ago
Draken 8fa63bca64 [TruyenHentai18] Add source 1 year ago
Draken e191e0c9c1
Merge pull request #1529 - dragonx943/fixes
[Madara] Fixes + Update sources domain [vi]
1 year ago
Draken 5970509886 [site/vi] Update sources domain 1 year ago
Draken b2c7d1028c [Madara] Fixes 1 year ago
Draken 377606d1aa [CBHentai + HentaiZ] Fixes 1 year ago
Draken 688bfdaa82
Merge pull request #1528 - dragonx943/sources/mangapill
[MangaPill] Add source
1 year ago
Draken bc60629e66
[MangaPill] Add source 1 year ago
Draken f3372751d1
[MangaPill] Add source 1 year ago
Draken 77a5216ebf
Merge pull request #1524 - shub39/mangakakalot_fix
[Mangakakalot.com] -> [Mangakakalot.gg] Fixes
1 year ago
shub39 7ced91beea 🔧 no description 1 year ago
shub39 41f5cee6c6 New way to get tags 1 year ago
shub39 ba5086c409 🔨 Mangakakalot fix
* issue #1522
* passing 5 out of 11 tests
1 year ago
Koitharu 531145c7f9
Change chapter name to title 1 year ago
Koitharu f26fecb714
Small fixes 1 year ago
Draken dd7568659f
Fix warn 1 year ago
Koitharu 843d1f1bea
Fixes 1 year ago
Koitharu 29263ff59b
Add missing changes from #1496 1 year ago
Koitharu 3b91a3883e
[Grouple] Fixes 1 year ago
Koitharu ddb9b13df7
Some refactor and fix warnings 1 year ago
Koitharu ed578e5bff
Migrate altTitle to Set<String> 1 year ago
Koitharu cdbb004ca1
Merge branch 'feature/search_query' 1 year ago
palaks-1 f6145bc412 Remove deprecated Manga constructor and introduce authors field 1 year ago
Draken 86e7c21e4d
Merge pull request #1514 - VietAnh14/source/buondua
[BuonDua] Some fixes
1 year ago
ViAnh 76638c6aab [BuonDua] Some fixes 1 year ago
Draken 3a950f401b
[BuonDua] Update domain 1 year ago
Draken eeba47ea03
Merge pull request #1513 - VietAnh14/source/buondua
[BuonDua] New source
1 year ago
ViAnh 762f5cbec2 [BuonDua] New source 1 year ago
Draken 5b6c9be6d6
[CBHentai] Small fixes 1 year ago
Koitharu f681ed270b
Update parsers structure 1 year ago
Koitharu a0168a7d49
Improvements 1 year ago
Draken 47263e641e Update Mangaoku.kt 1 year ago
Draken 70c78f1cab Update MangaKazani.kt 1 year ago
Draken e7cb83f7b1 Update MangaSiginagi.kt 1 year ago
Draken 0212e1e18f Update PrunusScans.kt 1 year ago
Draken 265c411df2 Update RobinManga.kt 1 year ago
Draken 69a14b6fc5 Update YaoiTr.kt 1 year ago
Draken 168446e6d5 Update YaoiTr.kt 1 year ago
Draken 23af28f4dd Update NabiScans.kt 1 year ago
Draken ac80384ba1 Update Mangaoku.kt 1 year ago
Draken c4be0b58ca Update ImparatorManga.kt 1 year ago
Draken 023d13e44b Update EpikMan.kt 1 year ago
bai f4909ade09 readded 1 year ago
bai 26fb2b9dcb new domain 1 year ago
bai 44c2ebd4a5 closed 1 year ago
bai 47874032a3 new domain 1 year ago
bai 6dfa5ad1c2 vlosed 1 year ago
bai 111ad58b57 closed 1 year ago
bai f1086c93f7 change 1 year ago
bai 5e27a59414 change domain 1 year ago
bai cece556669 change domain 1 year ago
bai 9252da8004 closed 1 year ago
bai a9fdfee7ad mangagezgini new domain 1 year ago
bai 6904aecea3 they are closed 1 year ago
bai b516632731 milasub new domain 1 year ago
bai 87ad5d0c3a nabiscans closed 1 year ago
bai b95bb46d00 nyx mange closed 1 year ago
bai 60c91f9cc4 prunuscans closed 1 year ago
bai 20ce9cb8d9 robinmanga closed 1 year ago
bai a2625410f9 rüyamange new domain 1 year ago
bai e2697012ea viyafansub is closed 1 year ago
bai f28cd3aad9 webtoonhatti new domain 1 year ago
bai fd82a54d63 change to new domain 1 year ago
bai d95d26a280 change site name 1 year ago
bai 33310bdc42 yaoi tr closed 1 year ago
bai 6281d770c1 zamanmanga is closed 1 year ago
Batu c37f3bdad7 Delete src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/tr/AlliedFansub.kt
allied fansub is closed
1 year ago
Draken 5630ca5982
Merge pull request #1507 - dragonx943/sources/pzt
[PointZero Toons] Add source
1 year ago
Draken ceb75d98d0
[PointZero Toons] Add source
[PointZero Toons] Add source
1 year ago
Draken 75ad3b5657
[PointZero Toons] Add source
[PointZero Toons] Add source
1 year ago
Draken 5adf27eab1
Update SssScanlator.kt 1 year ago
Draken 18fbbed723
Merge pull request #1501 - palaks-1/source/nhentai_xxx_fix
[NHentai.xxx] Fix source
1 year ago
Draken 201bbf755d
Merge pull request #1502 - AwkwardPeak7/weeb2
weebcentral: fix manga list & link resolving
1 year ago
AwkwardPeak7 b9fe35b8c3
weebcentral: fix manga list & link resolving 1 year ago
palaks-1 fd0787152e [NHentai.xxx] Fix source 1 year ago
Koitharu 29cf04c804
MangaParser interface 1 year ago
Koitharu a4827d1b7d
Merge branch 'palaks-1-new-search-query-proposal' into feature/search_query 1 year ago
Koitharu f107e11528
Refactoring 1 year ago
Draken 52b6f75dd0
Merge pull request #1495 - dragonx943/sources/HentaiZ
[HentaiZ] Add source
1 year ago
Draken fd719221ed
[HentaiZ] Add source 1 year ago
Draken a2c04fa954
[HentaiZ] Add source 1 year ago
Draken 6db5ac5f26
[HentaiVn.buzz] Small fixes 1 year ago
Draken cedce23cf5
Merge pull request #1494 - sources/domain
- [TruyenQQ] Refactor
- [TruyenGG] Refactor
- Update some sources domain [site/vi]
1 year ago
Draken 645816f614 [site/vi] Update sources domain 1 year ago
Draken 00a769bfd5
Merge pull request #1492 - dragonx943/sources/HentaiVn
[HentaiVn.buzz] Add source
1 year ago
Draken 0fe5133992
Merge pull request #1493 - dragonx943/sources/SssScanlator
[sources/SssScanlator] Small fixes
1 year ago
Draken 2994f5936e
Merge pull request #1491 - dragonx943/sources/cbhentai
[CBHentai] Fix tags
1 year ago
Draken e8a96fa0ad
[SssScanlator] Small fixes
Fix chapters > numChap [SssScanlator]
1 year ago
Draken 8c8cf5dcd9 [SssScanlator] Fix attempt 1 1 year ago
Draken 19ff5db70d
[HentaiVn.buzz] Add source 1 year ago
Draken c7b689842e
[HentaiVn.buzz] Add source 1 year ago
Draken df03c65d5a
[CBHentai] Small fixes 1 year ago
Draken 4a88a93e8d
[CBHentai] Fix tags
Fix "Root not found" / "Content not found or removed" error [CBHentai]
1 year ago
Draken 950b9e5510
[Vcomycs] Update source domain
Solved #1489
1 year ago
Draken 70230de8e1
[vi/KuroNeko] Fix source name
.../site/vi/viHentai.kt -> KuroNeko.kt [Việt Hentai]
1 year ago
palaks-1 c0ea9cadd7 New MangaSearchQuery implementation proposal 1 year ago
Draken 8c966c3e23
Merge pull request #1478 - VietAnh14/fixes
[Cmanga] Fixes
1 year ago
ViAnh 9f87e77ede [Cmanga] Fixes 1 year ago
ViAnh 6165c9b7cf [Cmanga] Update api 1 year ago
Koitharu 88ea5215c0
[MangaDex] Search by author 1 year ago
Koitharu 522b3d53cc
Artist filter support 1 year ago
Draken 9a1d37166a
[CManga] Small fixes
[CManga] Fix "Nothing found" bug #1477
1 year ago
Draken 07ae87f72b
[Kiryuu] Update domain
[Kiryuu] Fix looping captcha
1 year ago
Draken a16a202800
Merge PR #1475 - dragonx943/sources/viHentai
[viHentai] Add source
1 year ago
Draken 09ff9c0a7d [viHentai] Fix attempt 2 1 year ago
Draken 8c4a93d1dc [viHentai] Fix attempt 1 1 year ago
Draken 2da8a4af84 [viHentai] Add source 1 year ago
Draken 7d5f0d3187
Merge pull request #1464 - scryptan/wamanga-fix
[WaManga] Fixes
1 year ago
Draken a65c1810a2
Merge pull request #1465 - palaks-1/sources/nhentai_xxx
[NHentai.xxx] Fix source
1 year ago
palaks-1 09b7d27b40 [NHentai.xxx] Fix source 1 year ago
scryptan e1175d01cc use api, instead of html parse 1 year ago
Draken 51fe77ff33
Merge pull request #1461 - dragonx943/site/vi/newtruyen
[NewTruyen] Add source + Update sources domain [vi]
1 year ago
Draken c540266e48 [NewTruyen] Add source 1 year ago
Draken 198e859850
[HamTruyen] Small fixes
[HamTruyen] Fix chapters order + Number
1 year ago
Draken 87944719a5
Merge pull request #1457 - palaks-1/sources/nhentai_xxx
[NHentai.xxx] Add source
1 year ago
palaks-1 6b44656819 [NHentai.xxx] Add source 1 year ago
Draken 35334e82ae
Merge pull request #1456 - palaks-1/sources/nhentai_to
[NHentai.to] Add source
1 year ago
palaks-1 32e7b7184f [NHentai.to] Add source 1 year ago
Draken 4098183be7
Merge pull request #1453 - dragonx943/sources/muitohentai
[MuitoHentai] Fix "Content not found or removed" errors
1 year ago
Draken e25b03b68f [MuitoHentai] Fix attempt 3 1 year ago
Draken 7076b2d443 [MuitoHentai] Fix attempt 2 1 year ago
Draken 1933200372 [MuitoHentai] Fixes 1 year ago
Draken 0359afe574
Merge pull request #1452 - dragonx943/sources/hamtruyen
[HamTruyen] Add source
1 year ago
Draken cd8282bbe0 [HamTruyen] Add source 1 year ago
Draken 8350b3c70e Merge branch 'sources/hamtruyen' of https://github.com/dragonx943/Kotatsu-parsers into sources/hamtruyen 1 year ago
Draken 8488d80c6f
Merge branch 'KotatsuApp:master' into sources/hamtruyen 1 year ago
Draken affd4646af
Merge pull request #1451 - dragonx943/fixes
[Hentai18VN, TruyenHentaiVN] Refactor + Fixes
1 year ago
Draken 9853638c6b [Hentai18VN] Refactor 1 year ago
Draken faf2f2a312 [HentaiVN18] Refactor + Fixes 1 year ago
Draken 4adf485208 Merge branch 'fixes' of https://github.com/dragonx943/Kotatsu-parsers into fixes 1 year ago
Draken eaab76aa9e [HamTruyen] Add source 1 year ago
Draken 2ca740a85d
Merge pull request #1450 - dragonx943/sources/hentai18vn
[Hentai18VN] Add source
1 year ago
Draken 569aa73cd4 [Hentai18VN] Fix attempt 3 1 year ago
Draken d3858ff5ff [Hentai18VN] Fix attempt 2 1 year ago
Draken 2760dc8a76 [Hentai18VN] Fix attempt 1 1 year ago
Draken c1990dde75 Merge branch 'sources/hentai18vn' of https://github.com/dragonx943/Kotatsu-parsers into sources/hentai18vn 1 year ago
Draken af5b62b8fd
Merge branch 'KotatsuApp:master' into sources/hentai18vn 1 year ago
Draken fcd6e92dba [Hentai18VN] Add source 1 year ago
Draken dad098ff34
Merge pull request #1449 from dragonx943/fixes
[TruyenHentaiVN] Fix tags
1 year ago
Draken aa9a499a09 [TruyenHentaiVN] Fix tags 1 year ago
Draken f10e6f021c Add base code for Hentai18VN 1 year ago
Draken 09596331fd
[TruyenHentaiVN] Fix tags 1 year ago
Draken c8f9962def
[TruyenHentaiVN] Fix tags 1 year ago
Draken e91e8fcccb Merge branch 'sources/hentai18vn' of https://github.com/dragonx943/Kotatsu-parsers into sources/hentai18vn 1 year ago
Draken a702f1f7ef
Merge pull request #1439 from scryptan/wamanga-fix
[WaManga] Fix NoSuchMethodError
1 year ago
Draken 9654a68dad
Merge pull request #1443 - VietAnh14/fixes
[CManga] Fixes
1 year ago
ViAnh a2f18e2284 [Cmanga] Remove escape chars in description 1 year ago
ViAnh fceb927bce [Cmanga] Small fixes 1 year ago
ViAnh f192c1716d [Cmanga] Filter unknown manga item 1 year ago
Draken f2960c2229
Merge pull request #1442 - dragonx943/sources/manhwaindo
[ManhwaIndo] Fix "Content not found or removed"
1 year ago
Draken 552a68c1d4 [ManhwaIndo] Fix attempt 1 1 year ago
Draken e5f277c957
Merge pull request #1441 from AwkwardPeak7/weeb
WeebCentral: some fixes
1 year ago
AwkwardPeak7 ff05dd7694
[WeebCentral] fix chapter number not correct in some cases 1 year ago
AwkwardPeak7 7c604647bf
[WeebCentral] fix tags not loading 1 year ago
Draken 0fd0c38971
Merge pull request #1440 - dragonx943/sources/truyenhentaivn
Add new source: TruyenHentaiVN
1 year ago
Draken a454613ce8 [TruyenHentaiVN] Add source 1 year ago
Draken 73dd8c4688 [TruyenHentaiVN] Add source 1 year ago
scryptan 1a07cfc144 try fix not exist optJSONObject in kotatsu app 1 year ago
Draken 715fe9e645
[Weeb Central] Add source 1 year ago
Draken 6316ac055c
Merge pull request #1438 from AwkwardPeak7/weeb
[Weeb Central] Add source
1 year ago
Draken ff64db7a7c
Merge pull request #1437 from VietAnh14/fixes
[NetTruyen] Fix fetch chapters
1 year ago
AwkwardPeak7 d5a299d7ff
mark MangaSee and MangaLife as broken 1 year ago
AwkwardPeak7 0e3c3f89d0
add Weeb Central 1 year ago
ViAnh 234e23b7a5 [NetTruyenVie] Fix fetch chapters 1 year ago
ViAnh 69093b8214 [NetTruyen] Fix fetch chapters 1 year ago
Draken 708d7908aa
[GocTruyenTranh] Fixes
Add NSFW detector by Tags [site/vi/GocTruyenTranh]
1 year ago
Draken b08051789d
[GocTruyenTranh] Fixes
Fix author + "null" in altTitle [GocTruyenTranh]
1 year ago
Draken 794a737b6d
Merge pull request #1435 - dragonx943/vi/goctruyentranh
Add new source: GocTruyenTranh
1 year ago
Draken b1ef29fadf [GocTruyenTranh] Add source 1 year ago
Draken 0475fc81ed [GocTruyenTranh] Add source 1 year ago
Koitharu 6aefb603ae
[NetTruyen] Fix chapters order 1 year ago
Draken 92bdefcf9d
Merge pull request #1432 - dragonx943/sources/wpcomics
[site/wpcomics/vi] Fixes
1 year ago
Draken 576de54ef8 [wpcomics/vi/nettruyen*] Fix fetch invaild format 1 year ago
Draken 319225d645 [wpcomics/vi/nettruyen*] Fixes 1 year ago
Draken 9addea65d6 [wpcomics/vi/nettruyen*] Fixes 1 year ago
Draken 95c43a06d5 [wpcomics/vi/nettruyen*] Fixes 1 year ago
Draken 72c7317672
Merge pull request #1429 - dragonx943:sources/nsfw
Mark some sites as NSFW sources
1 year ago
Draken 1cec3e5436 Mark some sites as NSFW sources 1 year ago
Draken 6c18056cef
[NetTruyenVie] Fix attempt 1 1 year ago
Draken f4b678083d
Merge pull request #1422 from VietAnh14/source/vivicomi
[Vcomycs] New source
1 year ago
ViAnh 50eb6f7b7e [Vcomycs] Update content type 1 year ago
ViAnh 0b6af2549f [Vcomycs] New source 1 year ago
Draken f229d95469
Merge pull request #1421 - dragonx943:site/domain
Update some sources domain
1 year ago
hoanphonglinh 7ba725de6d [vi] Update sources domain 1 year ago
Draken 677fd51e91
Update ManhuaScan.kt 1 year ago
Koitharu 45b843fadc
Replace try-catch with use{} in parse functions 1 year ago
Koitharu 9ec62f0be0
Refactor 1 year ago
Draken 9baba17692
Merge pull request #1413 from dragonx943/sources/manhwaindo
[Manhwaindo] Fix missing images
1 year ago
hoanphonglinh d86d0a5d5b [Manhwaindo] Fix attempt 2 1 year ago
hoanphonglinh 543e8e3267 [Manhwaindo] Fix attempt 1 1 year ago
Draken 267dcf8fa7
[TruyenTranh3Q] Fixes 1 year ago
Draken fec4594480
Merge pull request #1410 from dragonx943:site/vi/final
[TruyenTranh3Q] Add source
1 year ago
hoanphonglinh f669df2ff5 [TruyenTranh3Q] Add source 1 year ago
hoanphonglinh 9dfa652125 [TruyenTranh3Q] Add source 1 year ago
Draken 87f1cd8cdc
Update Komiku.kt 1 year ago
Draken f30f8ee563
Merge pull request #1408 from dragonx943:site/vi/yurineko
[YuriNeko] Fix 403 errors + Missing images
1 year ago
Draken 64eceba9fa
[YuriNeko] Fix domain 1 year ago
Draken 58e09bdaba
Merge pull request #1397 from sources/nettruyenvie
[NetTruyenVie] Add source
1 year ago
Koitharu d4e9040ccf Fixes 1 year ago
Koitharu 653894cb82 First commit from local/Kotatsu-parsers 1 year ago
Draken 581cb0c1fe First commit from local/Kotatsu-parsers 1 year ago
Koitharu 4e545ca7af
[WaManga] Refactor #1390 1 year ago
scryptan b4050e0335 refactor 1 year ago
scryptan 1cecd1fe94 add wamanga parser 1 year ago
Koitharu 51ed1b2db8
Update json utils 1 year ago
Draken 3e4d712dca
Merge pull request #1385 from dragonx943/sources/domain
Update sources domain
1 year ago
Draken d284b647ab
Merge branch 'KotatsuApp:master' into sources/domain 1 year ago
Draken 3bcd05fc5f Update sources domain 1 year ago
Koitharu b0a1cc48a6
Update utils 1 year ago
Draken 2eba38b289 Update domains 1 year ago
Koitharu 8481fadbd0
Fix absolute url resolving 1 year ago
Koitharu 6abcdd8d4b
Fix nullable strings usage 1 year ago
Koitharu 5df1445e29
Migrate models to data class and update Manga class 1 year ago
Draken a94adf4d90
[CuuTruyen] Fixes
Fix timer + Add manga status for CuuTruyen
1 year ago
dragonx943 f21aa282dd [CuuTruyen] Fix attempt 1 1 year ago
Draken 8ce6694232
Merge pull request #1336 from dragonx943/sources/vi
[sources/vi] Fix sources domain
1 year ago
Draken d8bd51e17e [site/vi] Change sources domain 1 year ago
Draken b1b04d2953
Merge pull request #1331 from dragonx943/final
Update domain + Fixes
1 year ago
dragonx943 8852020ca8 [MeHentaiVN] Add source 1 year ago
Draken deb4f763d0
Merge pull request #2 from dragonx943/test
[NetTruyen] Fixes
1 year ago
dragonx943 58593de53d [NetTruyen] Fix attempt 2 1 year ago
dragonx943 040be87a54 [NetTruyen] Fix attempt 1 1 year ago
Draken 1703ac39d5
Rename + Change domain sources from dragonx943/test
Rename + Change domain sources
1 year ago
dragonx943 839869061e Rename some sources 1 year ago
Koitharu 764c65563b
[Grouple] Fixes 1 year ago
Koitharu 2550b9cac1
[KomikTap] Fix http pages 1 year ago
Koitharu 04225170d3
[Grouple] Fix pages parsing 1 year ago
Koitharu 326a3f78b2
Fix Response.mimeType extension 1 year ago
Koitharu f86d31f811
Update utils 1 year ago
vianh 10dac6c0d4 [CManga] New source 1 year ago
devi fece09b781 Merge remote-tracking branch 'origin/master' 1 year ago
devi 6b1fb90584 Update source
[AnimeH] Broken source close #1301
 [SussyScan] fix close #1298
 close #1287
 close #1282
 close #1273
 close #1267
 close #1268
 close #1260
 close #1234
 [Manhwa18] fix src close #1297
 close #1290
 close #1288
[KomikCast] close #1284
close #1283
close #1272
close #1270
close #1265
[Birdtoon] close #1276
[Komiktap] close #1275
[SadScans] close #1274
[Flamecomics] reverse chapter fix close #1266
[Kiryuu] fix url close #1258
[MangaTime] Broken close #1228
[Dexhentai] fix close #1226
1 year ago
Koitharu 5055cfddbd
Merge pull request #1294 from dragonx943/patch-1 1 year ago
Draken 878d20e612
[ComicExtra] Remake #1 1 year ago
Koitharu 336f64712d
Merge pull request #1292 from dragonx943/patch-1 1 year ago
Draken 56989c6b2f
Update Fecomicc.kt 1 year ago
Draken 7b1d3dcd46
Fixes 1 year ago
Koitharu f1e66daf06
Merge pull request #1291 from dragonx943/test 1 year ago
dragonx943 ca087290b9 [YuriNeko] Fix attempt 1 1 year ago
Koitharu 883886bc32
Merge pull request #1281 from dragonx943/test 1 year ago
Draken 9e60253eb1
Merge branch 'KotatsuApp:master' into test 1 year ago
dragonx943 dba85f2bff Fixes 1 year ago
Koitharu e5b852793e
Merge pull request #1280 from dragonx943/test 1 year ago
Draken d4cbd86b96
Update summary.yaml 1 year ago
Draken 8d82bacaa3
Merge branch 'KotatsuApp:master' into test 1 year ago
dragonx943 4af760c172 Fixes 1 year ago
dragonx943 7747bc53b1 Update domain + Refactor 1 year ago
Koitharu 0456a92f15
[WpComics] Fix covers 1 year ago
Draken 677afa9a41
Update TopTruyen.kt (#1279) 1 year ago
Koitharu ebcce4f2ec
[DocTruyen3Q] Fix covers 1 year ago
Koitharu 91e53e4872
[AsuraScans] Fix pages parsing 1 year ago
Koitharu 8bc51b3b79
[FlameComics] Rewrite #1237 1 year ago
Koitharu 733d3ca69f
[ResetScans] Migrate to the new website (close #1252) 1 year ago
Koitharu 4a2e465e5d
[NineManga] Fixes #1253 1 year ago
Draken 7ed3c5a175
Update sources domain (#1254)
* Update Mangarbic.kt

* Update BlogTruyenParser.kt

* Update CuuTruyenParser.kt

* Update HentaiVNParser.kt

* Update LxManga.kt

* Update DocTruyen3Q.kt

* Update NetTruyenHE.kt

* Update NetTruyenLL.kt

* Update NetTruyenSSR.kt

* Update NetTruyenUU.kt

* Update NhatTruyenVN.kt

* Update TopTruyen.kt

* Update OtakusanVi.kt

* Update OtakusanEn.kt

* Update HentaiVnPlus.kt

* Update TruyenTranhDamMyy.kt

* Update TruyenVn.kt

* Update DocTruyen5s.kt
1 year ago
bivaly 8567cedf0a
Updating FlameComics ListUrl (#1255)
* Update FlameComics.kt

* Update FlameComics.kt

* Update FlameComics's listUrl

Ther are no longer a listUrl but maybe they are  going to revert the changes so leaving there the overide for easy changing

* removing FlameComics ListUrl

Ther are no longer a listUrl but maybe they are  going to revert the changes so leaving there the overide for easy changing
1 year ago
Koitharu e02b563bf4
[AsuraComic] Fix pages parsing 1 year ago
Draken 27d2814ef9
[LectorManga] Update domain (#1233)
* Update LectorManga.kt

* Update LikeManga.kt
1 year ago
Draken de95fa2e3e
Update domain + Add sources (#1221)
* Update HariManga.kt

* Update Mgkomik.kt

* Update XoxoComics.kt

* Create DuaLeoTruyen.kt

* Create MyComicList.kt

* Update DuaLeoTruyen.kt

* Update summary.yaml

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Add suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Fixes

---------

Co-authored-by: Koitharu <nvasya95@gmail.com>
1 year ago
Koitharu 5a4f9d8914
[MangaFire] Fix search 1 year ago
Koitharu 23271c4623
[LibSocial] Use site domains 1 year ago
Koitharu f3d14e101c
[MintManga] Update domain 1 year ago
Koitharu 275d7f5419
[ImHentai] Fix pages parsing 1 year ago
Koitharu ac163085e1
[ImHentai] Fix pages parsing 1 year ago
Draken 8b4bac3cc2
[TruyenGG] Refactor (#1215)
* Update TruyenGG.kt

* Update TruyenGG.kt
1 year ago
Koitharu 35f4db7905
Rollback kotlin to fix build 1 year ago
Koitharu 08b1241a68
Remove redundant trim and mapNotNullToSet usage 1 year ago
Koitharu 3ffcefaa1b
Get rid of redundant boxing 1 year ago
Koitharu 3b173dc6fc
Improve utils 1 year ago
Draken 60f1fb1f70
Update + Add source (#1213)
* Update YurinekoParser.kt

* Create TruyenGG.kt

* Update and rename Truyenqq.kt to TruyenQQ.kt

* Update TruyenGG.kt

* Update summary.yaml

* Update suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Update suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Update suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Update suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Update suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Update suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Fix tags

---------

Co-authored-by: Koitharu <nvasya95@gmail.com>
1 year ago
devi f610ae6412 [ManhwaLatino] fix the order of the chapters and some manga don't display all the chapters.
close #1211
1 year ago
Koitharu f80b586081
[Iken] Fixes 2 years ago
Koitharu 7999064dd4
[MangaFire] Fix search (close #1196) 2 years ago
Koitharu 8c387e7983
[Desu] Cleanup 2 years ago
Koitharu 1d29cacf04
[LxManga] Refactor 2 years ago
Draken ce4b4d06d0
Update domain + Fixes (#1206)
* Update MaidScan.kt

* Update NetTruyenLL.kt

* Update Quaanhdaocuteo.kt

* Fixes
2 years ago
Koitharu 79e1d59482
Fixes 2 years ago
devi 47e1c0fa89 Minor changes 2 years ago
devi 3ec23a56ab Minor changes 2 years ago
devi a01493e071 [EpsilonScan] fix close #1038
[Keyoapp] fix pages close #1192
[Yaoiflix] Fix close #1201
[Teamxnovel] fix Searching close #1176
close #1185
[ManhwaLatino] close #1181
[MiHentai] fix close #1180
[GenzToon] close #1169
[MangaGalaxy] close #1168 ( redirect to VortexScans )
[CatharsisFantasy] close #1158
[CatharsisWorld] close #1157
Update soures
2 years ago
Draken 16052210c1
[NhatTruyenVN] Fixes (#1199)
* Update NhatTruyenVN.kt

* Update src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NhatTruyenVN.kt

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Update suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

* Update suggestions

Co-authored-by: Koitharu <nvasya95@gmail.com>

---------

Co-authored-by: Koitharu <nvasya95@gmail.com>
2 years ago
Koitharu 7dd9658864
[Ahen] Fix chapters parsing 2 years ago
Koitharu f794f411b7
[DesuMe] Fix covers 2 years ago
Draken 94fbbad6f3
[DocTruyen3Q + TopTruyen] Fixes (#1187)
* Update DocTruyen3Q.kt

* Update CuuTruyenParser.kt

* Update NetTruyen.kt

* Update DocTruyen3Q.kt

* TopTruyen sus :p

* Update TopTruyen.kt

* Remove unavailable tags

* Update TopTruyen.kt

* Remove redundant trim() calls

* Update DocTruyen3Q.kt

* Update TopTruyen.kt

* Remove some unavailable tags

* Update TopTruyen.kt

* Update DocTruyen3Q.kt

* Update TopTruyen.kt

---------

Co-authored-by: Koitharu <nvasya95@gmail.com>
2 years ago
Koitharu 4c5ed57958
Fix json iterator 2 years ago
Draken 3d5cc5ceff Update TruyenVn.kt 2 years ago
Draken c506153737 Update OtakusanEn.kt 2 years ago
Draken 95ee8a4a16 Update OtakusanVi.kt 2 years ago
Draken 3f7b0695f4 Update LxManga.kt 2 years ago
Draken b3bb0939de Update HentaiVnPlus.kt 2 years ago
Koitharu 08fe54c36d
Refactor json utilities 2 years ago
Koitharu 166c5be5d6
Refactoring 2 years ago
Koitharu 7ce2a97c1f
[DocTruyen5s] Refactoring 2 years ago
Draken b23e241973 Update DocTruyen5s.kt 2 years ago
Draken b8b54b7139 Update DocTruyen5s.kt 2 years ago
Draken 5853e54684 Update SayHentai.kt 2 years ago
Draken 32e3b07e5d Update HentaiCube.kt 2 years ago
Draken 3b99676e7a Update HentaiCube.kt 2 years ago
Draken 3c829302c7 Update TopTruyenViet.kt 2 years ago
Draken b579b01db5 Update DocTruyen3Q.kt 2 years ago
Draken 7db798c420 Update CuuTruyenParser.kt 2 years ago
Draken 77ed11b58e Update summary.yaml 2 years ago
Draken 3ae6ffb8d0 Tweak :p 2 years ago
Draken 0e5e4d57c5 Create SayHentai.kt 2 years ago
Koitharu d8cb38a9be
Improve links resolving 2 years ago
Koitharu 07b0a2da9e
Fix loading manga by link 2 years ago
Koitharu 1d040e8291
Link resolver implementation 2 years ago
davvarrr 797a91a037
Merge pull request #1167 from dragonx943/patch-1
Daily update
2 years ago
devi 9c0b57835f [WebtoonEmpire] fix close #1134 2 years ago
devi 72564f5449 [MadaraParser] auto fix url manga on
fix query ( withoutAjax
[MangaPark] fix close #1152
close #1145
[AdultWebtoon] fix close #1139
[ComicExtra] change type close #1130
[liliana] fix and add ManhuaPlusorg, Raw1001, MangaSect, MangaKoma01, ManhuaGold
2 years ago
Draken cc18692538
Update ComickFunParser.kt 2 years ago
Draken 3e102e9d69 Update BlogTruyenParser.kt 2 years ago
Draken b9b53bcc91 Add some tags 2 years ago
Draken 00ef8e5cdf Remove broken tag 2 years ago
Draken a738591dd0 Update DocTruyen5s.kt 2 years ago
Draken 85771bcd89 Fix BlogTruyen 2 years ago
Koitharu 0150ede4cb
[Liliana] Fix chapters order 2 years ago
Draken 1406165789 Update LxManga.kt 2 years ago
Draken a62eadb581 Fix filters 2 years ago
Draken 197b148fca Update Hijala.kt 2 years ago
Draken fd4a1cda2d Update HentaiCube.kt 2 years ago
Draken 108920c4fd Update DocTruyen5s.kt 2 years ago
Draken 49c7702707 Update LilianaParser.kt 2 years ago
Draken 15361654b7 Create LilianaParser.kt 2 years ago
Draken c2b64d40d1 Create DocTruyen5s.kt 2 years ago
Koitharu ce9404da84
[Madara] Fix multiple pages handling 2 years ago
Koitharu 5c55d65eb3
Refactor and fixes 2 years ago
Draken 6f9180545b Update and rename HentaiVnParser.kt to HentaiVnPlus.kt 2 years ago
Draken e966aa07cd Create BlogTruyenParser.kt 2 years ago
Draken 102993cb10 Remove BlogTruyen - Closed site 2 years ago
Draken d0627b83d0 Update BlogTruyenVN.kt 2 years ago
Draken a6602a0f5a Update BlogTruyenVN.kt 2 years ago
Draken 55b9a72795 Create BlogTruyenVN.kt 2 years ago
Draken d4f0c9d116 Update LxManga.kt 2 years ago
Draken bbb3184454 Update CuuTruyenParser.kt 2 years ago
Draken 7d81e68757 Update CuuTruyenParser.kt 2 years ago
Draken 487388c950 Update DocTruyen3Q.kt 2 years ago
Draken 9d507eb10c Update TopTruyenViet.kt 2 years ago
Draken cf9b19ffa7 This source will no longer work!
This site has been sold and it has changed name + source structure :p

Changed to: https://github.com/KotatsuApp/kotatsu-parsers/issues/1135
2 years ago
Draken 9591269a68 Update TopTruyenViet.kt 2 years ago
Draken 27fe426213 Update DocTruyen3Q.kt 2 years ago
Draken b23d899f5e Update CuuTruyenParser.kt 2 years ago
Draken 8bf0c03fe2 Tags ? 2 years ago
Draken 1ebb298cd7 Update CuuTruyenParser.kt 2 years ago
Draken 9f588ab9fd Rename HentaiVn.kt to HentaiVnParser.kt 2 years ago
Draken 91a1198900 Update HentaiVn.kt 2 years ago
Draken 321ac087dc Update TruyenVn.kt 2 years ago
Draken 5d81c030b5 Rename HentaiVnFit.kt to HentaiVn.kt 2 years ago
Draken e798bc7787 Update HentaiVnFit.kt 2 years ago
Draken 057b053d58 Update TopTruyenViet.kt 2 years ago
Draken e9a9ef6b95 Update DocTruyen3Q.kt 2 years ago
Draken bd7062a479 Update LxManga.kt 2 years ago
Koitharu a659068ef3
Fixes batch 2 years ago
Koitharu 6f7e1fcfb2
[Grouple] Fix closing response on error 2 years ago
Draken eff26c8889 Create NetTruyenFE.kt 2 years ago
Draken 037059d0e6 Update NetTruyenHE.kt 2 years ago
Draken 9b78a3ecf2 Update PinkTeaComic.kt 2 years ago
Draken 4df8311e18 Update Truyenqq.kt 2 years ago
Koitharu 645006fde8
Fix closing response in interceptors 2 years ago
Koitharu 95bbfefe90
Merge pull request #1128 from dragonx943/patch-1
Daily update sources
2 years ago
Draken 3181a5860b
Update Truyenqq.kt 2 years ago
Draken 39c83444b7
Broken:( 2 years ago
Draken 7356d2036d
Update BlogTruyenVNParser.kt 2 years ago
Koitharu a8df8665ae [CuuTruyen] Fix tags 2 years ago
Koitharu 6355252699
Merge pull request #1117 from dragonx943/het-cuu-truyen
[CuuTruyen] Fix tags
2 years ago
Draken 6ec65e4aa4
Update TopTruyenViet.kt 2 years ago
Draken f58bb421e5
Update DocTruyen3Q.kt 2 years ago
Draken 38ecde185a
Update CuuTruyenParser.kt 2 years ago
Draken e3924d5b06
Remove something
Remove some Tags (not found manga with it)
2 years ago
Draken c1a7fc2624
Update CuuTruyenParser.kt 2 years ago
Koitharu 818d14fdbb
Merge pull request #1109 from dragonx943/patch-2
Update domain
2 years ago
Draken e93a6865e7
Update HentaiVNParser.kt 2 years ago
Draken c6325eef50
Update DocTruyen3Q.kt 2 years ago
Draken 06d6653c78
Create DocTruyen3Q.kt 2 years ago
Draken aac722909b
Update HentaiVNParser.kt 2 years ago
Draken 78d64713a2
Create NetTruyenUU.kt 2 years ago
devi 7d71421c30 Update sources
Close #1105
Close #1104
2 years ago
Koitharu 7a571e596d
[NHentai] Fix duplicated preferences 2 years ago
davvarrr 1718a034ee
Merge pull request #1101 from dragonx943/patch-1
Update domain (Fetch data error)
2 years ago
devi 5443daabb6 [PornComix] fix close #1100 2 years ago
devi de4f8ef2f9 [madtheme] add SearchWithFilters
[manga18] add SearchWithFilters
[mangadventure] add SearchWithFilters
[mangaworld] add POPULARITY_ASC, NEWEST_ASC, SearchWithFilters, Year, ContentTypes, Multiple states
Remove type on fetch tags
[mmrcms] add SearchWithFilters
[TrWebtoon] add SearchWithFilters
[Truyenqq] add TagsExclusion, ContentTypes, NEWEST_ASC, UPDATED_ASC, POPULARITY_ASC
[Baozimh] add ContentTypes
[zmanga] add SearchWithFilters, Year, ContentTypes
2 years ago
Draken b9fb4a5e93
Update HentaiVNParser.kt 2 years ago
Koitharu 3cdd391410
[ExHentai] Use ContentType filter 2 years ago
Koitharu ad77e8afa4
[CuuTruyen] Fix UserAgent 2 years ago
Koitharu c85d17d60d
[CuuTruyen] Fix 2 years ago
Koitharu 955c75a99f
[CuuTruyen] Fix attempt 1 2 years ago
devi d32d1f5044 [ImHentai] add ContentTypes
add new ContentTypes
[iken] add SearchWithFilters, ContentTypes
[keyoapp] add SearchWithFilters
[likemanga] add POPULARITY_TODAY, POPULARITY_WEEK, POPULARITY_MONTH, SearchWithFilters
2 years ago
Koitharu 091c5247d5
Merge pull request #1093 from dragonx943/het-cuu-truyen
Fix decryptor + Add image reconstructor ?
2 years ago
Draken c593798d83
Update NetTruyenSSR.kt 2 years ago
Draken edac81f51a
Update NetTruyenLL.kt 2 years ago
Draken 8da3b4b7ac
Update NetTruyen.kt 2 years ago
Draken 844e5fdb50
Update Truyenqq.kt 2 years ago
Draken 3f502de1f7
Update Truyenqq.kt 2 years ago
Draken 6532f392b5
Update Truyenqq.kt 2 years ago
Draken 9f340b2dfb
Update LxManga.kt 2 years ago
Draken 30d34de653
Update CuuTruyenParser.kt 2 years ago
devi 52329e658f [SoManga] close #1091
[AsuraComic] fix close #1088
[HeanCms] add SearchWithFilters
[fuzzydoodle] add ContentTypes, SearchWithFilters
[DoujinDesu.tv] add SearchWithFilters, ContentTypes
2 years ago
devi 1c0df02c56 [HiveToon] fix close #1086 2 years ago
devi 2061a971b8 [fmreader] add SearchWithFilters
[LegacyScans] add ContentTypes, SortOrder.UPDATED
[MangaMana] add SortOrder.RATING_ASC , SortOrder.NEWEST_ASC
[Manhwa18.com] move to /en
Add new option on .toAbsoluteUrl()
2 years ago
Koitharu cc62981f12
[ComicK] Fix chapter numbers in names 2 years ago
Koitharu 613623fa53
Update information pages 2 years ago
devi fffdbfdbee [MangaDex] fix close #1084 2 years ago
devi a55a4720fe [TuMangaOnline] add SearchWithFilters, TagsExclusion, Demographics, ContentTypes
[TempleScanEsp] fix
Add KODOMO on Demographic
Add ONE_SHOT on ContentType
2 years ago
Koitharu 42f2813e44
Update readme 2 years ago
Koitharu c58d35288b
Added some codegen features 2 years ago
Koitharu f410df40f1
Improve utilities 2 years ago
Koitharu 7e95949ab7
[Grouple] Improve filter 2 years ago
Koitharu 7c4c3a3c97
[ExHentai] Improve filtering and tags 2 years ago
devi 7f25c5f82d [Manga-Starz] Fix close #1073 2 years ago
devi f1939391e8 [AsuraComic] Fix close #1077
add ContentTypes , SearchWithFilters
[CloneManga] isSearchSupported false
[MangaGeko] fix tags,  add MultipleTags, TagsExclusion
[Manhwa18.net] add isSearchSupported
[ManhwasMen] add Exception
[Pururin] add SearchWithFilters, MultipleTags , TagsExclusion
[VyManga] add SearchWithFilters, MultipleTags, TagsExclusion
2 years ago
devi d1f9b0d829 [mangaDex] Fix SortOrder and remove YearRange
[Bato] add OriginalLocale
[MangaPark] add OriginalLocale, SearchWithFilters
[Comick] add SearchWithFilters
[NineMangaParser] add SearchWithFilters
[AnimeBootstrap] add SearchWithFilters , ContentTypes
[madara] add Year, SearchWithFilters, SortOrder.RELEVANCE
[TeamXNovel] add ContentTypes , SearchWithFilters
2 years ago
Koitharu f2354957e6
[MangaDex] Fix filter capabilities 2 years ago
devi 600eab20a1 remove unessery code
Remove old chapter manga and change last source for new manga chapter
Simplify parseChapterDate
Fix fetchAvailableTags on some source need to override
2 years ago
Koitharu 336c4a4d49
Add year constants 2 years ago
Koitharu a1e47edfb2
Fix getPageUrl visibility 2 years ago
Koitharu 5269659c73
Merge pull request #1063 from KotatsuApp/feature/advanced_filter
Advanced filter
2 years ago
Koitharu ab1b549a64
Merge branch 'master' of github.com:KotatsuApp/kotatsu-parsers into feature/advanced_filter 2 years ago
devi 2867dffc5a Capabilities simplified on madara to avoid redundant code 2 years ago
Koitharu 6d972a13fe
Fix filter capabilities/options default values 2 years ago
Koitharu 3918bfafdf
Specify visibility modifiers explicitly 2 years ago
Koitharu 1c15e569bf
Migration to MangaListFilterCapabilities 2 years ago
Koitharu 5030548500
Global refactoring: partial migrate to ListFilterOptions/ListFilterCapabilities 2 years ago
Koitharu c5d3a7b0c1
Global refactoring: migrate getList to new filter 2 years ago
Draken 75c46130ed Update NetTruyenSSR.kt 2 years ago
Draken 62c17155ca Update NetTruyen.kt 2 years ago
Draken 3a52062ce4 Update NetTruyenLL.kt 2 years ago
Draken 74ccbdcaee Update HentaiCube.kt 2 years ago
Draken fbd610349a Update PinkTeaComic.kt 2 years ago
Draken 8af75d8c6a Update TruyenVn.kt 2 years ago
Draken 0bafd117ff Update and rename HentaiVnCafe.kt to HentaiVnFit.kt 2 years ago
Koitharu ae9a7c6090
Introduce MangaListFilterCapabilities class 2 years ago
Koitharu 481ad02e01
Partial migration to MangaListFilterV2 2 years ago
Koitharu 9042074c50
Merge remote-tracking branch 'origin/SortOrderPopularitytime' into feature/advanced_filter 2 years ago
Koitharu 821e51ff7d
Merge branch 'filter-type-and-demographic' into feature/advanced_filter 2 years ago
Koitharu 0f4808f5b5
Use ContentType instead of Type 2 years ago
Koitharu aba8a80d8f
[MangaDex] Fix settings 2 years ago
Koitharu 2b0edfde60
[MangaDex] Data-saver server support (close #1062) 2 years ago
Koitharu 01a496768a
MangaListFilter v2 2 years ago
Draken 82a57cb3c8 Update HentaiVN Domain 2 years ago
Naga 27c07d86bc fix tags 2 years ago
Naga e916f2a66e Closes #1057 2 years ago
devi 494ecdfec8 Add year range. 2 years ago
devi 8a3c0e02f6 Add relevance sort order for good filters with text searches
Adding the added filter
2 years ago
devi 37428bf9c0 Add year, localeMangas filter on MangaListFilter.Advanced
Add year, localeMangas and localeMangas on MangaDexParser
2 years ago
devi 62b4a21cef Add query filter on MangaListFilter.Advanced
Add query.filer Advanced on MangaDexParser
2 years ago
davvarrr b566e4e7e4
Update src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt
Co-authored-by: Koitharu <nvasya95@gmail.com>
2 years ago
davvarrr 4ff2061bb7
Update src/main/kotlin/org/koitharu/kotatsu/parsers/MangaParser.kt
Co-authored-by: Koitharu <nvasya95@gmail.com>
2 years ago
devi 03d7e138e9 Add support type and Demographic on MangaListFilter 2 years ago
devi 0540da57e6 Update Sources
Close #1060
2 years ago
devi e0e9d25a4f Improve madara 2 years ago
devi fd7684866e Refonte madara :
Add tagsExclude
Add support multiple tags
Add RATING_ASC ( and prepare RELEVANCE )
simplify postrequest
Preparation for query, year, author and artist filters
Change withoutAjax for some sources
2 years ago
devi 7c8b427d2a Miss add SortOrder in availableSortOrders for NHentaiParser 2 years ago
devi 4fcc68d149 add POPULARITY time sortOrder 2 years ago
Koitharu ad726a3fd7
[CuuTruyen] Fixes 2 years ago
Koitharu f167a8c8f6
[CuuTruyen] Fixes 2 years ago
Draken b404b44008 insert something :p 2 years ago
Draken c6dc51bb5d Some suggestions 2 years ago
Draken 69ee6be246 Create NetTruyenHE.kt 2 years ago
Draken e08fab2938 Final Source commit:(
i cried:(
2 years ago
Koitharu 5db275bce2
[Grouple] Handle Usagi redirects 2 years ago
Koitharu 9c05e3f8e8
Usagi parser 2 years ago
devi ac2c9b8621 Add InfamousScans 2 years ago
devi 02852ac4e7 Add Exclude Genres and new sort order on ComickFunParser
close #1032
2 years ago
devi 1163541ac5 Update sources and add sources
Fix MagusManga close #1021
2 years ago
devi 2da0c17fe2 Merge remote-tracking branch 'origin/master' 2 years ago
devi 7528480f54 add asc 2 years ago
devi a45a270479 Add Asc on some SortOrder 2 years ago
devi 380804361b Add Asc on some SortOrder 2 years ago
devi e072ee8b44 add asc 2 years ago
devi d812644e61 Add Asc on some SortOrder 2 years ago
devi 71deb11e1f Add Asc on some SortOrder 2 years ago
devi 2e138da3d5 add source and fix 2 years ago
Koitharu 939b6b1e46
Improve TooManyRequestExceptions 2 years ago
Koitharu d937c7e6ab
[ExHentai] Fix IP ban detection 2 years ago
Koitharu f91ff0b9d0
[ExHentai] Detect IP ban 2 years ago
Koitharu 98cbee11b9
Unify user agent header processing 2 years ago
Draken 7f0431d493 Update NetTruyenLL.kt 2 years ago
Draken 3227c53699 Create NetTruyenLL.kt 2 years ago
Draken 2ee8784d7d Update NetTruyen.kt 2 years ago
Draken 0fac0f450c Create BlogTruyenVNParser.kt 2 years ago
Draken b4fb202db4 Update BlogTruyenParser.kt 2 years ago
Draken 93ba163ea2 Update NetTruyen.kt 2 years ago
Draken bd3ff20146 Hotfix Source 2 years ago
devi b3a0b97f0e add HastaTeamReader close #1004
Update Sources ( fix , url and broken )
Add sources
2 years ago
devi ca212ca692 add auth on some sources
add Config Header on many sources
close #974
2 years ago
devi 3b809202b3
Merge pull request #994 from dragonx943/patch-1
Update HentaiVN Domain
2 years ago
Draken 9858419586
Update HentaiVN Domain 2 years ago
devi 1f7fe2aed3 Fix Sources
Add sources
Correct detect login on MadaraParser
close #988
fix oocini.biz close #987
Changing the latest DEPRECATION
( Technically we can raise the old support for getList() and MangaChapter() )
2 years ago
devi 71affd155c Some Fix 2 years ago
devi a54f030c4e Fix, change url and add broken
Fix HeamCms Chapter close #970
add Template FuzzyDoodleParser
Fix LelScanVf
Add hentaislayer , ScyllaComics
Close #609
Close #901
Close #440
Add template IkenParser
Fix MangaGalaxyParser
Add VortexScans
Fix HniScantrad
Add HastaTeam close #939
Add HotComicsParser close #962
Add HentaiCrot close #913
2 years ago
devi 3b5a018f8c Fix, change url and add broken 2 years ago
devi 8d5fc945d4 Move ReaperComics to HeanCms
Rework heancms based on @NagaYZ's code
2 years ago
devi 50194df24d Rm duniakomik close #952
Fix DoujinKu close #951
Fix DoujinDesu.tv close
Add, fix some sources  #963
change url close #965 ( the copy was added anyway )
2 years ago
devi 5f771973a8
Merge pull request #967 from NagaYZ/fix-reaper
Fix reaperscan site structure changed
2 years ago
devi 4fcfbb374f
Merge pull request #959 from dragonx943/patch-1
Broken source + Update domain
2 years ago
Koitharu 853c21e49f
[ExHentai] Fix tags parsing 2 years ago
Naga 3efb5d8520 fixed page duplicated 2 years ago
Naga e74985d870 Merge branch 'refs/heads/master' into fix-reaper
# Conflicts:
#	src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/ReaperComics.kt
2 years ago
Naga 32eab42c26
Merge pull request #31 from KotatsuApp/master
[pull] master from KotatsuApp:master
2 years ago
Naga 0d3d8232f9 reaper changed site structure 2 years ago
Draken fd3dc389df
Update Saytruyenhay.kt 2 years ago
Draken 2d274f37ee
Update TruyenTranhDamMyy.kt 2 years ago
Draken d1a12df18f
Create Saytruyenhay.kt 2 years ago
Draken 017a9a58a3
Update TruyenTranhDamMyy.kt 2 years ago
Draken e33c57f51b
Update Quaanhdaocuteo.kt 2 years ago
Draken da041df054
Delete src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/vi/Saytruyenhay.kt 2 years ago
Draken 99b57e1297
Update TruyenTranhDamMyy.kt 2 years ago
Draken 6ce8a22514
Update BlogTruyenParser.kt 2 years ago
Draken a9fc534ea7
Remove dead sources + Change domain (#955)
* Delete src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/TruyentranhLHParser.kt

* Update NetTruyen.kt

* Delete src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenAA.kt

* Delete src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NetTruyenX.kt

* Delete src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/Nettruyenmax.kt

* Update NetTruyen.kt

* Update NhatTruyenVN

* Delete src/main/kotlin/org/koitharu/kotatsu/parsers/site/wpcomics/vi/NhatTruyenSS.kt
2 years ago
devi 6e04fa1251 Add CupFoxParser and sources
Add OneMangaParser and sources
2 years ago
devi 8f851282b4 Add XManhwa, MangaMana, MangaFr
Fix XoxoComics close #928
2 years ago
devi d774935a6a Merge remote-tracking branch 'origin/master' 2 years ago
devi 712d42b328 Some Fix 2 years ago
SBS123 f66aab03f2
Updata MangaNoon (#924)
* Update Manjanoon.kt

* Update Manjanoon.kt

* Update Manjanoon.kt
2 years ago
devi 53ca9c9677 Rework WpComicsParser
Adds sources
2 years ago
devi 7bcc624a74 Add some sources
Fix some sources
close #929
2 years ago
devi fa2d73348e rm @Broken 2 years ago
devi 26c3edbc17 Fix some sources
add @Broken on sources
Close #927
2 years ago
Koitharu b9d89edfc2
[DynastyScans] Fixes 2 years ago
Koitharu 7ccc6438d5
[Grouple] Fix public url 2 years ago
devi cd9f65596b Add some sources
Fix some sources
Add @Broken on some sources
close #909 #912 #920 #910 #914 #916
2 years ago
Koitharu 5404743f17
Update dependencies 2 years ago
Koitharu c0296d0882
Fix build 2 years ago
Inkesk dec07bd27d Update MangaGalaxy.kt
parser issue for mangagalaxy not loading chapter solved
2 years ago
Inkesk 1be1e9843d Update MangaGalaxy.kt
Updated the domain
2 years ago
Inkesk 5b5afcfe4c Update LuaScans.kt
Luascans.com changed domain to Luacomic.com , committing the change so future bugs and issues don't arise.
2 years ago
Inkesk 4f5dc9ffad Update ManhuaScan.kt
manhuascan.io was changed to kaliscan.io hence the change committed in here .
2 years ago
Inkesk 9c5b00ecf0 Update DarkScans.kt
Updated the domain name from darkscans.com to darkscans.net
2 years ago
Inkesk 048ba99323 Update ResetScans.kt
Changed the domain from reset-scans.xyz to reset-scans.co.
As previous domain for source was closed due to DMCA. Figure out the rest of details yourself , but resetscans is still on mates. Enjoy
2 years ago
devi 35bdc15aa9 Fix url grimelek 2 years ago
Koitharu b06288e7eb
Merge branch 'master' into experimental/abstract_sources 2 years ago
SBS123 426c7ad708 Update Normoyun.kt 2 years ago
devi fadf8861e7 Rm BrMangas close #454
rm PowerManga close #462
Add MangaNinja close #897
Add Grimelek close #375
Add OpiaToon close#374
Add login on madara
Fix ManhwaFreak
2 years ago
devi dceedf019a Fix HentaiVN
Remove some warning
2 years ago
devi f49ddf7437 Fix HentaiVN
Remove some warning
2 years ago
devi 33a3c8350d
Merge pull request #885 from dragonx943/patch-1
domain change hentaivn
2 years ago
devi 052fa146d2
Merge pull request #889 from NagaYZ/fix-manhuaplus
domain change manhuaplus
2 years ago
devi 1c85782690 Fix CafecomYaoi close #715
Fix Topmanhua close #710
Add Some sources
Add new Parser
fix EpsilonScan close #602
Add MangaHub.link close #892
Fix SussyScan close #893
Fix MaidScan close #585
fix HuntersScan close #584
Fix TeamXNovel close #575
close #574
Fix MangaTown close #569
Add LeitorDeManga close #864
add Search on ScanParser
Add mangabr close #862
Add Manga Italia close #841
Add Mangá Terra close #856
Fix MilaSub close #559
Fix KomikTapParser close #554
Fix YugenApp close #586
Fix TuMangaOnline close #566
Fix bato close #516
Fix Mangakakalot close #490
2 years ago
Draken cf5abdf7d3
Update HentaiVNParser.kt 2 years ago
Naga df562bedbb Closes #886 2 years ago
devi 11c1eafa0d Fix ReaperComics close #816
close #699
Fix GuildaTierDraw close #786
Fix Truyenqq close #793
Fix JiangzaiToon close #783
Fix MangaPark close #734
Fix ScanIta close #670
Add MangaPeak close #888
Fix ArvenComics close #660
2 years ago
Draken cc8369e02a
Update HentaiVNParser.kt 2 years ago
devi cd468df9ad Add : MangaTx.to - AlterkaiScans - StoneScape
Kalango - SolooScan - ErosScans - SolooScan
RaysScan - TemakiMangas - ZinChanManga
Urls Changes
Add volume on some parser
Fix ThunderScans close #818
NinjaScan close #753
Add Ngomik close #761
Remove dulicate source paragonscans
Fix LuminousScans close #578
Fix YugenApp close #854
Fix KaiScans close #614
Add PeachBl Close #884
Fix GalinhaSamurai close #835
Fix RocksManga close #827
Fix CrystalComics close #792
2 years ago
Draken 64666e42e8
Update HentaiVNParser.kt
not sure, i hope it will works...
2 years ago
Koitharu ad7c953d29
Merge pull request #883 from NagaYZ/fix-mangaworld 2 years ago
Naga b345efe2d1 fixed source mangaworld pages 2 years ago
Naga 74b8aaa94e fixed source mangaworldadult 2 years ago
Naga 1ace1ba3ec added source mangaworldadult, refactor mangaworld 2 years ago
Naga b822574b70 moved mangaworld in package 2 years ago
devi 466ca0f0e9 Merge remote-tracking branch 'origin/master' 2 years ago
devi 0bcac0c639 Add @Broken on some Dead Sources
Urls Changes
repair sources
Remove Duplicate Source
Add volume on some parser
2 years ago
Koitharu 7433fb8fa0
[LibSoc] Fix image server option 2 years ago
Koitharu b1ac1cf238
[LibSoc] Fix image server option 2 years ago
Naga a88a861d82 added main page sortby updated 2 years ago
Naga 41ced8edee added states , fixed title, added types 2 years ago
Koitharu 0f73f74539
[LibSoc] Default image server config option 2 years ago
Koitharu bb9902e3b2
[LibSoc] Add split by translations config option 2 years ago
devi c03b0fc981 Fix search on MangaDexParser
Fix State on ScansMangasMe
2 years ago
Koitharu e8733f15e4
Merge pull request #853 from NagaYZ/fix-mangaworld 2 years ago
Naga 26519c71a6 fix #852 2 years ago
Koitharu f923acc5a7
Extract MangaSource interface 2 years ago
Naga 55b9b6aac4
Merge pull request #26 from KotatsuApp/master
[pull] master from KotatsuApp:master
2 years ago
devi 52db07a33b MiHentai Closes #829
MrBenne Closes #627
Closes #555
Norterose Closes #625
Closes #564
Closes #483
MaidSecret Closes #623
Closes #550
HentaiOrigines
Fix SortOrder On MmrcmsParser
2 years ago
Koitharu 7ed8c9f787
[AllHen] Update domain 2 years ago
Koitharu cc12b193d3
[MangaWorld] Refactor 2 years ago
Koitharu 4d9c51bcd9
[MangaWtf] Parser 2 years ago
Naga 7de48393b6 added source MangaWorld 2 years ago
Naga 283aa92fd5 fixed url and chapters 2 years ago
Naga 9d0393ca72 update domain 2 years ago
Koitharu c5fc7f76d3
Merge pull request #845 from NagaYZ/fix-aquamanga 2 years ago
Naga 6b6325895b update aquamanga domain 2 years ago
Koitharu 39e9f5a2ff
Added UNKNOWN manga source constant 2 years ago
Koitharu 204223e2a8
Merge pull request #837 from NagaYZ/fix-baozimh 2 years ago
Naga b39abcc4a0 fix select chapter 2 years ago
SBS123 f49e9fa66b Update MangaPro.kt 2 years ago
scaledzdn 56fd22b43f src: id: shinigami: Change url to shinigamitoon.id
* Previously not working.
2 years ago
Koitharu 0b2bf607f7
Make locale non-nullable 2 years ago
Zakhar Timoshenko 3ff9e69585
[MangaOVH] Removal (closes #824) 2 years ago
Koitharu 9ceb90204c
Merge pull request #801 from OtakuArab/patch-3 2 years ago
SBS123 ab3c4b2b2d
Update and rename PewPiece.kt to Gmanga.kt 2 years ago
SBS123 060ca4b7ef
Update MangaLinkNet.kt 2 years ago
SBS123 a7a29ef029
Rename LinkManga.kt to MangaLinkNet.kt 2 years ago
SBS123 e7826c2570
Update src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/LinkManga.kt
Co-authored-by: Koitharu <nvasya95@gmail.com>
2 years ago
SBS123 77a733a062 Update and rename EnAresManga.kt to FlAres.kt 2 years ago
SBS123 96bd99650f Update and rename RocksManga.kt to YuriMoonSub.kt 2 years ago
SBS123 2d1899f1f2 Update TeamXNovel.kt 2 years ago
SBS123 42b1740b0f Update FlixScans.kt 2 years ago
SBS123 fbf613c4e6 Update and rename MangalinkParser.kt to LekMangaOrg.kt 2 years ago
SBS123 bf18070bc1 Update and rename MangaLekNet.kt to MangaLeko.kt 2 years ago
SBS123 c9c41fdd33 Create StellarSaber.kt 2 years ago
SBS123 949d822385 Delete src/main/kotlin/org/koitharu/kotatsu/parsers/site/madara/ar/StellarSaber.kt 2 years ago
SBS123 83b8f28055 Create MangaPro.kt 2 years ago
SBS123 c602d817d9 Create CrowScans.kt 2 years ago
SBS123 a50b217b6f Update and rename MangaLekCom.kt to LekMangaCom.kt 2 years ago
SBS123 23c031780f Update and rename Mangaatrend.kt to ManhaTok.kt 2 years ago
SBS123 e3b50d5dfa Update and rename KingOfShojo.kt to Hijalacom.kt 2 years ago
SBS123 9b1bee567f Update and rename KingOfManga.kt to MangaAtrend.kt 2 years ago
SBS123 3534973357 Create RocksManga.kt 2 years ago
SBS123 c4c4bdd547 Update Source 2 years ago
SBS123 528fadceb8 Update UmiManga.kt 2 years ago
SBS123 e15aecaa52 Update and rename BeastScans.kt to UmiManga.kt 2 years ago
SBS123 62e635ebe5 Update MangaTime.kt 2 years ago
SBS123 be846b43ae Update Mangaspark.kt 2 years ago
SBS123 7bfe168eee Update MangaLionz.kt 2 years ago
SBS123 e8d4a91ad7 Update MangaStarz.kt 2 years ago
SBS123 bc1804c030 Update and rename LikeManga.kt to LikeMangaNet.kt 2 years ago
SBS123 d18c9fe0da Update LikeManga.kt 2 years ago
SBS123 a7ea87dd8c Update AzoraMoon.kt 2 years ago
SBS123 e3a0ab8513 Update LikeManga.kt 2 years ago
SBS123 3a77144a5e Update LikeManga.kt 2 years ago
SBS123 69e311829b Update and rename MangaLike.kt to LikeManga.kt 2 years ago
SBS123 b1effdb7c7 Update and rename Azoranov.kt to AzoraMoon.kt 2 years ago
SBS123 124537eb18 Create ArAreaScans.kt 2 years ago
SBS123 b81924b144 Update Manjanoon.kt 2 years ago
Naga dd18605678 cleanup 2 years ago
Naga 6bce31d43f fix title selector 2 years ago
Naga 0da5b47ce5 fetching code using suspendlazy 2 years ago
Naga d0d891b112 Apply suggestions from code review
Co-authored-by: Koitharu <nvasya95@gmail.com>
2 years ago
Naga 2292c392e9 fix filters, added search support (#775) 2 years ago
Koitharu cc35aa6bc3 Broken parser annotation 2 years ago
SBS123 9e4a124a58
Update ScarManga.kt 2 years ago
SBS123 d63afb0917
Rename LikManga.kt to LinkManga.kt 2 years ago
SBS123 80a3f61a1f
Create ArAreaScans.kt 2 years ago
SBS123 303c817d2d
Update and rename MangalinkNet.kt to LikManga.kt 2 years ago
SBS123 d9cf94c6b5
Update LekManga.kt 2 years ago
SBS123 d72414d531
Update and rename AresManga.kt to ScarManga.kt 2 years ago
Koitharu 26be293f24
[ComicK] Fix volume & chapter numbers 2 years ago
SBS123 36b8633c1e Update and rename MangaLek.kt to LekManga.kt 2 years ago
Naga 6bdbf2c65e closes #795 2 years ago
Zakhar Timoshenko 80ec615548
[Comick] Fix "No value for hentai" 2 years ago
Koitharu ffc45ff79b
Merge pull request #777 from AwkwardPeak7/patch-1 2 years ago
AwkwardPeak7 48cb28d9f8
Update MangaFireParser.kt 2 years ago
Koitharu 51da0b62c1 Apply suggestions from code review 2 years ago
AwkwardPeak7 e391ed52f3 MangaFire: related manga author language filter 2 years ago
AwkwardPeak7 a5e6e4255d MangaFire: tag exclusion 2 years ago
AwkwardPeak7 590e7e3ba3 add MangaFire 2 years ago
Koitharu d218ad5a67
[MangaReaderTo] Syncronize descrambling 2 years ago
Koitharu 350bc0ad58
Small refactor 2 years ago
AwkwardPeak7 a0f9bc032d cleanup and test impl 2 years ago
AwkwardPeak7 4edcd70871 simplify api 2 years ago
AwkwardPeak7 90c0bf46f4 image redrawing api 2 years ago
AwkwardPeak7 9d4fc1980f MangaReader.To 2 years ago
Naga 332524f1f3 new source added modescanlator, closes #759 2 years ago
Koitharu 078b59b1e2
[Madara] Migrate to ScatterSet and add UserAgent config option 2 years ago
Koitharu 0551ed5f0b
[MangaDex] Chapters fixes 2 years ago
Koitharu 915d4093b9
Fixes 2 years ago
Koitharu 952e9c39ac
[LibSocial] Rewrite parsers 2 years ago
Koitharu 3e32a6280a
[LuratoonScan] Rewrite parser 2 years ago
Zakhar Timoshenko 288b67e250
Close #741 2 years ago
Zakhar Timoshenko 7d2f5696f5
Close #719 2 years ago
Zakhar Timoshenko 8c3fec0933
Close #677 2 years ago
Koitharu 33b00fe65f
Fixes #728 #725 2 years ago
Koitharu f0f50a37b5
Fixes batch #733 #730 #731 #727 2 years ago
Koitharu c2b2148190
Upgrade gradle 2 years ago
Koitharu f22362dc53
[Grouple] Fix alt titles parsing 2 years ago
Koitharu 68cc1d4c4f
[DesuMe] Improvements 2 years ago
Zakhar Timoshenko 0f84ef1e58
[Desu] Fix list loading 2 years ago
Naga a245574dee fix #702 2 years ago
Koitharu fd90970173
[MadTheme] Improve pages parsing 2 years ago
Koitharu 75a55e4748
[SinensisScans] Update domain #674 2 years ago
Koitharu bbd4867830
[FbSquads] Update domain #681 2 years ago
Koitharu 3463c8a49e
[HeanCms] Fixes 2 years ago
Koitharu 7829a2ad3b
[MadTheme] Fix pages parsing #701 2 years ago
AwkwardPeak7 fb387dbcd9 FreakComic: update selectors 2 years ago
Koitharu cd0b3cd1dc
Update gitignore 2 years ago
Koitharu fe500a27c0
Update dependencies 2 years ago
Koitharu da860bc250
Fix imports 2 years ago
Koitharu e3a8eeb647 Update src/main/kotlin/org/koitharu/kotatsu/parsers/site/heancms/en/OmegaScans.kt
Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
2 years ago
Naga 6dbc709f98 fix #687 2 years ago
AwkwardPeak7 49ca54d68a MangaGalaxy: move to MangaReader 2 years ago
AwkwardPeak7 e81b8127af DrakeScans: move to MangaReader 2 years ago
AwkwardPeak7 d1b490ed98 add german to MangaPlus 2 years ago
AwkwardPeak7 893ed6afd2 999Hentai: fix api url 2 years ago
Zakhar Timoshenko 9df2cf5bd7
[AstrumScans] Source removal on request (closes #682) 2 years ago
Zakhar Timoshenko b5ceaf4e65
Add OtakuWorld to README 2 years ago
Naga 44ea9fe709 fix #667 2 years ago
Koitharu 8174229d28
Merge pull request #658 from NagaYZ/fix-xoxocomics
fix chapter order
2 years ago
Zakhar Timoshenko f628ed2e1b
Add kotatsu-dl to README 2 years ago
Zakhar Timoshenko c90a0a77bc
Update README 2 years ago
Naga 10b3f2e65b fix chapter order 2 years ago
Koitharu 9821e93d25
Merge pull request #655 from NagaYZ/fix-xoxocomics
fix #652
2 years ago
Naga 6c16c9c55f fix #652 2 years ago
Koitharu ab679bccc6
Merge pull request #647 from NagaYZ/fix-issue
Fix Omegscans
2 years ago
Naga 6fcf3785ef fix #591 2 years ago
Naga e605b325c7 fixrating 2 years ago
Naga a714d0927e fix OmegaScans tags 2 years ago
Naga fe1ef89d30 fix OmegaScans site structure changed 2 years ago
Koitharu 639895f511
MangaOVH: Parsing related manga 2 years ago
Koitharu 4fdc02de35
MangaOVH: Fix handling unexisting branches 2 years ago
Koitharu 103ef11f3d
Dynamic UserAgent support 2 years ago
Koitharu 69e7efe6d1
NHentai: configurable User-Agent 2 years ago
Koitharu 83e971d85b
Update UserAgents 2 years ago
Koitharu fec60955ed
Merge pull request #631 from NagaYZ/fix-issue
Fix source domain and issue
2 years ago
Koitharu c5b980a406
Update issue templates 2 years ago
Koitharu 99483268d6
Remove Bakai parser #629 2 years ago
Naga 67fef2cc1c fix mangatown select and pagination 2 years ago
Naga f450111c1c fix Baozimh webview and 50+ page chapter 2 years ago
Naga 718e7eab82 fix source domain 2 years ago
Koitharu eb031b00ab
ComicK: Update domain 2 years ago
Koitharu a10c1101ab
New source: MangaOVH 2 years ago
Naga 9c0c20f86b Merge remote-tracking branch 'upstream/master' 2 years ago
Koitharu c6d1f1b525
[NepNep] Refactor #628 2 years ago
Koitharu 9a84791f5c
Merge pull request #628 from NagaYZ/feature-manga4life-sort
Feature manga4life sort
2 years ago
Koitharu 14fc02cb23
[ReaperComics] Refactor #620 2 years ago
Koitharu 39ae6a406c
[Grouple] Added Seimanga support 2 years ago
Koitharu ba8682f79e
Merge pull request #620 from NagaYZ/feature-search-reaper
Feature search reaper
2 years ago
Naga 986e4d8fcc
Merge pull request #1 from KotatsuApp/master
[pull] master from KotatsuApp:master
2 years ago
Naga 016ced24e0 added pagination 2 years ago
Naga b60b2d8355 added sort by popularity/updated 2 years ago
Naga f733b85878 fix webtoon sort newest 2 years ago
Naga 1926a73dee without serialization, and added cache 2 years ago
Koitharu 071f4f0911
Trigger rebuild 2 years ago
Koitharu 813c5236b3
[Grouple] Back to the old chapters parsing behavior with option 2 years ago
Naga f39a9f191a cleanup 2 years ago
Naga 6d8d757798 fix manga chapters list 2 years ago
Naga 81975977ad added search support for reaperscans 2 years ago
Koitharu b7613606c0
[TuMangaOnline] Fix regexp 2 years ago
Koitharu 0aa4ea01f7
[Grouple] Multiple translations support 2 years ago
Koitharu 20685598e3
[TuMangaOnline] Fix pages parsing 2 years ago
Koitharu b1fb1bdc6b
Merge pull request #615 from NagaYZ/feature-search-by-number
Feature search by number
2 years ago
Naga a80bdcc611 full title name 2 years ago
Naga 4571e3e001 gitignore 2 years ago
Naga 38d7d97167 added search by id 2 years ago
Koitharu 3913e95b53
Merge pull request #608 from nghduc97/fix-mangabox-parser-white-spaces
fix mangaboxparser white space coding in search query
2 years ago
Koitharu 954c926a9f
Merge pull request #588 from NagaYZ/feature-webtoon-original
Feature webtoon original
2 years ago
nghduc97 0973444c47
fix mangaboxparser white space coding in search query 2 years ago
Naga 368b61c21a fixed suggestions 2 years ago
Naga efc9b3502c fixed suggestions 2 years ago
Naga 2e86c480ec
Update src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/WebtoonsParser.kt
Co-authored-by: Koitharu <nvasya95@gmail.com>
2 years ago
Naga 39eeaacdcb fixup! trying to improve speed 2 years ago
Naga ea45e91aa7 trying to improve speed 2 years ago
Naga a65e41e796 fix 2 years ago
Naga 91648756c9 added more language 2 years ago
Naga a225234800 fixed chapter date 2 years ago
Naga de58333dc4 fixup! fixup! fixed offset 2 years ago
Naga 24a25e3dbf fixup! fixed offset 2 years ago
Naga 8e5ae51067 fixed offset 2 years ago
Naga 86c981ae8e fixup! trying to speed up loading 2 years ago
Naga b716c35438 trying to speed up loading 2 years ago
Naga 9e984510d4 added caching for title list to speed up loading 2 years ago
Naga 3c08119ccc added caching for title list to speed up loading 2 years ago
Naga ada1b7b54c fixed sort descending 2 years ago
Naga a7ed4fbcc3 fix import 2 years ago
Naga 1330feaea5 added webtoon original english support 2 years ago
Koitharu 103f578c61
Merge pull request #579 from NagaYZ/master
Update domains name
2 years ago
Koitharu deee80ae71
Merge pull request #581 from NagaYZ/fix-popular-page-url
Fix popular page url
2 years ago
Naga 54350040ad fixed popular page url 2 years ago
Naga 3472b8ba00 Update ZinManga.kt 2 years ago
Naga f2dd9af66c Update MagusManga.kt
fixed domain name and listurl acces
2 years ago
Naga da46f7967a fixed bunch of domain name 2 years ago
Naga ed3d6c5f98 Replaced GloryScans/GloryScansX with LunarScans/LunarHentai
domain changed
2 years ago
Naga c4ea825df9 Replaced LightScans with Luascans , domain name changed 2 years ago
Naga 4120e3946c Update RagnarokScanlation.kt
new domain: ragnarokscanlation.net
2 years ago
Naga c84b6acff2 Update EmperorScan.kt
new domain : emperorscan.com
2 years ago
Naga 159110c032 Update ResetScans.kt
new domain: reset-scans.xyz
2 years ago
Naga 93e1b38da2 Update MangaEsp.kt
new domain : mangaesp.net
2 years ago
Koitharu 3ff028c4e9
Merge pull request #572 from geexuan/baozimh_details_fix
Baozimh: Fix for not getting all chapter details
2 years ago
geexuans 9b46963015 Baozimh: Fix for not getting all chapter details 2 years ago
Koitharu 09e1c14f37
Merge pull request #565 from sandeepjak2007/Issue##561
Issue #561 Leading to non existing page
2 years ago
Sandeep Karanam 58af01b90a Issue #561 Leading to non existing page
Issue #561 fixed
2 years ago
Koitharu e338237e2a
[NudeMoon] Fix pages loading 2 years ago
Koitharu 014ea5ef49
[Manganato] Update domain 2 years ago
Koitharu 4a8c7fa36a
Fix urlDecode usage 2 years ago
Koitharu 1251a6cd75
Merge pull request #548 from Ah3ron/patch-1
Fix tag filtering for Russian characters
2 years ago
Ah3ron 0ce8eae969
Fix tag filtering for Russian characters 2 years ago
devi 8b9c76fca5 LerYaoi #512
StellarSaber #533
Update Url ( #530 )
Remove dead sources
2 years ago
Koitharu 52e4e6b802
Update collections library 2 years ago
Koitharu 57c9d26916
Fix android compatibility 2 years ago
Koitharu 19d7003a9e
[BatoTo] Fix pages parsing #521 #522 2 years ago
Koitharu b11e50ab22
Fixes batch 2 years ago
Koitharu a03ab0e0c0
Update tests 2 years ago
Koitharu 05d4db00b3
[ResetScans] Fix chapters names #518 2 years ago
Koitharu f4aaa14fc5
Simplify workflow 2 years ago
Koitharu a8f9423307
Migrate chapter number to float 2 years ago
Koitharu 7c871edbca
Merge pull request #503 from mangadventure/master
Add MangAdventure sources (Arc-Relight, Assorted Scans)
2 years ago
ObserverOfTime f7d9579cee
Relax pagination test 2 years ago
ObserverOfTime b7e6ca8a26
Add MangAdventure sources
- Arc-Relight
- Assorted Scans
2 years ago
Zakhar Timoshenko 8852d1e22e
[Remanga] Attempt to fix `No value for page id` 2 years ago
Koitharu fcaa0ea442
Add volume field to chapter 2 years ago
Zakhar Timoshenko 8e7d7e0bde
Add some sources 2 years ago
scaledzdn 50c6c9f842
src: id/shinigami.kt: Change url source (#481)
* Previous url got banned on Indonesia
2 years ago
Koitharu a2979753a9
[Grouple] Fix pages parsing 2 years ago
Koitharu 789e39b6cb
Fixes batch 2 years ago
Zakhar Timoshenko 2f5441ef20
[Remanga] Use home url as `authUrl` 2 years ago
devi 6ad08f13d4 Add Comic1000, ThunderScans, WhaleManga, SussyScan, EDoujin, 3XYaoi, SussyScan #444
Url TeamXNovel, WestManga
Remove AzureManga, BabelToon, FranxxMangas, LsHistoria, MangaGenki, Wowomik, MiraiScans, Nonbiri, PeaceScans, ReaperScansTr, TurkToon
2 years ago
devi 656f621711 Remove bad import 2 years ago
devi 6dc0e12fb0 add filter on sources
Remove Komikgo redirect to ManhwaLand
Add Template PizzaReaderParser and move FmTeam
Add PhoenixScans, TuttoAnimeManga, LupiTeam, LadyEstelarScan, GtoTheGreatSite
2 years ago
Koitharu 0e8579017b
[BatoTo] Fix pages parsing 2 years ago
devi e03d0efe71 Fix FlixScansOrg and add filter 2 years ago
Koitharu 0b497c4e0b
[ComicK] Fix branches names 2 years ago
devi 704f589b6d Add new filter on sources
Url GekkouScans, SssScanlator
Add LunarScan #435
2 years ago
Koitharu 3feb84ac9e
Update some domains 2 years ago
Koitharu e375654009
[ComickFun] Fix branches and titles 2 years ago
Koitharu ce7ac5d8f4
[ExHentai] Fix tags and branch 2 years ago
devi a172fa5a5b Add new filter on sources
Add BurningScans
2 years ago
Koitharu 7c89f53988
[ExHentai] Improve tags loading 2 years ago
Zakhar Timoshenko 4e2d739840
[Shinigami] Add user-agent changing option 2 years ago
devi 5cdbda700b Urls : DomalFansub, HentaiVN, PheTruyen, TruyentranhLH
Add : KomikSay, MangaLesen
Remove : 1stManhwa, ConsejoDeMatones, OzulScans, SugarLab, HyoManga, Mi2Manga
2 years ago
Koitharu b274b51699
Improve filtering 2 years ago
Koitharu efb5d34279
Merge pull request #431 from KotatsuApp/ExcludeTags 2 years ago
devi 9ecb75b51c Add ZScanlation, KomikGesn , Kenhuav2Scan
Fix MangaMate
2 years ago
Koitharu 69332a85da
Add isTagsExclusionSupported flag 2 years ago
Koitharu ea095084cc
Fix some parsers 2 years ago
Koitharu 1f176ebab2
Update dependencies 2 years ago
devi 327c05d03e Fix Chapters madtheme
Url NettruyenBing
Remove Dead Sources :
AkuManga , CmReader, FalconManga, JaiminisBox, Manga3s, MangaCv, MangaLeks, UniToonOficial , 1stKissManga.tv, Manhwa2read, ManhuaMix, ManhuaDex, Hwago, ImmortalUpdates, KomikSay, MangaTx, Scan-Fr.org, QueenScans, PhantomScans, OpScanlations, NeuManga.net, ManhwaFreak.fr, Lady Estelar Scan, KomikManhwa, KomikManga, KaratcamScans, Boosei, LyraScans, MangaDiyari, MangaPro, NHentai.uk, Oxapk, SpiderScans, MangaNelo.biz, MangaOnline.team, SpartanManga
2 years ago
devi 2d7c120d19 Add Feature Content Rating Filter 2 years ago
devi a2e9f19814 Fix Build 2 years ago
devi c602355e65 Feature Add Exclude Tags 2 years ago
devi a390e0de49 Fix ALPHABETICAL Sort on BentomangaParser
Remove Toonily.net
Add MonzeeKomik, Tresdaos
2 years ago
Koitharu a228d71d57
Merge pull request #429 from KotatsuApp/Alpha-zaAndStateUpcoming
Add a new filter and a new state
2 years ago
devi b2f0eaccc4 Remove Import Local 2 years ago
devi 07aaec7d3c Add template Scan and move ScanVfOrg in
Add ScanIta

Remove Mangastic -> redirect to toon69
Remove MangaGoYaoi -> redirect to asurascans.us
Remove Cizgiromanarsivi -> redirect to mangaoku.info
Remove WebtoonEvreni -> redirect to merlinscans.com
Remove GalaxyAction -> redirect to flixscans.com
Remove GalaxyAction -> redirect to kingofshojo.com
Remove aquamanga.live -> redirect to kingofshojo.com
Remove MangaReaderPro -> redirect to hadesnofansub.com

Urls : S2Manga.io, KomikzoId, Toon69, ReaperComics, ShadowXManga, TeamXNovel, YaoiFlix, MangaEmpress, MangaMukai.com, komikRemaja.cfd, MangaKyo, KomikMirror, ManhwaDesu, ManhwaIndo.sbs, ManhwaLand, Wowomik, LireScanVf.com, NhattruyenPlus
2 years ago
devi 392e9af944 New SortOrder.ALPHABETICAL_DESC
New MangaState.UPCOMING
2 years ago
Koitharu 904e0719eb
[HitomiLa] Fix tag names 2 years ago
Koitharu 52ac3d2e6c
Merge pull request #428 from AwkwardPeak7/hitomi
HitomiLa: fix thumbnails
2 years ago
devi 859b07e454 Add Ler999, ShadowCeviri 2 years ago
AwkwardPeak7 d4b252fd71
HitomiLa: fix thumbnails
I dismissed this in their scripts as the thumbnails worked fine but now they broke
2 years ago
Koitharu 4a0e7221b0
[HitomiLa] Refactor 2 years ago
Koitharu 43bdbe5a01
Merge pull request #426 from AwkwardPeak7/hitomi
Hitomi.la
2 years ago
Koitharu 3d10456a87
Apply suggestions from code review 2 years ago
Koitharu d5a6c95ffb
[MangaInUa] Fix chapters loading 2 years ago
devi edc07b5dbd New Template GuyaParser
Add DankeFursLesen, GuyaCubari, Hachirumi, MahouShoujobu, Olaoe
Add local on sources GattsuParser
2 years ago
devi a5219ceb6c New Template GattsuParser
Add Baozimh, MuitoHentai, TopReadManhwa, HentaiSeason, HentaiTokyo, MundoHentaiOficial, UniversoHentai
2 years ago
AwkwardPeak7 039075086e
hitomi: better locale in tag search 2 years ago
AwkwardPeak7 495c9fad33
hitomi: formatting 2 years ago
AwkwardPeak7 b61c5e8f12
hitomi: locales 2 years ago
AwkwardPeak7 a40a8d329a
simplify search a bit 2 years ago
AwkwardPeak7 3f3ab5a7be
Hitomi.la 2 years ago
devi d8c32047d0 Fix MangaATrend
Add LerManga
Remove ReaperScansPt.kt
2 years ago
devi f5f2854f36 Add OnePieceEx 2 years ago
devi 86a2b95141 Add Comicaso, LectorManga, Mangaoku, MangaSehri.net, MangaTr, TempestFansub.net
Fix KomikCast
Url ElarcPage , InfraFandub
2 years ago
Koitharu 4db7ccc853
Tiny refactoring 2 years ago
Koitharu b1da5c2924
[Mangaowl] Fix chapters parsing 2 years ago
devi 0fe6fed838
Merge pull request #418 from PhantomShift/patch-1
Fix MangaDex nsfw check
2 years ago
PhantomShift a3f8c0a236
Fix MangaDex nsfw check
Additionally check for pornographic content rating when labeling a work as NSFW.
2 years ago
devi c88d7de138 Add AComics, ReaperComics ( en ) 2 years ago
devi eeafb3c6fd Fix JiangzaiToon #417 2 years ago
devi 0208f5be97 Add DatgarScanlation, HyoManga, HeckScans, MerlinScans, TraduccionesMoonlight, NinjaScan, Mi2MangaEs, ScanVfOrg
Url InariManga
Fix chapters MangaGeko
2 years ago
Koitharu 42ecf8a958
Merge pull request #413 from KotatsuApp/ZeistMangaParser
Add ZeistMangaParser
2 years ago
Koitharu d780623b98
Merge pull request #406 from AwkwardPeak7/mplus
MangaPlus
2 years ago
devi 1b1a167580 Add EpikMan 2 years ago
devi 49d0c80070 Add MaxgsScan, WolfScanBr 2 years ago
devi a35c2f3394 Add ZeistMangaParser and sources
add urldecode
ul VfScan
2 years ago
AwkwardPeak7 3034a87929
MangaPlus: divide different languages to avoid too many calls during chapter fetching & prevent some non-english titles to be filtered out 2 years ago
AwkwardPeak7 33b96043c9
Merge remote-tracking branch 'upstream/master' into mplus 2 years ago
devi 4d02dc0d12 Move FlixScansOrg to En
add MangasOnline , WorldManhwas
Url Shinigami
2 years ago
AwkwardPeak7 79fd2a58c1
MangaPlus: improvements 2 years ago
AwkwardPeak7 ab82d0eba0
MangaPlus 2 years ago
Koitharu 75cc0716fd
Merge pull request #404 from AwkwardPeak7/999 2 years ago
Koitharu 87f99addbb
Update SuspendLazy 2 years ago
AwkwardPeak7 3e1b15a23c
review changes 2 years ago
devi 5758e9f68f Add BentoScan, Manga18.xyz, NewManhua
Url MangaScan, Anime-Sama.me
Fix DoujinDesu.tv
2 years ago
AwkwardPeak7 0964c02a44
largeCover and better chapter branch name 2 years ago
AwkwardPeak7 bf06ab7492
use `SuspendLazy` for fetching cdn host 2 years ago
AwkwardPeak7 e992566899
999Hentai 2 years ago
devi 97338f31c5 Add IrisScanlator #403
Add RagnaScan
Fix VexManga
2 years ago
devi 0ff4f976e1 Fix FlixScans
Add FlixScansOrg
Add DoujinKu
2 years ago
devi 496b3388ee Add source : MangaPark , GekkouScans, YuriLab 2 years ago
devi d6b578acb0 Add, fix sources
Manual addition of lang on Bato.To
Add multi tag , lang and rework tags on exhentai
Add lang on imhentai
2 years ago
Koitharu b3be87f541
Update readme 2 years ago
devi 786cd12dba Add, fix sources
Add language on Bato.To
2 years ago
devi a1a17c02d7 Add, fix sources
Add language on Bato.To
2 years ago
devi b8caf8e572 Minor update. 2 years ago
Koitharu 44c2c074a8
[MangaDex] Language filter support 2 years ago
Koitharu 3ed960254d
[GalleryAdults] Language filter support #357 2 years ago
Koitharu f1e8fbec5d
Merge pull request #401 from KotatsuApp/filter
Rework getList
2 years ago
Koitharu c5ed9cf54b
Merge pull request #400 from deividgabrielpeira/master
Neoxscan change url
2 years ago
devi 65c567dbcb Fix NHentaiParser 2 years ago
devi b312823afd Merge remote-tracking branch 'origin/master' 2 years ago
devi 56eb059bcd fix getlist VmpParser, LineWebtoonsParser, BlogTruyenParser, HentaiVNParser, YurinekoParser
add filter on WpComicsParser, ZMangaParser, LxManga, Truyenqq, TruyentranhLHParser

Move NetTruyen to template WpComicsParser and add multitags on WpComicsParser
2 years ago
devi 6ad78f1cba fix getlist OtakuSanctuaryParser, bakai , brmangas, LerMangaOnline, MangaOnline, YugenMangas, MangaAy, SadScans, YaoiFlix
add filter on SinmhParser, TrWebtoon
2 years ago
devi 26b758ae50 fix getlist some source mangaeader, MmrcmsParser
add filter on NepnepParser
2 years ago
devi 019b8cc087 fix getlist some source mangaeader, MmrcmsParser
add filter on NepnepParser
2 years ago
Deivid Gabriel Pereira de Oliveira c95845083a Neoxscan change url 2 years ago
Zakhar Timoshenko 9c7f314f65 [HentaiUkr] Add usage of `objects69.json` 2 years ago
devi 743a92a3e8 remove import 2 years ago
devi 37e9b33c24 add filter : LikeMangaParser, FmTeam, MadthemeParser , MangaReaderParser
Fix GetList : NicovideoSeigaParser, Manga18Parser

add filter & multitags on MangaboxParser
2 years ago
devi 7c2ac033d7 add filter on HeanCms , DoujinDesu.tv
Fix GetList GalleryAdultsParser
2 years ago
Koitharu 0efd5437f9
[HentaiUkr] Fix objects loading 2 years ago
devi db770351f3 add filter on FmreaderParser, LugnicaScans
Fix GetList on FoolSlideParser , FuryoSociety , LegacyScans, LireScan, ScansMangas.me, ScantradUnion
2 years ago
devi 14b2457627 fix getlist TempleScanEsp
fix getlist and add tag on Details mangas TuMangaOnline
2 years ago
devi f62ea43de8 fix getlist on AnimeBootstrapParser, ImHentai, beetoon, CloneManga, MangaGeko, Po2Scans, Pururin
add filter FlixScans, TeamXNovel, ComicExtra, MangaOwl.to, MangaTown, Manhwa18.net, ManhwasMen

fix getlist and add tag list on DynastyScans
fix getlist and add SortOrder.UPDATED on MangaStorm
2 years ago
devi ab2aad6bd0 add filter on bentomanga
add filter on ninemanga
2 years ago
devi 1293233ad9 fix getListPage Some sources madara 2 years ago
devi 5fff2adbfb MadaraParser add filter.states
- Some sources madara, need to be changed too
2 years ago
devi 7d67b718a5 Mangadex add filter.states and info states 2 years ago
devi b8ca21d588 unwanted change 2 years ago
devi 44d91e43ad Add altName on ComicK
Change oneOrThrowIfMany on Bato.To
2 years ago
Zakhar Timoshenko 3bb10ee948 [HentaiUkr] Url hotfix 2 years ago
devi 81caa59a36 Add status filter support
Add oneOrThrowIfMany() on MangaState
2 years ago
Koitharu 46e863ef79
[ComicK] Fix manga state 2 years ago
Koitharu cecba7623e
[ComicK] Fixes and status filter support 2 years ago
Koitharu 7dee865c8a
Merge pull request #397 from deividgabrielpeira/master
Daily Update (extensions portuguese)
2 years ago
Deivid Gabriel Pereira de Oliveira 5ac3cdb03c Arkham Scan -> HuntersScan (Fusion) 2 years ago
Koitharu c3613f3ba4
[Grouple] Migrate to the new getList 2 years ago
Koitharu e7ac1c1feb
Fix PagedMangaParsers 2 years ago
Koitharu 24573d12f5
Fix compiling 2 years ago
Koitharu 9d2f633e14
Merge branch 'feature/filter-ex' 2 years ago
Koitharu 570b0c19c5
Merge pull request #388 from deividgabrielpeira/master
Daily Update (extensions portuguese)
2 years ago
Deivid Gabriel Pereira de Oliveira a11e42a4d6
Merge branch 'KotatsuApp:master' into master 2 years ago
devi 410c73a74f Add multi tag some sources
Add SortOrder.POPULARITY some sources
Add all language on GalleryAdultsParser
fix YugenMangas (pt)
2 years ago
devi 3ea088371b Minor daily update 2 years ago
Deivid Gabriel Pereira de Oliveira 09a0bb364a HentaiSSSSSS (is closed) 2 years ago
Deivid Gabriel Pereira de Oliveira d91947ae15 NocturneSummer fix? 2 years ago
Deivid Gabriel Pereira de Oliveira 0a8a9e5935 NocturneSummer fix 2 years ago
Deivid Gabriel Pereira de Oliveira 59226b298f apenasmaisumyaoi fix 2 years ago
Deivid Gabriel Pereira de Oliveira dc41bf3e83 ArthurScan Fix 2 years ago
Deivid Gabriel Pereira de Oliveira a790a18f7f test 2 years ago
Deivid Gabriel Pereira de Oliveira 1fcc1096f5 ArthurScan add Catalog (pt) and (test) 2 years ago
Deivid Gabriel Pereira de Oliveira 86aaca271f
Merge branch 'KotatsuApp:master' into master 2 years ago
Deivid Gabriel Pereira de Oliveira b8cc2d6953 Animaregia (Down) 2 years ago
Koitharu 11f110b0bb
Merge pull request #385 from KotatsuApp/add-hiatus
add hiatus
2 years ago
Deivid Gabriel Pereira de Oliveira 31459ea6e7 yaoitoshokan (closed the site) 2 years ago
Deivid Gabriel Pereira de Oliveira 7ab84688c2 LerYaoi (down), posting in telegram 2 years ago
Deivid Gabriel Pereira de Oliveira d18f287804 NocturneSummer fix 2 years ago
Deivid Gabriel Pereira de Oliveira 1c3b41acba Nocsummer, yaoi site, necessary (ContentType.HENTAI) 2 years ago
Deivid Gabriel Pereira de Oliveira ecfc7e40c9 HiperCool, change url 2 years ago
Koitharu 41eea1c420
Set jvmTarget to 8 2 years ago
Deivid Gabriel Pereira de Oliveira 594524afc3 apenasmaisumyaoi change name, possibly the url will be changed 2 years ago
Deivid Gabriel Pereira de Oliveira 70a5337604 AkumanoTenshi -> tatakaeScant (fusion) 2 years ago
Deivid Gabriel Pereira de Oliveira 924fde8790 CeriseScans, change url 2 years ago
Koitharu dea70974bf
Revert unwanted changes 2 years ago
Koitharu 43fd7b8d47
[HoneyManga] Fix images urls 2 years ago
devi 6c2a526aeb rename 2 years ago
Koitharu 43a8caa687
Advanced filtering support 2 years ago
devi f3fc848b6c Minor daily update 2 years ago
devi 98f5d115b3 Minor daily update 2 years ago
devi 885d98cebd Minor daily update 2 years ago
devi 6f73ff23e8 Minor update 2 years ago
Koitharu 2cd2d7dbc4
Merge pull request #368 from deividgabrielpeira/master
Update Daily
2 years ago
devi d3afdad95b Minor update 2 years ago
Deivid Gabriel Pereira de Oliveira 48ac46eb5c chance url VexManga : Source url changed #370 2 years ago
Deivid Gabriel Pereira de Oliveira 2d6e6b359b they'll shut down leitorizakaya, they'll switch to blackoutcomics, it makes more sense to put it on madara, because they've made their own website. 2 years ago
Deivid Gabriel Pereira de Oliveira 7462743be8 se não funciona, so na proxima 3 years ago
Deivid Gabriel Pereira de Oliveira db2acb752a fix 3 years ago
Deivid Gabriel Pereira de Oliveira 335229e61b fix error thanks @ztimms73 3 years ago
Deivid Gabriel Pereira de Oliveira 2acdd66f70 fix again 3 years ago
Deivid Gabriel Pereira de Oliveira d3bec9006e Fix uppercase in parser 3 years ago
Deivid Gabriel Pereira de Oliveira 44f70c1388
Merge branch 'KotatsuApp:master' into master 3 years ago
Koitharu 02463e5833
[MangaReader] Fix NetShield bypass 3 years ago
Deivid Gabriel Pereira de Oliveira 778d14a1a2 change mangaSource 3 years ago
Deivid Gabriel Pereira de Oliveira 8b3814c485 , 3 years ago
Deivid Gabriel Pereira de Oliveira 6bcd9dd65c mudou de nome 3 years ago
Koitharu 154ae09c6e
[MangaReader] Add NetShield bypass (testing) #198 3 years ago
devi 58c9d14d42 Minor daily update 3 years ago
Koitharu 3e69b88518
Update dependencies 3 years ago
devi 9d3671e117 Minor daily update 3 years ago
devi 0731d2487a Minor daily update 3 years ago
devi 5e4dd82b8d Minor daily update 3 years ago
devi 9d33d1a560 Minor daily update 3 years ago
devi e788cff04a Minor daily update 3 years ago
Koitharu face1d5b26
Merge pull request #355 from KotatsuApp/GalleryAdultsParser
Add GalleryAdultsParser
3 years ago
devi 18f8472baa Add GalleryAdultsParser
reworking some sources
add sources
3 years ago
devi f14edd871f Minor daily update 3 years ago
Koitharu c892a38c52
Trigger rebuild 3 years ago
devi e9d473711d Minor daily update
- Fix tag ninemanga
- Fix search neonep parsser
3 years ago
devi 97a8bb1ca7 Minor daily update 3 years ago
devi 66d47bf579 Minor daily update 3 years ago
devi 508647eb5a Minor daily update ( and remove warning for name ) 3 years ago
Koitharu 4ca3a492b0
[ComicK] Fix genres 3 years ago
devi fffe104cbe Minor daily update 3 years ago
devi 6258476a58 Minor daily update 3 years ago
devi af1aca8725 Merge remote-tracking branch 'origin/master' 3 years ago
devi bdb7fbc039 Minor daily update 3 years ago
Koitharu 6bf0ae92e4
[*chan] Fix sort orders 3 years ago
Koitharu 17b8fd51b7
[ComicK] Fix chapters numbers 3 years ago
Koitharu d089fad6ce
[MangaLib] Add branching support 3 years ago
Koitharu ec11b79b18
[MangaLib] Fix search result covers 3 years ago
Koitharu 0f216c6de9
[Remanga] Fix loading large amount of chapters 3 years ago
devi 0606ace83a
Merge pull request #328 from deividgabrielpeira/master
HentaiGekkout(pt)
3 years ago
Deivid Gabriel Pereira de Oliveira 746d857550 I forgot about Hentai Gekkou, which is part of Gekkou, which was taken down together by the DMCA. 3 years ago
devi cf1d0b8d7e
Merge pull request #327 from deividgabrielpeira/master
GekkouScan(pt) and FinalScans(pt), is down
3 years ago
Deivid Gabriel Pereira de Oliveira d94145c6e5 GekkouScan(pt), has been taken down by the DMCA
FinalScans(pt), just closed
3 years ago
devi b71a0ac3cf Minor daily update 3 years ago
devi 32afa26be5 Minor daily update 3 years ago
devi 23cc86e429 Minor daily update 3 years ago
devi 87032a6835 Minor daily update 3 years ago
devi a4d9c6e36c Minor daily update 3 years ago
devi 76af0b0edb Minor daily update 3 years ago
Koitharu 0054d06e6e
Fix sources titles 3 years ago
devi e2e027df81 Minor daily update 3 years ago
Koitharu a61e441e79
Merge pull request #283 from KotatsuApp/devi4
add sources and fix
3 years ago
devi 9b4153aacc fix 3 years ago
devi b1d2d0a475 add Fakku 3 years ago
devi aeec98db2a Rework tag hentaivn 3 years ago
devi 00f21de9eb quick fix 3 years ago
devi 2d595bb6ee Add small sources, fix , rename sources , remove code not used, remove dead sources, typo. 3 years ago
devi e43c9f6489 Add small sources, fix , rename sources , remove code not used, remove dead sources, typo. 3 years ago
devi 81004b53a9 add Yaoi Flix 3 years ago
devi 85a0918da5 add Manga Ay 3 years ago
devi b10fa0fd10 add change requested and fix url 3 years ago
Koitharu a2f4bb35cd
[*chan] Fix title parsing 3 years ago
devi 4120514256 add search on manga online 3 years ago
devi 29ef717ece add sources and fix 3 years ago
devi d34cfbf405 add sources and fix 3 years ago
devi 94c2326153 Rework Yugen Mangas 3 years ago
devi 06c2287b71 fix 3 years ago
devi 8cb0d3d6fe Tag reduction on Pururin 3 years ago
devi 76860f82b9 add sources and fix 3 years ago
devi f134137a05 add sources and fix 3 years ago
devi fc231ec330 fix Asura Scans 3 years ago
devi d3c32faf01 Tag reduction on hentaifox 3 years ago
devi d16f4cd892 add ImHentai 3 years ago
devi 9a8de28a1d add Pururin 3 years ago
devi 939f08b8a1 add Mirai Scans 3 years ago
devi 09500c0734 add sources and fix 3 years ago
Koitharu 400a90464e
[LineWebtoons] Refactoring 3 years ago
Koitharu 9103f9d473
Merge pull request #273 from DCNick3/line-webtoons-source
Add Line Webtoons sources (webtoons.com)
3 years ago
devi 27c29f99d9 minor fixes and add small sources 3 years ago
Nikita Strygin 753c27a90c Clean up code after review
- Do not use ConfigKey.Domain for supplementary domains
- use IllegalArgumentException
- use "$var" instead of "${var}" where possible
- fix the language in `publicUrl`
- use `?` to handle `largeCoverUrl`
- use `requireNotNull` instead of `!!`
3 years ago
Koitharu 3d7e62d2fe
[MintManga] Update domain 3 years ago
Koitharu e8289277a7
Merge pull request #274 from KotatsuApp/devi3
Add sources
3 years ago
Nikita Strygin 887fc691d1 Add support for more languages in Line Webtoons parser
Also change the default sorting to POPULARITY & clean up
3 years ago
devi 543e3da194 Add sources 3 years ago
devi 908cc22619 Minor fix 3 years ago
Nikita Strygin c09d484a14 Add Line Webtoons sources (webtoons.com)
It uses the API of mobile app
3 years ago
Koitharu 07c3f22f75
Merge pull request #268 from VietAnh14/source/yurineko 3 years ago
devi d494bf19ee Small fix 3 years ago
vianh eda4f75219 [Yurineko] Fix api domain and chapters order 3 years ago
Koitharu 931f126119
Merge pull request #269 from deividgabrielpeira/patch-1
prismascan chance Domain
3 years ago
Koitharu bc171ce06e
[Grouple] Fix pumpkins 3 years ago
Deivid Gabriel Pereira de Oliveira 191d1fcec8
prismascan chance Domain 3 years ago
vianh eaea9e54d1 [Yurineko] New source 3 years ago
devi c9b3912fd4 Quick small fix 3 years ago
Koitharu 1611dc3429
Remove duplicated classes 3 years ago
Koitharu 88dc2145e0
Merge branch 'AwkwardPeak7-ksk' 3 years ago
Koitharu 8979987344
Merge pull request #266 from KotatsuApp/devi2
Update focus on fix and remove dead sources
3 years ago
AwkwardPeak7 1cec2cc187
remove need for refreshing chapter list 3 years ago
devi a1279b64a2 small fix and sources 3 years ago
AwkwardPeak7 2ce5f2563a
KskMoe: get original quality images 3 years ago
devi 75a60ff738 fix Union Mangás 3 years ago
devi 74eb7f83d6 other fix 3 years ago
devi 5b2ac5f6fb typo 3 years ago
devi fde4ba1d85 remove mangatone 3 years ago
devi 8278a8f5c3 multiples fix, remove dead source and add source 3 years ago
Koitharu a39d644094
[Remanga] Ability to change user-agent 3 years ago
Koitharu 0004be15ba
Merge branch 'source/devi' 3 years ago
Koitharu 8fef459346
Misc fixes 3 years ago
Koitharu 109603f124
[HentaiUkr] Fix ids 3 years ago
Koitharu 91879a8405
Merge pull request #261 from CakesTwix/master
[HentaiUkr] Tag ID is String now
3 years ago
devi f471a3513e add mangashiro.me and fix 3 years ago
CakesTwix 7362111318
[HentaiUkr] Tag ID is String now 3 years ago
devi 609c7188c0 add sources tr 3 years ago
devi a106b34379 add sources and fix 3 years ago
devi 853b95363e add missing content type 3 years ago
devi 45113e419e small fix 3 years ago
devi 519bb3053b add ksk 3 years ago
devi 366cc862ba remove 1stkissmanga.me and add likemanga 3 years ago
devi 611aaf0d38 remove mirrordesu 3 years ago
devi 712d829b54 add sources 3 years ago
devi e25656c090 reformat 3 years ago
devi 7debe6d7e2 add sources 3 years ago
devi 55e14e4cb3 add sources 3 years ago
Koitharu 7fbeb2e266
Add gradle jvmargs 3 years ago
Koitharu 7be6d779da
Reformat 3 years ago
Koitharu 96585fbdf7
[DesuMe] Add alternate domain 3 years ago
Koitharu 1a0d623c02
Merge pull request #252 from davvarrr/master
adds sources and fix
3 years ago
Koitharu 67cf2c2a6e
Revert MangaSources changes 3 years ago
Koitharu b6dd7fb5f6
[HentaiUkr] Fixes 3 years ago
Koitharu e0c5562600
Merge pull request #251 from CakesTwix/master
New 18+ UA Source and update urls in `CONTRIBUTING.md`
3 years ago
devi 2447f66834 change domain 3 years ago
devi 11ed27e397 correction 3 years ago
devi 6158547571 correction 3 years ago
devi a24dfde1ba correction 3 years ago
devi d805b49a4d adds sources and fix 3 years ago
devi f871bf77fa Merge remote-tracking branch 'origin/master' 3 years ago
devi be610b825d adds sources and fix 3 years ago
Koitharu f791cc6f9c
Added SoftSuspendLazy class 3 years ago
CakesTwix 30b2e81b15
[CONTRIBUTING] Update example urls
380861f29e
3 years ago
CakesTwix 067c7f2d0d
[HentaiUkr] New 18+ UA Source 3 years ago
Koitharu aae3fa3b05
Add abandoned state 3 years ago
Koitharu 41cd8ac7d7
Merge pull request #250 from CakesTwix/master
[HoneyManga] Change search url
3 years ago
CakesTwix 61f2a499da
[HoneyManga] Change search url 3 years ago
Koitharu 2f7e704e21
[MangaLib] Check page response size 3 years ago
Koitharu 4d22e7d1e8
Merge pull request #243 from davvarrr/master
Add sources and fix
3 years ago
devi 18125ea982 fix url Ozulscans 3 years ago
devi 1498541b45 fix url Nivera Fansub 3 years ago
devi 4faaa9fc13 fix url Manhwalist 3 years ago
devi c6c4b7190a fix url Random Scans 3 years ago
devi c0bf3ad56c add RackusReads 3 years ago
devi ad51271c40 format code 3 years ago
devi fce952d240 change request and change url Nettruyenmax & NetTruyen 3 years ago
devi 0dc5032a7a change url Shinigami 3 years ago
devi 0395f29503 small change 3 years ago
devi 03581add21 add Perf Scan and fix Legacy Scans 3 years ago
devi e0249accf8 fix domain Mgkomik 3 years ago
devi a3afbafdcc remove small code 3 years ago
devi 4d4c126327 remove dead source 3 years ago
devi c4bf7b2ae0 add sources and fix 3 years ago
devi 5d5f804c76 add sources and fix 3 years ago
Koitharu 3a76504380
[NepNep] Fixes 3 years ago
Koitharu 5035c90c73
Merge pull request #238 from davvarrr/master
add source and move Manga4Life to tempalte parser
3 years ago
devi 7f1cd30ce8 change domaine for QueenScans 3 years ago
devi 155a91a13d add Mangarbic 3 years ago
devi 8c291e274d nothing 3 years ago
devi 10e1eb8aac add source and move Manga4Life to tempalte parser
and fix url Duniakomik
3 years ago
Koitharu 06a2aa6f97
[Manga4Life] Refactoring and fixes 3 years ago
Koitharu f08a2e4551
Merge pull request #235 from davvarrr/master
add sources and fix
3 years ago
Koitharu e6839b1270
[Grouple] Change getPageUrl exception 3 years ago
Koitharu 270890f841
[NudeMoon] Migrate to mobile website parsing 3 years ago
devi 02581c9e2d Change and add Manga4Life 3 years ago
devi ea4e69df19 remove japscan 3 years ago
devi cb0ebb31f7 add more source 3 years ago
devi 497cd0bd15 add more source 3 years ago
devi b74c7841dd add sources and fix 3 years ago
Koitharu 407ef5b655
Merge pull request #232 from davvarrr/master
quick fix
3 years ago
devi f034b1874b add Mangakakalot 3 years ago
devi eac086b935 add jaiminisbox 3 years ago
devi ea310b184e add MangaCrab and MantrazScan 3 years ago
devi ea3386eda5 add MangaCrab and MantrazScan 3 years ago
devi 2efdb1940a fix Asura Scans 3 years ago
devi 896139a8ed Add exception for source don't support multi tags
and other fix
Add birdToon
3 years ago
devi da344233f4 quick fix 3 years ago
Koitharu 9cf3551356
[HentaiVN] Fix async/await 3 years ago
Koitharu 10aaae1ca8
Merge pull request #229 from davvarrr/master
Add template FoolSlideParser ands sources
3 years ago
Koitharu dcb5206272
Add minus operator to favicons 3 years ago
devi ccdd2b6c19 fix TuMangaOnlineParser 3 years ago
devi 16f3e1c180 add Ninja Scan 3 years ago
devi 7283cde5ff add missing contenttype 3 years ago
devi 4c20384d39 add sources and small fix 3 years ago
devi 5e9bc4fcc7 fix async and remove .imageUrl() to add .src() 3 years ago
devi e5448649c3 fix pagination HentaiVNParser 3 years ago
devi 951e89d429 add rating on MadaraParser 3 years ago
devi f34dd7926a add skanlacje-feniksy and fix hentaivn 3 years ago
devi b900c05d9b Merge remote-tracking branch 'origin/master' 3 years ago
devi 06948934ac and sources 3 years ago
devi e82915ef17
Merge branch 'KotatsuApp:master' into master 3 years ago
devi bb37da403f Add template FoolSlideParser ands sources 3 years ago
Koitharu 065263a9bd
Merge pull request #227 from davvarrr/master 3 years ago
devi f3359cc830 quick fix hentai20 move to tempalte mangareader 3 years ago
Koitharu 1b3b5d95c0
Merge pull request #226 from davvarrr/master
fix and add sources
3 years ago
Koitharu c290ba5436
Update src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/DynastyScans.kt 3 years ago
devi bb4ea05375 fix domaine peacescans 3 years ago
devi 47dde65f65 fix and add sources 3 years ago
Koitharu 08eb9552d6
Update gradle, kotlin and enum extensions 3 years ago
Koitharu 6058221fec
Merge pull request #223 from davvarrr/master
add templates and sources and fix
3 years ago
devi 5bf4ecb394 min change 3 years ago
devi 41fa80e8b8 Change .src() in accord of commit 3c43590 3 years ago
devi 0dd3a26080 no pagination 3 years ago
devi fb400d83e3 q fix 3 years ago
devi 37e5c43e5c
Merge branch 'KotatsuApp:master' into master 3 years ago
Koitharu 341aa0e13d
[JapScan] Migrate to desktop version 3 years ago
Koitharu 3c4359096d
Add Jsoup extensions 3 years ago
devi d56240086c
Update src/main/kotlin/org/koitharu/kotatsu/parsers/site/zmanga/ZMangaParser.kt
Co-authored-by: Koitharu <nvasya95@gmail.com>
3 years ago
devi bfea5b314e
Update src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/ScansMangasMe.kt
Co-authored-by: Koitharu <nvasya95@gmail.com>
3 years ago
Koitharu 03b4fc9f00
Add Result utils 3 years ago
devi 826563694e add MangaboxParser and sourcres 3 years ago
devi 880ff923bc add AnimeBootstrapParser and sources 3 years ago
devi cd0d4b103a add zmanga and soruces 3 years ago
devi fefec4985d add Wpcomics and sources 3 years ago
devi ba655f3d64 add domain and fix search url 3 years ago
devi be3812fa2f fix pagination komikcast 3 years ago
devi 87d4326411 add MadthemeParser and sources 3 years ago
devi ef6ede6b12 add correct page for UPDATED 3 years ago
devi eeccda24b0 Add 2 source more typo and add val on gettags on madara for site not let list /manga/ 3 years ago
devi 2bf29da2ea small fix and rename bad typo 3 years ago
devi 0b54029e26 Merge remote-tracking branch 'origin/master' 3 years ago
Koitharu d24ab347cb
Move CloudFlareProtectedException to test sources 3 years ago
Koitharu 74ffe9418b
Fix html descriptions 3 years ago
Koitharu c945434d71
Merge pull request #222 from davvarrr/master
add new template and source
3 years ago
devi b28545c852 add sources 3 years ago
devi c8d33d06a1 add fmreader 3 years ago
devi 2089c3cc7b fix import name 3 years ago
devi 5df1da2bfd fix nswf 3 years ago
devi 70fda48983 fix nswf 3 years ago
devi 36fe5cb625
Merge branch 'KotatsuApp:master' into master 3 years ago
devi 7e2a00520b fix and add updated on template Mmrcms 3 years ago
Koitharu caeeb699d1
Add content type field to sources 3 years ago
devi f6dc4f9a00 add source 3 years ago
devi 28135aed66 add 2 new template and new source 3 years ago
Koitharu 8e452f4271
[MangaLib] Fix Android compatibility 3 years ago
Koitharu f4656efebd
Update packages structure 3 years ago
Koitharu 42cc0430f8
Merge pull request #220 from davvarrr/master
fix some source and add source
3 years ago
Koitharu 81dd8b58c8
Merge pull request #219 from VietAnh14/source/hentaivn
Add new source HentaiVN
3 years ago
Koitharu 415da81b1a
[Komikcast] Fix chatpers names #221 3 years ago
Koitharu 74a20455f2
[MangaLib] Fix pages url 3 years ago
devi 0bdce39def
Update src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/LxManga.kt
Co-authored-by: Koitharu <nvasya95@gmail.com>
3 years ago
devi 24f7d8b5bd
Update src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt
Co-authored-by: Koitharu <nvasya95@gmail.com>
3 years ago
devi 18adc66f53
Update src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt
Co-authored-by: Koitharu <nvasya95@gmail.com>
3 years ago
devi 8c1c855531
Update src/main/kotlin/org/koitharu/kotatsu/parsers/site/fr/LugnicaScans.kt
Co-authored-by: Koitharu <nvasya95@gmail.com>
3 years ago
devi 02ae76baf2 add new set order 3 years ago
devi b5c44383e8 add lxmanga.net 3 years ago
devi c48ebb937b add Lugnica Scans 3 years ago
devi 49c30d1d26 Merge remote-tracking branch 'origin/master' 3 years ago
devi cb1a7ece8c fix some source and add 3 source 3 years ago
devi 4d60ae3cb6 fix some source and add 3 source 3 years ago
vianh fe64620e9f [HentaiVN] Load pages using api 3 years ago
vianh a42ca9e2da [HentaiVN] New source 3 years ago
Koitharu 506f6f7d30
Merge pull request #217 from davvarrr/master
add source and fix
3 years ago
devi 380861f29e Group sites by language 3 years ago
devi 9afc046d7b Change logic date on mangareader
Change logic domain on mangareader
3 years ago
devi aec87a9a67 add source and fix japscan 3 years ago
Koitharu 69e0a531df
[Madara] Refactoring 3 years ago
Koitharu c85101991f
Merge pull request #214 from davvarrr/master
Add source and create decode url image
3 years ago
devi 99e87fd97a Merge remote-tracking branch 'origin/master' 3 years ago
devi 665ec6b7ed fix japscan 3 years ago
devi 01fbd209c8
Merge branch 'KotatsuApp:master' into master 3 years ago
devi f0fa8d59a1 add source and create decode url image 3 years ago
Koitharu a1598fd712
[MangaInUa] Fix parsing chapters and pages 3 years ago
Koitharu db96a1ff2e
[Grouple] Fix tags parsing 3 years ago
Koitharu 833a42e798
Merge pull request #206 from bapeey/tmo
New parser: TuMangaOnline
3 years ago
Koitharu 922c665ef5
Merge pull request #210 from davvarrr/master
Add new source, fix and other
3 years ago
Koitharu 01842e9870
Merge pull request #212 from VietAnh14/master
[BlogTruyen] Update domain, user agent
3 years ago
vianh a235f6281c [BlogTruyen] Update domain, user agent 3 years ago
devi c9e7d4f483 add source requested 3 years ago
devi 94496ecc02 fix Asura Scans 3 years ago
devi 5104a8b542 add source request and remove source duplicate 3 years ago
devi e67beae235 add source request 3 years ago
devi 7c4bffede2 minor fix and add quick source ar 3 years ago
devi 8df620b1cd fix 3 years ago
devi cbbdd13c8e add new source ( lise below )
fix multiple source. ( lise below )
update madra parser :
-> new option on getListPage for take liste without Ajax
-> add format date
-> change defaut date parterne ( the news is most used by default on sites )
3 years ago
Koitharu 3874fe31d5
[TuMangaOnline] Fixes 3 years ago
Koitharu a8c086b340
Merge branch 'tmo' of github.com:bapeey/kotatsu-parsers into bapeey-tmo 3 years ago
Koitharu 06a043d290
Refactor Madara parser 3 years ago
Koitharu 5a15014369
Add additional methods to WebClient 3 years ago
Koitharu 7fe1f4b3ff
Merge pull request #208 from davvarrr/master
add source and other
3 years ago
devi 0055146264 fix and quick add source request 3 years ago
seew3l cc26897fb9 Opps 3 years ago
seew3l 46535f82af Apply requested changes 3 years ago
seew3l 23a7b945af Add headers to getPages request 3 years ago
devi 4fb6ff63b9 Merge remote-tracking branch 'origin/master' 3 years ago
devi c2e90a210c add new source and add other 3 years ago
seew3l 22d59e46c8 Get domain from ConfigKey 3 years ago
seew3l e462b7df97 Remove unused val 3 years ago
seew3l af8fb952df Remove comments 3 years ago
seew3l a3fa04bcb7 Remove comments 3 years ago
seew3l adf6fea4ac Add TMO 3 years ago
Koitharu 69af4fe586
Merge pull request #203 from davvarrr/master
80 new source
3 years ago
devi 31476a55f9 Fix small bug parse on desc 3 years ago
devi c833563907 add 10 new source 3 years ago
devi 74a4a3a8b4 And official Manga Origines 3 years ago
devi 141bca517d Add Koitharu optimization, and rename Manga origines 3 years ago
devi b9eb6363b7 Add 5 new source 3 years ago
devi c3bed26739 Add 3 new source "fr" 3 years ago
devi a15d54fe11 Add 10 new source 3 years ago
devi 1998ceac5a Add 3 new sources 3 years ago
devi 4192ffdbfe Add ScantradUnion 3 years ago
devi 5ec7227886 Add ScantradUnion 3 years ago
devi a9ebc372f6 Add 10 new source 3 years ago
devi 1e98104eca Add new source and add new scrapper for Chapter on theme madara 3 years ago
devi d05d5fa911 Add new source 3 years ago
Koitharu 07df5a81cf
[Chan] Add related manga implementation 3 years ago
Koitharu 9861a9aa6b
[Grouple] Add related manga implementation 3 years ago
Koitharu b65e1c498e
Simple related manga search implementation 3 years ago
Koitharu 240562037d
Merge pull request #202 from davvarrr/master
rework madara theme and fix many parser
3 years ago
Koitharu b3f79e77c3
[JapScan] Fix code style 3 years ago
Koitharu db3c73156e
[JapScan] Fix code style 3 years ago
devi 44b3b9b0c1 Merge remote-tracking branch 'origin/master' 3 years ago
devi ea8c1de44d rework madara theme and fix som parser 3 years ago
devi 8195d0190a
remove miss test in local 3 years ago
devi 8fd10eabd1 fix japscan and fix wrong date on 2 source 3 years ago
devi 7b28072ee5 delete table mode for automatic detect mode and chage defaut value for date and list url 3 years ago
devi b37db4d680 delete table mode for automatic detect mode and chage defaut value for date and list url 3 years ago
Koitharu 92bfc7e9fa
[Bentomanga] Fix UserAgent 3 years ago
Koitharu b3e97760db
[DesuMe] Update headers 3 years ago
Koitharu 3895312bfc
Refactor MangaReaderParser 3 years ago
Koitharu 06d2976bb5
Batch sources fix 3 years ago
Koitharu da4566f82f
Fix GitHub workflow 3 years ago
Koitharu b8f0ea4690
Merge pull request #195 from davvarrr/patch-16
Create AiyuMangaScanlation.kt
3 years ago
Koitharu bb5e49758a
Merge pull request #194 from davvarrr/patch-15
54 new source and rework
3 years ago
devi e617904b62
Update MangaScantrad.kt 3 years ago
devi b0e455e163
Update ScantradVf.kt 3 years ago
devi 6c31cea155
Update HhentaiFr.kt 3 years ago
devi 9c71d1b57c
Update Hentaizone.kt 3 years ago
devi e4f12149ed
fix 3 years ago
devi f6d264f37d
Update MangaScantrad.kt 3 years ago
devi aa734e5ad3
fix 3 years ago
devi 591291e9d6
fix status 3 years ago
devi 2311a56658
add new parse language for status 3 years ago
Koitharu 514e4473a1
Merge branch 'master' into patch-15 3 years ago
Koitharu 7ccacf4c27
Fix plugins versions 3 years ago
Koitharu 15f226fc73
Update gradle config 3 years ago
Koitharu 1388196288
Update gradle config 3 years ago
Koitharu fe024cb306
Merge branch 'master' into patch-15 3 years ago
Koitharu 04e7edaa3e
Fix GitHub worklflow Java version 3 years ago
devi 87abdf6346
Update MangaReaderParser.kt 3 years ago
devi c4fad321db
Update MangaReaderParser.kt 3 years ago
devi c17c9d9ccc
Update MangaReaderParser.kt
Add long for parse onging / finished
3 years ago
devi d27c208ab7
Create AiyuMangaScanlation.kt
add aiyumangascanlation.com "es"
3 years ago
devi 90966aa7e4
Update MangaReaderParser.kt
add 54 new source 
rework parseInfoList
Source sorting part language
3 years ago
Koitharu 553a6c709a
Reformat code 3 years ago
Koitharu 16698201f4
Merge pull request #191 from davvarrr/patch-12
Create ScantradVf.kt
3 years ago
Koitharu 33f27a8af7
Merge pull request #190 from davvarrr/patch-11
Create MangaScantrad.kt
3 years ago
Koitharu 48ec877ad5
Merge pull request #189 from davvarrr/patch-10
Create Hipercool.kt
3 years ago
Koitharu 54002ce27f
Merge pull request #188 from davvarrr/patch-9
Create Hentaiteca.kt
3 years ago
Koitharu de03d80e10
Merge pull request #186 from davvarrr/patch-7
Create Atlantisscan.kt
3 years ago
Koitharu 60954c38b6
Merge pull request #187 from davvarrr/patch-8
Create FrScan.kt
3 years ago
Koitharu ba41d5f4ce
Merge pull request #184 from davvarrr/patch-5
add french parse date and new source
3 years ago
Koitharu 9b76f42138
Merge pull request #185 from davvarrr/patch-6
Create AstralManga.kt
3 years ago
Koitharu 5a0306b4b5
Merge pull request #192 from davvarrr/patch-13
Create HhentaiFr.kt
3 years ago
Koitharu b70ddf0a7c
Merge pull request #193 from davvarrr/patch-14
Create Hentaizone.kt
3 years ago
devi f53174258c
add Stkissmanga "en"
add Stkissmanga "en"
3 years ago
devi 36706d998e
Create Hentaizone.kt 3 years ago
devi 65ec3ca5c6
Update Hipercool.kt
Add parse description
3 years ago
devi 0b0068e113
Update HhentaiFr.kt
large covers are not always present
3 years ago
devi 553777ef08
Create HhentaiFr.kt 3 years ago
devi 3cda7ffc18
Create ScantradVf.kt 3 years ago
devi 48d98548cd
Create MangaScantrad.kt 3 years ago
devi b878824d15
Create Hipercool.kt 3 years ago
devi 16a99d7b8c
Create Hentaiteca.kt 3 years ago
devi c34f250ac9
Create FrScan.kt 3 years ago
devi e74b276c98
Create Atlantisscan.kt 3 years ago
devi f0e61daa63
Create AstralManga.kt 3 years ago
devi 34b6f4aa32
add french parse date 3 years ago
Koitharu c2b79b55f8
Update dependencies 3 years ago
Koitharu 875b08b1fc
Merge pull request #182 from davvarrr/patch-3
Create ReaperScansFr.kt
3 years ago
Koitharu 0e3788d943
Merge pull request #181 from davvarrr/patch-2
Add 2 source fr on MangaReaderParser
3 years ago
Koitharu 396508da6f
Merge pull request #183 from davvarrr/patch-4
Fix uloaddate and force Enables list
3 years ago
devi 6fc6beb067
Force Enables list
Enables list mode to be forced to load images
3 years ago
devi 939cdf71ad
Fix uloaddate
allows you to use the function in madaraparser to fix dates, e.g. "2 hours ago", otherwise the app wouldn't display the chapters.
3 years ago
devi 8d7eabdd71
Create ReaperScansFr.kt
add reaperscans.fr
3 years ago
devi d28d6b17d8
Update MangaReaderParser.kt
add epsilonscan.fr ( +18 ) "fr"
add legacy-scans.com "fr"
3 years ago
Koitharu f096ca2ad3
Merge pull request #180 from drsgdfnhtyjktyjt/patch-1
Add Phenixscans.fr
3 years ago
Koitharu 32914eb36d
Small fixes 3 years ago
sddsg b58190ed17
Add Phenixscans.fr
I was finally able to test it at home and it works perfectly.

Could you add it, thank you.
3 years ago
Koitharu 86a82970fc
Empty-Commit 3 years ago
Koitharu 9976ea5dfc
[Ninemanga] Fix covers issue 3 years ago
Koitharu f732582d55
[Grouple] Fix chapters parsing 3 years ago
Koitharu 44e28b40d3
Update issue templates 3 years ago
Koitharu f969e2a8d1
[Grouple] Fix multiple genres search 3 years ago
Koitharu fa7ea5b16a
Fix domain config key 3 years ago
Koitharu 3d7a39cf67
Fix pagination 3 years ago
Koitharu 46f8b8e700
[Ninemanga] Remove redirect check 3 years ago
Koitharu ebcc6391d6
[Mangalib] Fix nsfw detection 3 years ago
Koitharu 12522ce8d8
[Anibel] Fix parser 3 years ago
Koitharu cae7073f87
[MangaDex][ComicK] Fix branching again 3 years ago
Koitharu 0366ad0e57
Fix Android compatibility 3 years ago
Koitharu 6b95a8c55e
[MangaDex] Fix branching ##172 3 years ago
Koitharu 6183f6edf3
[ComicK] Fix branching #163 #167 3 years ago
Koitharu 83cca4ef12
[KomikTap] fix page size 3 years ago
Koitharu 20ad9d0472
[ALlHentai] New parser 3 years ago
Koitharu 96b9ac36f3
[Bentomanga] Fixes 3 years ago
Koitharu 65170c0fe9
[Mangadex] Fix chapters numbers 3 years ago
Koitharu 3e349d3db3
[Ninemanga] Fix redirects 3 years ago
Koitharu b2675c3cac
Fix @MangaSources annotation 3 years ago
Koitharu 7414cf4a4e
Add @JvmField on models fields 3 years ago
Koitharu f43c482539
[Honeymanga] Refactoring 3 years ago
Koitharu f0fbbf4333
Merge pull request #169 from CakesTwix/master
[New source] HoneyManga
3 years ago
Koitharu 0d03c68c8b
[Manhwaland] Fix list url 3 years ago
CakesTwix b4a4c86049
[HoneyManga] Impl. filter by genres
and fix cover url
3 years ago
Koitharu 306d46ea93
[Grouple] Fix pages url extraction 3 years ago
Koitharu 5b94badfc2
[MangaRead] Fix list parsing 3 years ago
Koitharu 9adc0c5358
[Bentomanga] New parser 3 years ago
Koitharu 66adc65a96
[JapScan] New parser 3 years ago
Koitharu 8def9b5446
Improve network error handling 3 years ago
Koitharu e6511061a7
[Grouple] Fix server selecting 3 years ago
CakesTwix 647a694d91
[HoneyManga] throw IllegalArgumentException if search < 3 3 years ago
CakesTwix e5ececa42d
[HoneyManga] Update code, get(), CONTENT_ENCODING 3 years ago
Koitharu c321faeaa9
Merge pull request #168 from VietAnh14/source/doujindesu
[DoujinDesu] New parser
3 years ago
vianh 3112b7937c [DoujinDesu] Apply suggesions from code review 3 years ago
CakesTwix a3565abdc3
[HoneyManga] Improve code 3 years ago
CakesTwix 34d2e15302
[New source] HoneyManga 3 years ago
Koitharu 1b6d1456f3
Add support for POST with json body 3 years ago
vianh b49aee3188 [DoujinDesu] New parser 3 years ago
Koitharu 749f682ef9
[Madara] Fix empty tags 3 years ago
Koitharu 1b269efaa4
Update readme 3 years ago
Koitharu 2db1a8d958
Remove broken parser 3 years ago
Koitharu ece7d71e09
Update domains 3 years ago
Koitharu 129602ea70
[IsekaiScan] Fix chapters numbers #162 3 years ago
Koitharu 5e3fddb652
[Grouple] Fix relative page urls 3 years ago
Koitharu 2340100999
Fix false nsfw flags on some madara parsers #164 3 years ago
Koitharu dc0151720e
[IsekaiScanEu] Fix chapters order #162 3 years ago
Koitharu 36e88b2c0c
[Remanga] Fix chapters numbers 3 years ago
Koitharu 35695904d1
[Remanga] Add Accept header to image requests 3 years ago
Koitharu a3ffecc00f
[IsekaiScan] Fix chapters parsing 3 years ago
Koitharu cc418570d5
[Remanga] Fix mirror 3 years ago
Zakhar Timoshenko 481af22ae7 [Remanga] Add mirror 3 years ago
Zakhar Timoshenko 96b9dc8b5c [ComicK] Updated API 3 years ago
Koitharu 93f5f70d79
Remove referrer field from page model 3 years ago
Koitharu fc53b19915
Add toString to models 3 years ago
Koitharu 413f4a2f10
Batch parsers fixes 3 years ago
Koitharu e8d299782b
[Tempestfansub] Update domain #159 3 years ago
Koitharu 489a4ab586
[NeatManga] Fix chapters loading 3 years ago
Koitharu 32fa3a89e2
Merge pull request #152 from OshekharO/patch-1 3 years ago
Saksham Shekher f2614dd3d7
Fix 3 years ago
Koitharu 1093584202
[Grouple] Add UserAgent config 3 years ago
Koitharu f62d024342
Fix Manga District & Hentai4free parsers #157 3 years ago
Koitharu eea87d8607
Fix grouple pages parsing #156 3 years ago
Koitharu f4c47b5b84
Merge pull request #155 from VietAnh14/source/truyentranhlh
[TruyentranhLH]: New source
3 years ago
vianh fa78d5dd6f [TruyentranhLH]: New source 3 years ago
Koitharu 779c6664be
Merge pull request #154 from OshekharO/patch-2
BatoTo: New Subdomain
3 years ago
Saksham Shekher 38efd7935a
BatoTo: New Subdomain 3 years ago
Saksham Shekher 03f021377d
Update README.md
some minor grammatical corrections
3 years ago
Koitharu cf345d2d0c
[Grouple] update UserAgent 3 years ago
Koitharu fb3a3f49dd
Merge pull request #150 from OshekharO/patch-1
Add: KumaPoi
3 years ago
Koitharu f3b731114e
[DesuMe] Genres in list 3 years ago
Koitharu 454b24ec88
[Grouple] Selecting the fastest pages server 3 years ago
Saksham Shekher 2c6c64fbe9
Add: ToonHunter 3 years ago
Saksham Shekher a51d6d6485
Add: Hentai20 3 years ago
Saksham Shekher 0da57c041c
Fix 3 years ago
Saksham Shekher 503573f388
Add: KomikLokal 3 years ago
Saksham Shekher 29cd1f1fdf
Add: KumaPoi 3 years ago
Saksham Shekher e51b33c74a
Add: Toonily, Komiklab, KomikDewasa, KomiKav (#148)
* Add: Toonily

* Add: KOMIKLAB

* Update MangaReaderParser.kt

* Add: KomikDewasa

* Add: KomiKav
3 years ago
Koitharu 624a3dd991
Merge pull request #146 from KotatsuApp/feature/refactor2
Improve network requests processing
3 years ago
Koitharu 05d705ac03
Remove deprecated "getFavicon" method 3 years ago
Koitharu 1e1756aa17
Refactor web client 3 years ago
Koitharu 91109bf6dc
Merge pull request #144 from OshekharO/patch-1
Add: KomikManga & Fixes
3 years ago
Saksham Shekher 7f852e793a
Add: BakaMan
Thai Manga Provider
3 years ago
Saksham Shekher aae3747925
Add: KomikIndo
Closes #145
3 years ago
Saksham Shekher 5e23d52e8a
refactor: HachiManga 3 years ago
Saksham Shekher 3bc8013d2f
Merge branch 'KotatsuApp:master' into patch-1 3 years ago
Koitharu 00abaea324
[Remanga] Fix headers 3 years ago
Koitharu d14168e325
Make headers accessible 3 years ago
Koitharu c3b1556816
Add headers to CloudFlareException 3 years ago
Koitharu 80a2a10e71
[Remanga] Fix headers 3 years ago
Saksham Shekher 029f554d6e
Add: DragonTranslation 3 years ago
Saksham Shekher 841d8f05d9
Fix: MangaRosie, PianManga 3 years ago
Saksham Shekher 7253699529
Dead: ReadManwha 3 years ago
Saksham Shekher 5fe546c94d
Dead: MangaTX OT
Removed as site is not being updated
3 years ago
Saksham Shekher 31c26f16ac
Dead: IsekaiScan
Url changed to isekaiscan.to ( chapter not found in kotatsu )
3 years ago
Saksham Shekher 5eda6f96b4
Dead: ReadManhwa 3 years ago
Saksham Shekher 56a54d5eb4
Fix: NeatManga 3 years ago
Saksham Shekher 7a78a67740
Dead: MangaKik 3 years ago
Saksham Shekher 742533977f
Dead: X2Manga 3 years ago
Saksham Shekher 2dedaea0ab
Dead: KingManga 3 years ago
Saksham Shekher cf7f55181f
Dead: Manhwa 3 years ago
Saksham Shekher f68f0009a2
Dead: ManhwaChill 3 years ago
Saksham Shekher 4799f54fd1
Dead: All Top Manga 3 years ago
Saksham Shekher 06ff8df2e4
Dead: Heroxia 3 years ago
Saksham Shekher 9b78944f9b
Fix: ManhwaList 3 years ago
Saksham Shekher fc2781c83a
Add: KomikManga 3 years ago
Saksham Shekher 176437531d
Add: CosmicScans (#142)
* Add: CosmicScans

* Fix info

* Rem: nsfw

* Mangasu url changed 

Cert issue needs to be fixed
3 years ago
Zakhar Timoshenko c28e2a72d5 [Remanga] Bypass blocking 3 years ago
Zakhar Timoshenko 7f630184c0 [GroupLe] Fix pages loading 3 years ago
Koitharu e5a6b82853
[Grouple] Fix chapter link extraction 3 years ago
Koitharu e1d22b1110
[ExHentai] Option to hide suspicious content 3 years ago
Koitharu 918318252e
Fix coroutine cancellation handling in runCatching 3 years ago
Koitharu e153463c35
[Grouple] Fix authorization recognizing 3 years ago
Koitharu cf00732023
Misc parsers fixes 3 years ago
Zakhar Timoshenko ef838068ff [MangahaTachi] Change url (closes #128) 3 years ago
Zakhar Timoshenko c4acb9725f [Lib] Fix auth part 2 3 years ago
Zakhar Timoshenko 04e219ff69 Remove accidentally duplicated parser 3 years ago
Zakhar Timoshenko 106a85aa80 [Lib] Authorization fixes 3 years ago
Zakhar Timoshenko ad6e074cce [Asura Scans] New parser 3 years ago
Zakhar Timoshenko e89f06a35b [KomikTap] New parser (closes #40) (?) 3 years ago
Zakhar Timoshenko 238cc7529a [Asura Scans (tr)] New parser (closes #55) 3 years ago
Zakhar Timoshenko fb22eb5fac [Tempest Fan Sub] New parser (closes #56) 3 years ago
Koitharu 9ee1c21a67
[ExHentai] Fix pagination 3 years ago
Koitharu c7c142f4a0
[Remanga] Show bought chapters 3 years ago
Zakhar Timoshenko efd0394398 [MangaTale] New parser (closes #125) 3 years ago
Koitharu 0091a8280a
Merge pull request #122 from VietAnh14/source/heroxia
[MangaReader] Fix chapters order
3 years ago
vianh f9b0e82473 [MangaReader] Add new source 3 years ago
vianh 63676c21a8 [MangaReader] fix chapters order 3 years ago
Koitharu add70b4790
[ExHentai] Fix pagination 3 years ago
Koitharu d18f665556
Merge pull request #121 from VietAnh14/source/heroxia
[MangaReadTheme] Add some new sources using MangaReader theme
3 years ago
vianh 42253e43b3 Add more mangareader theme sources, fix cache tags 3 years ago
vianh ad500a9db6 [MangaReadTheme] Add some new sources using MangaReader theme 3 years ago
ViAnh 2ab03cb668
[Manhwa18] Fix parse upload date (#115) 3 years ago
ViAnh b78ddc6166
[NetTruyen] Update domain (#116) 3 years ago
Koitharu 1e49d4095b
[Grouple] Fix pages parsing 3 years ago
Zakhar Timoshenko bf8a1f3db2 [MangaInUa] Fix #104 4 years ago
Koitharu 321cc7529e
Merge pull request #96 from VietAnh14/source/manhwa18
[Manhwa18] New source
4 years ago
ViAnh 7907b3126c
Apply suggestions from code review
Co-authored-by: Koitharu <nvasya95@gmail.com>
4 years ago
vianh ccf9af0e51 [Manhwa18] New source 4 years ago
Koitharu b38b43edc0
Merge pull request #94 from VietAnh14/master
[BlogTruyen] Fix image src attr change
4 years ago
vianh 5d13c55dd5 [BlogTruyen] Fix image src attr change 4 years ago
Koitharu a1441e7ed7
[MangaDex] Fix large chapters loading error 4 years ago
Koitharu a62662d116
Fix some madara parsers 4 years ago
Koitharu 5cb953eb86
Small fixes 4 years ago
Koitharu 505ffcf405
Mangalink parser #66 4 years ago
Koitharu a45f9bfc58
ReaperScansID parser #83 4 years ago
Koitharu b1990c7918
Fixes 4 years ago
Koitharu e8b801684f
[MangaDex] Fix error on empty description 4 years ago
Koitharu de0c12c078
Add contribution guidelines 4 years ago
Koitharu b3a9c5fcda
Propagate deprecation from MangaParser to MangaSource 4 years ago
Koitharu c73d34b04f
[Madara] Fix date parsing 4 years ago
Koitharu 583b24854f
[Remanga] Omit paid chapters 4 years ago
Koitharu 1c79b29845
Merge branch 'master' of github.com:KotatsuApp/kotatsu-parsers 4 years ago
Zakhar Timoshenko c348d68983 Add deprecation annotation to some sources 4 years ago
Zakhar Timoshenko 6d7506cc1b [CloneManga] Add source 4 years ago

@ -3,12 +3,18 @@ root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = tab
indent_style = space
indent_size = 4
max_line_length = 120
tab_width = 4
insert_final_newline = false
disabled_rules=no-wildcard-imports,no-unused-imports
insert_final_newline = true
trim_trailing_whitespace = true
# noinspection EditorConfigKeyCorrectness
disabled_rules = no-wildcard-imports, no-unused-imports
[{*.kt,*.kts}]
[*.{kt,kts}]
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
[*.md]
indent_size = 2
trim_trailing_whitespace = false

@ -1,65 +1,66 @@
name: 🐞 Issue report
description: Report a source issue in Kotatsu
labels: [bug]
description: Report a source issue with a source
labels: [ bug ]
body:
- type: input
id: source
attributes:
label: Source information
description: |
You can find the source name in navigation drawer.
placeholder: |
Example: "MangaDex"
validations:
required: true
- type: input
id: source
attributes:
label: Source information
description: |
You can find the source name in navigation drawer.
placeholder: |
Example: "MangaDex"
validations:
required: true
- type: textarea
id: reproduce-steps
attributes:
label: Steps to reproduce
description: Provide an example of the issue.
placeholder: |
Example:
1. First step
2. Second step
3. Issue here
validations:
required: false
- type: textarea
id: reproduce-steps
attributes:
label: Steps to reproduce
description: Provide an example of the issue.
placeholder: |
Example:
1. First step
2. Second step
3. Issue here
Please use English language
validations:
required: false
- type: input
id: kotatsu-version
attributes:
label: Kotatsu version
description: |
You can find your Kotatsu version in **Settings → About**.
placeholder: |
Example: "3.3"
validations:
required: true
- type: input
id: kotatsu-version
attributes:
label: Kotatsu version
description: |
You can find your Kotatsu version in **Settings → About**.
placeholder: |
Example: "3.3"
validations:
required: true
- type: input
id: android-version
attributes:
label: Android version
description: |
You can find this somewhere in your Android settings.
placeholder: |
Example: "Android 12"
validations:
required: false
- type: input
id: android-version
attributes:
label: Android version
description: |
You can find this somewhere in your Android settings.
placeholder: |
Example: "Android 12"
validations:
required: false
- type: textarea
id: other-details
attributes:
label: Other details
placeholder: |
Additional details and attachments.
- type: textarea
id: other-details
attributes:
label: Other details
placeholder: |
Additional details and attachments.
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
options:
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue.
required: true
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
options:
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue.
required: true

@ -1,30 +1,31 @@
name: ⭐ Feature request
description: Suggest a feature to improve a source
labels: [feature request]
labels: [ feature request ]
body:
- type: textarea
id: feature-description
attributes:
label: Describe your suggested feature
description: How can an existing source be improved?
placeholder: |
Example:
"It should work like this..."
validations:
required: true
- type: textarea
id: feature-description
attributes:
label: Describe your suggested feature
description: How can an existing source be improved?
placeholder: |
Example:
"It should work like this..."
Please use English language
validations:
required: true
- type: textarea
id: other-details
attributes:
label: Other details
placeholder: |
Additional details and attachments.
- type: textarea
id: other-details
attributes:
label: Other details
placeholder: |
Additional details and attachments.
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
options:
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue.
required: true
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
options:
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue.
required: true

@ -1,33 +1,31 @@
name: 🗑 Source removal request
description: Scanlators can request their site to be removed
labels: [source removal]
labels: [ source removal ]
body:
- type: input
id: link
attributes:
label: Source link
placeholder: |
Example: "https://example.org"
validations:
required: true
- type: input
id: link
attributes:
label: Source link
placeholder: |
Example: "https://example.org"
validations:
required: true
- type: textarea
id: other-details
attributes:
label: Other details
placeholder: |
Additional details and attachments.
- type: textarea
id: other-details
attributes:
label: Other details (reason for removal, etc)
placeholder: |
Additional details and attachments.
- type: checkboxes
id: requirements
attributes:
label: Requirements
description: Your request will be denied if you don't meet these requirements.
options:
- label: Proof of ownership/intent to remove sent to a Kotatsu Discord server mod via DM
required: true
- label: Site only hosts content scanlated by the group and not stolen from other scanlators or official releases (i.e., not an aggregator site)
required: true
- label: Site is not infested with user-hostile features (e.g., invasive or malicious ads)
required: true
- type: checkboxes
id: requirements
attributes:
label: Requirements
description: Your request will be denied if you don't meet these requirements.
options:
- label: Proof of ownership of the website is sent to a Kotatsu [Discord server](https://discord.gg/NNJ5RgVBC5) or [Telegram community](https://t.me/kotatsuapp)
required: true
- label: Site only hosts content scanlated by the group and not stolen from other scanlators or official releases (i.e., not an aggregator site)
required: true

@ -1,50 +1,53 @@
name: 🌐 Source request
description: Suggest a new source for Kotatsu
labels: [source request]
labels: [ source request ]
body:
- type: input
id: name
attributes:
label: Source name
placeholder: |
Example: "Example Scans"
validations:
required: true
- type: markdown
attributes:
value: Please specify source **name** and **language** in the issue title
- type: input
id: name
attributes:
label: Source name
placeholder: |
Example: "Example Scans"
validations:
required: true
- type: input
id: link
attributes:
label: Source link
placeholder: |
Example: "https://example.org"
validations:
required: true
- type: input
id: link
attributes:
label: Source link
placeholder: |
Example: "https://example.org"
validations:
required: true
- type: input
id: language
attributes:
label: Language
placeholder: |
Example: "English"
validations:
required: true
- type: input
id: language
attributes:
label: Language
placeholder: |
Example: "English"
validations:
required: true
- type: textarea
id: other-details
attributes:
label: Other details
placeholder: |
Additional details and attachments.
- type: textarea
id: other-details
attributes:
label: Other details
placeholder: |
Additional details and attachments.
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
options:
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue.
required: true
- label: I have checked that the source does not already exist on the app.
required: true
- label: I have checked that the source does not already exist by searching the [GitHub repository](https://github.com/KotatsuApp/kotatsu-parsers) and verified it does not appear in the code base.
required: true
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
options:
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue.
required: true
- label: I have checked that the source does not already exist on the app.
required: true
- label: I have checked that the source does not already exist by searching the [GitHub repository](https://github.com/KotatsuApp/kotatsu-parsers) and verified it does not appear in the code base.
required: true

@ -0,0 +1 @@
total: 1256

@ -0,0 +1,25 @@
name: Check & Test latest parsers
on:
push:
branches:
- master
jobs:
check-and-build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository 🌏
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up enviroment 🔧
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
with:
java-version: '21'
distribution: 'temurin'
- name: Set up Gradle 📦
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
- name: Compile parsers 🚀
run: ./gradlew compileKotlin

@ -1,27 +1,29 @@
name: Parsers test
name: Parsers test for PRs
on:
workflow_dispatch:
pull_request:
paths:
- 'src/main/kotlin/org/koitharu/kotatsu/parsers/site/*'
workflow_dispatch:
pull_request:
paths:
- 'src/main/kotlin/org/koitharu/kotatsu/parsers/**'
permissions:
contents: read
contents: read
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: 'gradle'
- run: ./gradlew :test --tests "org.koitharu.kotatsu.parsers.MangaParserTest" || true
- run: ./gradlew generateTestsReport
- uses: actions/upload-artifact@v3
with:
name: Report
path: build/test-results-html/TEST-org.koitharu.kotatsu.parsers.MangaParserTest.htm
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository 🌏
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up enviroment 🔧
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
with:
java-version: '21'
distribution: 'temurin'
- name: Set up Gradle 📦
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
- name: Compile parsers 🚀
run: ./gradlew compileKotlin

21
.gitignore vendored

@ -4,6 +4,7 @@
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
.idea/**/copilot
# Generated files
.idea/**/contentModel.xml
@ -16,6 +17,7 @@
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
.idea/**/Project_Default.xml
# Gradle
.idea/**/gradle.xml
@ -25,10 +27,13 @@
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
.idea/deviceManager.xml
.idea/.name
.idea/artifacts
.idea/compiler.xml
.idea/jarRepositories.xml
.idea/modules.xml
.idea/ktlint-plugin.xml
.idea/*.iml
.idea/modules
*.iml
@ -69,11 +74,25 @@ fabric.properties
.gradle/
build/
bin/
.idea/**/misc.xml
.idea/**/vcs.xml
.idea/**/ktlint.xml
.idea/codeStyles/
.idea/kotlinc.xml
src/test/resources/cookies.txt
local.properties
local.properties
.kotlin/
!/.idea/kotlin-statistics.xml
.idea/**/discord.xml
.idea/**/migrations.xml
.idea/**/runConfigurations.xml
.idea/**/AndroidProjectSystem.xml
.idea/caches/deviceStreaming.xml
/.idea/copilot.data.migration.agent.xml
/.idea/copilot.data.migration.ask.xml
/.idea/copilot.data.migration.ask2agent.xml
/.idea/copilot.data.migration.edit.xml

5
.idea/.gitignore vendored

@ -1,3 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# GitHub Copilot persisted chat sessions
/copilot/chatSessions
.name
deviceManager.xml

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.6.21" />
</component>
</project>

@ -0,0 +1,96 @@
# Contributing
The following is a guide for creating Kotatsu parsers. Thanks for taking the time to contribute!
## Prerequisites
Before you start, please note that the ability to use the following technologies is **required**.
- Basic [Android development](https://developer.android.com/)
- [Kotlin](https://kotlinlang.org/)
- Web scraping ([JSoup](https://jsoup.org/)) or JSON API
### Tools
- [Android Studio](https://developer.android.com/studio)
- [IntelliJ IDEA](https://www.jetbrains.com/idea/) (Community edition is enough)
- Android device (or emulator)
Kotatsu parsers are not a part of the Android application, but you can easily develop and test it directly inside an
Android application project and relocate it to the library project when done.
### Before you start
First, take a look at the `kotatsu-parsers` project structure. Each parser is a single class that
extends the `MangaParser` class and has a `MangaSourceParser` annotation.
Also, pay attention to extensions in the `util` package. For example, extensions from the `Jsoup` file
should be used instead of existing JSoup functions because they have better nullability support
and improved error messages.
## Writing your parser
So, you want to create a parser, that will provide access to manga from a website.
First, you should explore a website to learn about API availability.
If it does not contain any documentation about
API, [explore network requests](https://firefox-source-docs.mozilla.org/devtools-user/):
some websites use AJAX.
- [Example](https://github.com/KotatsuApp/kotatsu-parsers/blob/master/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt)
of Json API usage.
- [Example](https://github.com/KotatsuApp/kotatsu-parsers/blob/master/src/main/kotlin/org/koitharu/kotatsu/parsers/site/be/AnibelParser.kt)
of GraphQL API usage
- [Example](https://github.com/KotatsuApp/kotatsu-parsers/blob/master/src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MangaTownParser.kt)
of pure HTML parsing.
If the website is based on some engine it is rationally to use a common base class for this one (for example, Madara
Wordpress theme and the `MadaraParser` class)
### Parser class skeleton
The parser class must have exactly one primary constructor parameter of type `MangaLoaderContext` and have an
`MangaSourceParser` annotation that provides the internal name, title, and language of a manga source.
All members of the `MangaParser` class are documented. Pay attention to some peculiarities:
- Never hardcode domain. Specify the default domain in the `configKeyDomain` field and obtain an actual one using
`domain`.
- All IDs must be unique and domain-independent. Use `generateUid` functions with a relative URL or some internal id
that is unique across the manga source.
- The `availableSortOrders` set should not be empty. If your source does not support sorting, specify one most relevant
value.
- If you cannot obtain direct links to page images inside the `getPages` method, it is ok to use an intermediate URL
as `Page.url` and fetch a direct link in the `getPageUrl` function.
- You can use _asserts_ to check some optional fields. For example, the `Manga.author` field is not required, but if
your source provides this information, add `assert(it != null)`. This will not have any effect on production but help
to find issues during unit testing.
- Your parser may also implement the `Interceptor` interface for additional manipulation of all network requests and
responses, including image loading.
- If your source website (or its API) uses pages for pagination instead of offset you should extend `PagedMangaParser`
instead of `MangaParser`.
- If your source website (or its API) does not provide pagination (has only one page of content) you should extend
`SinglePageMangaParser` instead of `MangaParser` or `PagedMangaParser`.
![parser_classes.png](docs/parser_classes.png)
## Development process
During the development, it is recommended (but not necessary) to write it directly
in the Kotatsu Android application project. You can use the `core.parser.DummyParser` class as a sandbox. The `Dummy`
manga source is available in the debug Kotatsu build.
Once the parser is ready you can relocate your code into the `kotatsu-parsers` library project in a `site` package and
create a Pull Request.
### Testing
It is recommended that unit tests be run before submitting a PR.
- Temporary modify the `MangaSources` annotation class: specify your parser(s) name(s) and change mode
to `EnumSource.Mode.INCLUDE`
- Run the `MangaParserTest` (`gradlew :test --tests "org.koitharu.kotatsu.parsers.MangaParserTest"`)
- Optionally, you can run the `generateTestsReport` gradle task to get a pretty readable html report from test results.
## Help
If you need help or have some questions, ask a community in our [Telegram chat](https://t.me/kotatsuapp)
or [Discord server](https://discord.gg/NNJ5RgVBC5).

@ -1,12 +1,13 @@
# Kotatsu parsers
Library that provides manga sources.
This library provides a collection of manga parsers for convenient access manga available on the web. It can be used in
JVM and Android applications.
[![](https://jitpack.io/v/KotatsuApp/kotatsu-parsers.svg)](https://jitpack.io/#KotatsuApp/kotatsu-parsers) ![Kotlin](https://img.shields.io/github/languages/top/KotatsuApp/kotatsu-parsers) ![License](https://img.shields.io/github/license/KotatsuApp/Kotatsu) [![Discord](https://img.shields.io/discord/898363402467045416?color=5865f2&label=discord)](https://discord.gg/NNJ5RgVBC5)
![Sources count](https://img.shields.io/badge/dynamic/yaml?url=https%3A%2F%2Fraw.githubusercontent.com%2FKotatsuApp%2Fkotatsu-parsers%2Frefs%2Fheads%2Fmaster%2F.github%2Fsummary.yaml&query=total&label=manga%20sources&color=%23E9321C) [![](https://jitpack.io/v/KotatsuApp/kotatsu-parsers.svg)](https://jitpack.io/#KotatsuApp/kotatsu-parsers) ![License](https://img.shields.io/github/license/KotatsuApp/Kotatsu) [![Telegram](https://img.shields.io/badge/chat-telegram-60ACFF)](https://t.me/kotatsuapp) [![Discord](https://img.shields.io/discord/898363402467045416?color=5865f2&label=discord)](https://discord.gg/NNJ5RgVBC5)
### Usage
## Usage
1. Add it in your root build.gradle at the end of repositories:
1. Add it to your root build.gradle at the end of repositories:
```groovy
allprojects {
@ -35,17 +36,39 @@ Library that provides manga sources.
}
```
See for versions at [JitPack](https://jitpack.io/#KotatsuApp/kotatsu-parsers)
Versions are available on [JitPack](https://jitpack.io/#KotatsuApp/kotatsu-parsers)
When used in Android
projects, [core library desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring) with
the [NIO specification](https://developer.android.com/studio/write/java11-nio-support-table) should be enabled to
support Java 8+ features.
3. Usage in code
```kotlin
val parser = MangaSource.MANGADEX.newParser(mangaLoaderContext)
val parser = mangaLoaderContext.newParserInstance(MangaParserSource.MANGADEX)
```
`mangaLoaderContext` is an implementation of the `MangaLoaderContext` class.
See [Android](https://github.com/KotatsuApp/Kotatsu/blob/devel/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt)
and [Non-Android](https://github.com/KotatsuApp/kotatsu-dl/blob/master/src/main/kotlin/org/koitharu/kotatsu_dl/env/MangaLoaderContextImpl.kt)
implementation examples.
See examples
of [Android](https://github.com/KotatsuApp/Kotatsu/blob/devel/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLoaderContextImpl.kt)
and [Non-Android](https://github.com/KotatsuApp/kotatsu-dl/blob/master/src/main/kotlin/org/koitharu/kotatsu/dl/parsers/MangaLoaderContextImpl.kt)
implementation.
## Projects that use the library
- [Kotatsu](https://github.com/KotatsuApp/Kotatsu)
- [Doki](https://github.com/DokiTeam/Doki)
- [kotatsu-dl](https://github.com/KotatsuApp/kotatsu-dl)
- [Shirizu (WIP)](https://github.com/ztimms73/shirizu)
- [OtakuWorld](https://github.com/jakepurple13/OtakuWorld)
## Contribution
See [CONTRIBUTING.md](./CONTRIBUTING.md) for the guidelines.
## DMCA disclaimer
Note that the `MangaSource.LOCAL` and `MangaSource.DUMMY` parsers cannot be instantiated.
The developers of this application have no affiliation with the content available in the app. It is collected from
sources freely available through any web browser.

@ -1,73 +0,0 @@
import tasks.ReportGenerateTask
plugins {
id 'java-library'
id 'org.jetbrains.kotlin.jvm'
id 'com.google.devtools.ksp'
id 'maven-publish'
}
group = 'org.koitharu'
version = '1.0'
test {
useJUnitPlatform()
}
compileKotlin {
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs += [
'-opt-in=kotlin.RequiresOptIn',
'-opt-in=kotlin.contracts.ExperimentalContracts',
'-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi',
'-opt-in=org.koitharu.kotatsu.parsers.InternalParsersApi',
]
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs += [
'-opt-in=kotlin.RequiresOptIn',
'-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi',
'-opt-in=org.koitharu.kotatsu.parsers.InternalParsersApi',
]
}
}
kotlin {
sourceSets {
main.kotlin.srcDirs += 'build/generated/ksp/main/kotlin'
}
}
afterEvaluate {
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
}
}
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
implementation 'com.squareup.okio:okio:3.2.0'
api 'org.jsoup:jsoup:1.15.2'
implementation 'org.json:json:20220320'
implementation 'androidx.collection:collection-ktx:1.2.0'
ksp project(':kotatsu-parsers-ksp')
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4'
testImplementation 'io.webfolder:quickjs:1.1.0'
}
task generateTestsReport(type: ReportGenerateTask)

@ -0,0 +1,64 @@
import tasks.ReportGenerateTask
plugins {
`java-library`
`maven-publish`
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.ksp)
}
group = "org.koitharu"
version = "1.0"
tasks.test {
useJUnitPlatform()
}
ksp {
arg("summaryOutputDir", "${projectDir}/.github")
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
compilerOptions {
freeCompilerArgs.addAll(
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=kotlin.contracts.ExperimentalContracts",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=org.koitharu.kotatsu.parsers.InternalParsersApi",
)
}
}
kotlin {
jvmToolchain(8)
explicitApiWarning()
sourceSets["main"].kotlin.srcDirs("build/generated/ksp/main/kotlin")
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
}
}
}
dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.okhttp)
implementation(libs.okio)
implementation(libs.json)
implementation(libs.androidx.collection)
api(libs.jsoup)
ksp(project(":kotatsu-parsers-ksp"))
testImplementation(libs.junit.api)
testImplementation(libs.junit.engine)
testImplementation(libs.junit.params)
testRuntimeOnly(libs.junit.launcher)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.quickjs)
}
tasks.register<ReportGenerateTask>("generateTestsReport")

@ -1,13 +0,0 @@
plugins {
id('org.jetbrains.kotlin.jvm') version '1.6.21'
}
repositories {
mavenCentral()
}
dependencies {
implementation gradleApi()
implementation 'org.simpleframework:simple-xml:2.7.1'
implementation 'com.soywiz.korlibs.korte:korte-jvm:3.0.0-Beta5'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3'
}

@ -0,0 +1,17 @@
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
kotlin {
jvmToolchain(8)
}
dependencies {
implementation(libs.korte)
implementation(libs.simplexml)
implementation(libs.kotlinx.coroutines.core)
}

Binary file not shown.

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

47
buildSrc/gradlew vendored

@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -80,13 +82,11 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@ -133,22 +133,29 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@ -193,18 +200,28 @@ if "$cygwin" || "$msys" ; then
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

@ -13,8 +13,10 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -25,7 +27,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@ -56,32 +59,34 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

@ -0,0 +1,7 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}

@ -1,6 +1,6 @@
package tasks
import com.soywiz.korte.Template
import korlibs.template.Template
import kotlinx.coroutines.runBlocking
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
@ -42,10 +42,13 @@ open class ReportGenerateTask : DefaultTask() {
val results = LinkedHashMap<String, LinkedHashMap<String, TestCase>>()
val tests = LinkedHashSet<String>()
for (case in testSuite.testCases) {
if (!case.isValid()) {
continue
}
tests.add(case.testName)
val map = results.getOrPut(case.source) { LinkedHashMap() }
val oldValue = map.put(case.testName, case)
check(oldValue == null)
check(oldValue == null) { "Check failed: $oldValue" }
}
val failPercent = (testSuite.failures.toDouble() / testSuite.tests * 100.0).roundToInt()

@ -19,14 +19,16 @@ class TestCase {
var failure: Failure? = null
val index by lazy {
name.split('|')[0].toInt()
name.split('|').getOrNull(0)?.toIntOrNull() ?: 0
}
val testName by lazy {
name.split('|')[1]
name.split('|').getOrNull(1).orEmpty()
}
val source by lazy {
name.split('|')[2]
name.split('|').getOrNull(2).orEmpty()
}
}
fun isValid() = name.count { it == '|' } == 2
}

@ -2,13 +2,15 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta content="width=device-width, initial-scale=1" name="viewport">
<title>{{ testSuite.name }}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2"
crossorigin="anonymous"></script>
<!-- CSS only -->
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css"
integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" rel="stylesheet">
<!-- JavaScript Bundle with Popper -->
<script crossorigin="anonymous"
integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3"
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
</head>
<body class="py-4">
@ -25,67 +27,80 @@
{{ testSuite.errors }} ({{ error_percent }}%)
</div>
</div>
<div class="table-responsive mt-4">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Source</th>
{% for test in tests %}
<th scope="col" style="min-width: 5em;">{{ test }}</th>
{% endfor %}
</tr>
</thead>
{% for name, cases in results %}
<tr>
<th scope="row">{{ name }}</th>
{% for test in tests %}
{% set case = cases[test] %}
{% if case.failure == null %}
<td class="table-success text-center">
<i data-feather="check"></i>
</td>
{% else %}
{% if case.failure.type == 'java.lang.AssertionError' %}
<td class="table-warning text-center" style="cursor: pointer;"
data-bs-toggle="modal" data-bs-target="#failure_{{ case.hashCode }}">
<i data-feather="alert-triangle"></i>
</td>
{% else %}
<td class="table-danger text-center" style="cursor: pointer;"
data-bs-toggle="modal" data-bs-target="#failure_{{ case.hashCode }}">
<i data-feather="x"></i>
</td>
{% endif %}
<!--suppress HtmlUnknownTag -->
<div class="modal fade" id="failure_{{ case.hashCode }}" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ case.testName }} failed</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body font-monospace lh-sm bg-light" style="font-size: 0.7em;">
{{ case.failure.textHtml()|raw }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
<table class="table table-hover">
<thead class="sticky-top bg-body">
<tr>
<th scope="col">Source</th>
{% for test in tests %}
<th class="text-center" scope="col" style="min-width: 5em;">{{ test }}</th>
{% endfor %}
</tr>
</thead>
{% for name, cases in results %}
<tr>
<th scope="row">{{ name }}</th>
{% for test in tests %}
{% set case = cases[test] %}
{% if case.failure == null %}
<td class="table-success text-center">
<i data-feather="check"></i>
</td>
{% else %}
{% if case.failure.type == 'java.lang.AssertionError' %}
<td class="table-warning text-center" data-bs-target="#failure_{{ case.hashCode }}"
data-bs-toggle="modal" style="cursor: pointer;">
<i data-feather="alert-triangle"></i>
</td>
{% elseif case.failure.type == 'java.net.SocketTimeoutException' or case.failure.type ==
'java.net.UnknownHostException' %}
<td class="table-secondary text-center" data-bs-target="#failure_{{ case.hashCode }}"
data-bs-toggle="modal" style="cursor: pointer;">
<i data-feather="power"></i>
</td>
{% elseif case.failure.type == 'org.koitharu.kotatsu.parsers.CloudFlareProtectedException' %}
<td class="table-secondary text-center" data-bs-target="#failure_{{ case.hashCode }}"
data-bs-toggle="modal" style="cursor: pointer;">
<i data-feather="shield"></i>
</td>
{% elseif case.failure.type == 'org.koitharu.kotatsu.parsers.exception.AuthRequiredException' %}
<td class="table-secondary text-center" data-bs-target="#failure_{{ case.hashCode }}"
data-bs-toggle="modal" style="cursor: pointer;">
<i data-feather="user-x"></i>
</td>
{% else %}
<td class="table-danger text-center" data-bs-target="#failure_{{ case.hashCode }}"
data-bs-toggle="modal" style="cursor: pointer;">
<i data-feather="x"></i>
</td>
{% endif %}
<!--suppress HtmlUnknownTag -->
<div class="modal fade" id="failure_{{ case.hashCode }}" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ case.testName }} failed</h5>
<button aria-label="Close" class="btn-close" data-bs-dismiss="modal"
type="button"></button>
</div>
<div class="modal-body font-monospace lh-sm bg-light" style="font-size: 0.7em;">
{{ case.failure.textHtml()|raw }}
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button">Close</button>
</div>
</div>
</div>
{% endif %}
{% endfor %}
</tr>
</div>
{% endif %}
{% endfor %}
</table>
</div>
</tr>
{% endfor %}
</table>
</div>
<script>
feather.replace()
</script>
</body>
</html>
</html>

@ -0,0 +1,804 @@
<?xml version="1.0" encoding="utf-8"?>
<gaphor xmlns="https://gaphor.org/model" xmlns:Core="https://gaphor.org/modelinglanguage/Core" xmlns:UML="https://gaphor.org/modelinglanguage/UML" xmlns:general="https://gaphor.org/modelinglanguage/general" version="4" gaphor-version="3.1.0">
<model>
<Core:StyleSheet id="58d6989a-66f8-11ec-b4c8-0456e5e540ed"/>
<UML:Package id="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed">
<name>
<val>Новая модель</val>
</name>
<ownedDiagram>
<reflist>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</reflist>
</ownedDiagram>
<ownedType>
<reflist>
<ref refid="0b54a350-f59d-11ef-bfb1-4cbb5880a0b8"/>
<ref refid="a300f58a-f5bd-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="ad4c68d0-f5bd-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="198a3108-f5be-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="32081654-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</ownedType>
<packagedElement>
<reflist>
<ref refid="0b54a350-f59d-11ef-bfb1-4cbb5880a0b8"/>
<ref refid="a300f58a-f5bd-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="ad4c68d0-f5bd-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="198a3108-f5be-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="32081654-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="25a17c58-f5be-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="41318a02-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</packagedElement>
</UML:Package>
<UML:Diagram id="58d6c536-66f8-11ec-b4c8-0456e5e540ed">
<element>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</element>
<name>
<val>Новая диаграмма</val>
</name>
<ownedPresentation>
<reflist>
<ref refid="0b54edc4-f59d-11ef-bfb1-4cbb5880a0b8"/>
<ref refid="a3018fc2-f5bd-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="ad4d2aae-f5bd-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="198aace6-f5be-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="320868c0-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="b760bcd4-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="bff32eae-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="d5113ca4-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="e8042ad8-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="531831f2-f5c0-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="5ccae8d4-f5c0-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="b6e0240e-f5bd-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="254e40f6-f5be-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="2bb6e87a-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="40e36d2c-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="bcc07c64-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="f6b48e4c-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="f89ba010-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="fabe0540-f5bf-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="6f993af6-f5c0-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</ownedPresentation>
</UML:Diagram>
<UML:Class id="0b54a350-f59d-11ef-bfb1-4cbb5880a0b8">
<clientDependency>
<reflist>
<ref refid="25a17c58-f5be-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</clientDependency>
<comment>
<reflist>
<ref refid="bff31afe-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</comment>
<isAbstract>
<val>1</val>
</isAbstract>
<name>
<val>AbstractMangaParser</val>
</name>
<owningPackage>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</owningPackage>
<package>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</package>
<presentation>
<reflist>
<ref refid="0b54edc4-f59d-11ef-bfb1-4cbb5880a0b8"/>
</reflist>
</presentation>
<specialization>
<reflist>
<ref refid="b969dac6-f5bd-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="2c236356-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</specialization>
</UML:Class>
<UML:ClassItem id="0b54edc4-f59d-11ef-bfb1-4cbb5880a0b8">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 405.16796875, 388.8671875)</val>
</matrix>
<top-left>
<val>(0.0, 0.0)</val>
</top-left>
<width>
<val>158.0</val>
</width>
<height>
<val>60.0</val>
</height>
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<show_attributes>
<val>0</val>
</show_attributes>
<show_operations>
<val>0</val>
</show_operations>
<subject>
<ref refid="0b54a350-f59d-11ef-bfb1-4cbb5880a0b8"/>
</subject>
</UML:ClassItem>
<UML:Class id="a300f58a-f5bd-11ef-9ec2-4cbb5880a0b8">
<comment>
<reflist>
<ref refid="d5112a70-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</comment>
<generalization>
<reflist>
<ref refid="b969dac6-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</generalization>
<isAbstract>
<val>1</val>
</isAbstract>
<name>
<val>PagedMangaParser</val>
</name>
<owningPackage>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</owningPackage>
<package>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</package>
<presentation>
<reflist>
<ref refid="a3018fc2-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
</UML:Class>
<UML:ClassItem id="a3018fc2-f5bd-11ef-9ec2-4cbb5880a0b8">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 476.3368367667698, 525.76953125)</val>
</matrix>
<top-left>
<val>(0.0, 0.0)</val>
</top-left>
<width>
<val>142.0</val>
</width>
<height>
<val>60.0</val>
</height>
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<show_attributes>
<val>0</val>
</show_attributes>
<show_operations>
<val>0</val>
</show_operations>
<subject>
<ref refid="a300f58a-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</subject>
</UML:ClassItem>
<UML:Class id="ad4c68d0-f5bd-11ef-9ec2-4cbb5880a0b8">
<comment>
<reflist>
<ref refid="e80418f4-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</comment>
<generalization>
<reflist>
<ref refid="2c236356-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</generalization>
<isAbstract>
<val>1</val>
</isAbstract>
<name>
<val>SinglePageMangaParser</val>
</name>
<owningPackage>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</owningPackage>
<package>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</package>
<presentation>
<reflist>
<ref refid="ad4d2aae-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
</UML:Class>
<UML:ClassItem id="ad4d2aae-f5bd-11ef-9ec2-4cbb5880a0b8">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 405.16796875, 627.46875)</val>
</matrix>
<top-left>
<val>(0.0, 0.0)</val>
</top-left>
<width>
<val>175.0</val>
</width>
<height>
<val>60.0</val>
</height>
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<show_attributes>
<val>0</val>
</show_attributes>
<show_operations>
<val>0</val>
</show_operations>
<subject>
<ref refid="ad4c68d0-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</subject>
</UML:ClassItem>
<UML:GeneralizationItem id="b6e0240e-f5bd-11ef-9ec2-4cbb5880a0b8">
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<horizontal>
<val>False</val>
</horizontal>
<orthogonal>
<val>False</val>
</orthogonal>
<subject>
<ref refid="b969dac6-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</subject>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 499.2109069824219, 463.45703125)</val>
</matrix>
<points>
<val>[(28.486861756586336, 62.3125), (25.111328125, -14.58984375)]</val>
</points>
<head-connection>
<ref refid="a3018fc2-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</head-connection>
<tail-connection>
<ref refid="0b54edc4-f59d-11ef-bfb1-4cbb5880a0b8"/>
</tail-connection>
</UML:GeneralizationItem>
<UML:Generalization id="b969dac6-f5bd-11ef-9ec2-4cbb5880a0b8">
<general>
<ref refid="0b54a350-f59d-11ef-bfb1-4cbb5880a0b8"/>
</general>
<presentation>
<reflist>
<ref refid="b6e0240e-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
<specific>
<ref refid="a300f58a-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</specific>
</UML:Generalization>
<UML:Interface id="198a3108-f5be-11ef-9ec2-4cbb5880a0b8">
<name>
<val>MangaParser</val>
</name>
<owningPackage>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</owningPackage>
<package>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</package>
<presentation>
<reflist>
<ref refid="198aace6-f5be-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
<supplierDependency>
<reflist>
<ref refid="25a17c58-f5be-11ef-9ec2-4cbb5880a0b8"/>
<ref refid="41318a02-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</supplierDependency>
</UML:Interface>
<UML:InterfaceItem id="198aace6-f5be-11ef-9ec2-4cbb5880a0b8">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 278.00391387939453, 232.92578125)</val>
</matrix>
<top-left>
<val>(0.0, 0.0)</val>
</top-left>
<width>
<val>105.0</val>
</width>
<height>
<val>80.0</val>
</height>
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<show_attributes>
<val>0</val>
</show_attributes>
<show_operations>
<val>0</val>
</show_operations>
<subject>
<ref refid="198a3108-f5be-11ef-9ec2-4cbb5880a0b8"/>
</subject>
<folded>
<val>0</val>
</folded>
</UML:InterfaceItem>
<UML:InterfaceRealizationItem id="254e40f6-f5be-11ef-9ec2-4cbb5880a0b8">
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<horizontal>
<val>False</val>
</horizontal>
<orthogonal>
<val>False</val>
</orthogonal>
<subject>
<ref refid="25a17c58-f5be-11ef-9ec2-4cbb5880a0b8"/>
</subject>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 306.1445007324219, 270.0625)</val>
</matrix>
<points>
<val>[(55.866059373910275, 42.86328125), (164.5765002560883, 118.8046875)]</val>
</points>
<head-connection>
<ref refid="198aace6-f5be-11ef-9ec2-4cbb5880a0b8"/>
</head-connection>
<tail-connection>
<ref refid="0b54edc4-f59d-11ef-bfb1-4cbb5880a0b8"/>
</tail-connection>
</UML:InterfaceRealizationItem>
<UML:InterfaceRealization id="25a17c58-f5be-11ef-9ec2-4cbb5880a0b8">
<client>
<reflist>
<ref refid="0b54a350-f59d-11ef-bfb1-4cbb5880a0b8"/>
</reflist>
</client>
<owningPackage>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</owningPackage>
<presentation>
<reflist>
<ref refid="254e40f6-f5be-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
<supplier>
<reflist>
<ref refid="198a3108-f5be-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</supplier>
</UML:InterfaceRealization>
<UML:GeneralizationItem id="2bb6e87a-f5bf-11ef-9ec2-4cbb5880a0b8">
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<horizontal>
<val>False</val>
</horizontal>
<orthogonal>
<val>False</val>
</orthogonal>
<subject>
<ref refid="2c236356-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</subject>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 436.2929382324219, 439.1913757324219)</val>
</matrix>
<points>
<val>[(20.37646032737257, 188.27737426757812), (18.488327026367188, 9.675811767578125)]</val>
</points>
<head-connection>
<ref refid="ad4d2aae-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</head-connection>
<tail-connection>
<ref refid="0b54edc4-f59d-11ef-bfb1-4cbb5880a0b8"/>
</tail-connection>
</UML:GeneralizationItem>
<UML:Generalization id="2c236356-f5bf-11ef-9ec2-4cbb5880a0b8">
<general>
<ref refid="0b54a350-f59d-11ef-bfb1-4cbb5880a0b8"/>
</general>
<presentation>
<reflist>
<ref refid="2bb6e87a-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
<specific>
<ref refid="ad4c68d0-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</specific>
</UML:Generalization>
<UML:Class id="32081654-f5bf-11ef-9ec2-4cbb5880a0b8">
<clientDependency>
<reflist>
<ref refid="41318a02-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</clientDependency>
<name>
<val>MangaParserWrapper</val>
</name>
<note>
<val></val>
</note>
<owningPackage>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</owningPackage>
<package>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</package>
<presentation>
<reflist>
<ref refid="320868c0-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
</UML:Class>
<UML:ClassItem id="320868c0-f5bf-11ef-9ec2-4cbb5880a0b8">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 128.5008992667698, 410.48990205860804)</val>
</matrix>
<top-left>
<val>(0.0, 0.0)</val>
</top-left>
<width>
<val>158.0</val>
</width>
<height>
<val>60.0</val>
</height>
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<show_attributes>
<val>0</val>
</show_attributes>
<show_operations>
<val>0</val>
</show_operations>
<subject>
<ref refid="32081654-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</subject>
</UML:ClassItem>
<UML:InterfaceRealizationItem id="40e36d2c-f5bf-11ef-9ec2-4cbb5880a0b8">
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<horizontal>
<val>False</val>
</horizontal>
<orthogonal>
<val>False</val>
</orthogonal>
<subject>
<ref refid="41318a02-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</subject>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 306.0585632324219, 249.69920349121094)</val>
</matrix>
<points>
<val>[(11.759223915218172, 63.22657775878906), (-98.55766396565207, 160.7906985673971)]</val>
</points>
<head-connection>
<ref refid="198aace6-f5be-11ef-9ec2-4cbb5880a0b8"/>
</head-connection>
<tail-connection>
<ref refid="320868c0-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</tail-connection>
</UML:InterfaceRealizationItem>
<UML:InterfaceRealization id="41318a02-f5bf-11ef-9ec2-4cbb5880a0b8">
<client>
<reflist>
<ref refid="32081654-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</client>
<owningPackage>
<ref refid="58d6c2e8-66f8-11ec-b4c8-0456e5e540ed"/>
</owningPackage>
<presentation>
<reflist>
<ref refid="40e36d2c-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
<supplier>
<reflist>
<ref refid="198a3108-f5be-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</supplier>
</UML:InterfaceRealization>
<UML:Comment id="b760ac44-f5bf-11ef-9ec2-4cbb5880a0b8">
<body>
<val>Used for providing external api. Do not use this class directly</val>
</body>
<presentation>
<reflist>
<ref refid="b760bcd4-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
</UML:Comment>
<UML:CommentItem id="b760bcd4-f5bf-11ef-9ec2-4cbb5880a0b8">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 108.0561294327963, 550.1347579956054)</val>
</matrix>
<top-left>
<val>(0.0, 0.0)</val>
</top-left>
<width>
<val>183.21868896484375</val>
</width>
<height>
<val>91.23829650878906</val>
</height>
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<subject>
<ref refid="b760ac44-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</subject>
</UML:CommentItem>
<UML:CommentLineItem id="bcc07c64-f5bf-11ef-9ec2-4cbb5880a0b8">
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<horizontal>
<val>False</val>
</horizontal>
<orthogonal>
<val>False</val>
</orthogonal>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 549.205520203852, 278.05499559311954)</val>
</matrix>
<points>
<val>[(-349.5400462886338, 192.4349064654885), (-349.5400462886338, 272.0797624024858)]</val>
</points>
<tail-connection>
<ref refid="b760bcd4-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</tail-connection>
</UML:CommentLineItem>
<UML:Comment id="bff31afe-f5bf-11ef-9ec2-4cbb5880a0b8">
<annotatedElement>
<reflist>
<ref refid="0b54a350-f59d-11ef-bfb1-4cbb5880a0b8"/>
</reflist>
</annotatedElement>
<body>
<val>Extend this class if your manga source provides standard limit-offset based lists (get manga list by offset)</val>
</body>
<presentation>
<reflist>
<ref refid="bff32eae-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
</UML:Comment>
<UML:CommentItem id="bff32eae-f5bf-11ef-9ec2-4cbb5880a0b8">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 673.0610499890082, 367.0515553989646)</val>
</matrix>
<top-left>
<val>(0.0, 0.0)</val>
</top-left>
<width>
<val>228.8028016098773</val>
</width>
<height>
<val>88.0</val>
</height>
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<subject>
<ref refid="bff31afe-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</subject>
</UML:CommentItem>
<UML:Comment id="d5112a70-f5bf-11ef-9ec2-4cbb5880a0b8">
<annotatedElement>
<reflist>
<ref refid="a300f58a-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</annotatedElement>
<body>
<val>Extend this class if your manga source provides paged-based lists (get manga list by page number)</val>
</body>
<presentation>
<reflist>
<ref refid="d5113ca4-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
</UML:Comment>
<UML:CommentItem id="d5113ca4-f5bf-11ef-9ec2-4cbb5880a0b8">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 666.7924311664914, 507.7539062499999)</val>
</matrix>
<top-left>
<val>(0.0, 0.0)</val>
</top-left>
<width>
<val>214.34368896484375</val>
</width>
<height>
<val>88.0</val>
</height>
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<subject>
<ref refid="d5112a70-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</subject>
</UML:CommentItem>
<UML:Comment id="e80418f4-f5bf-11ef-9ec2-4cbb5880a0b8">
<annotatedElement>
<reflist>
<ref refid="ad4c68d0-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</annotatedElement>
<body>
<val>Extend this class if your manga source does not provide pagination (all manga provided in one list)</val>
</body>
<presentation>
<reflist>
<ref refid="e8042ad8-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
</UML:Comment>
<UML:CommentItem id="e8042ad8-f5bf-11ef-9ec2-4cbb5880a0b8">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 666.7924311664914, 560.9671898788581)</val>
</matrix>
<top-left>
<val>(0.0, 58.00435704705592)</val>
</top-left>
<width>
<val>263.9307954323941</val>
</width>
<height>
<val>78.01706672440287</val>
</height>
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<subject>
<ref refid="e80418f4-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</subject>
</UML:CommentItem>
<UML:CommentLineItem id="f6b48e4c-f5bf-11ef-9ec2-4cbb5880a0b8">
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<horizontal>
<val>False</val>
</horizontal>
<orthogonal>
<val>False</val>
</orthogonal>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 561.8951626340418, 549.6101338901756)</val>
</matrix>
<points>
<val>[(56.44167413272805, 7.038279316310902), (104.89726853244963, 8.304008355003589)]</val>
</points>
<head-connection>
<ref refid="a3018fc2-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</head-connection>
<tail-connection>
<ref refid="d5113ca4-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</tail-connection>
</UML:CommentLineItem>
<UML:CommentLineItem id="f89ba010-f5bf-11ef-9ec2-4cbb5880a0b8">
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<horizontal>
<val>False</val>
</horizontal>
<orthogonal>
<val>False</val>
</orthogonal>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 559.3873501340418, 413.0007588901755)</val>
</matrix>
<points>
<val>[(3.7806186159582467, 0.0), (113.67369985496646, 1.6012844908540842)]</val>
</points>
<head-connection>
<ref refid="0b54edc4-f59d-11ef-bfb1-4cbb5880a0b8"/>
</head-connection>
<tail-connection>
<ref refid="bff32eae-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</tail-connection>
</UML:CommentLineItem>
<UML:CommentLineItem id="fabe0540-f5bf-11ef-9ec2-4cbb5880a0b8">
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<horizontal>
<val>False</val>
</horizontal>
<orthogonal>
<val>False</val>
</orthogonal>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 522.3600063840418, 652.6882588901756)</val>
</matrix>
<points>
<val>[(57.80796236595825, 5.29182139794003), (144.43242478244963, 5.657840086725969)]</val>
</points>
<head-connection>
<ref refid="ad4d2aae-f5bd-11ef-9ec2-4cbb5880a0b8"/>
</head-connection>
<tail-connection>
<ref refid="e8042ad8-f5bf-11ef-9ec2-4cbb5880a0b8"/>
</tail-connection>
</UML:CommentLineItem>
<general:Box id="531831f2-f5c0-11ef-9ec2-4cbb5880a0b8">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 375.05802564891326, 349.05453145170736)</val>
</matrix>
<top-left>
<val>(0.0, 3.15625)</val>
</top-left>
<width>
<val>590.6594026101285</val>
</width>
<height>
<val>368.44140625</val>
</height>
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
</general:Box>
<UML:Comment id="5ccad4ca-f5c0-11ef-9ec2-4cbb5880a0b8">
<body>
<val>To create your own parser you have to extends one of these classes</val>
</body>
<presentation>
<reflist>
<ref refid="5ccae8d4-f5c0-11ef-9ec2-4cbb5880a0b8"/>
</reflist>
</presentation>
</UML:Comment>
<UML:CommentItem id="5ccae8d4-f5c0-11ef-9ec2-4cbb5880a0b8">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 756.725301794198, 225.57697659840966)</val>
</matrix>
<top-left>
<val>(0.0, 0.0)</val>
</top-left>
<width>
<val>208.99212646484375</val>
</width>
<height>
<val>73.47482464883183</val>
</height>
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<subject>
<ref refid="5ccad4ca-f5c0-11ef-9ec2-4cbb5880a0b8"/>
</subject>
</UML:CommentItem>
<UML:CommentLineItem id="6f993af6-f5c0-11ef-9ec2-4cbb5880a0b8">
<diagram>
<ref refid="58d6c536-66f8-11ec-b4c8-0456e5e540ed"/>
</diagram>
<horizontal>
<val>False</val>
</horizontal>
<orthogonal>
<val>False</val>
</orthogonal>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 943.6141683885666, 419.2772168262177)</val>
</matrix>
<points>
<val>[(-27.404961772030788, -67.06643537451032), (-27.404961772030788, -120.2254155789762)]</val>
</points>
<head-connection>
<ref refid="531831f2-f5c0-11ef-9ec2-4cbb5880a0b8"/>
</head-connection>
<tail-connection>
<ref refid="5ccae8d4-f5c0-11ef-9ec2-4cbb5880a0b8"/>
</tail-connection>
</UML:CommentLineItem>
</model>
</gaphor>

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

@ -1 +1,15 @@
## Following this blog:
# https://proandroiddev.com/how-we-reduced-our-gradle-build-times-by-over-80-51f2b6d6b05b
kotlin.code.style=official
systemProp.org.gradle.unsafe.configuration-cache=false
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=4096m -XX:+UseParallelGC
org.gradle.configureondemand=true
org.gradle.configuration-cache.problems=warn
## Use these flags on local machine for faster build time
# org.gradle.caching=true
# org.gradle.configuration-cache=true
# org.gradle.vfs.watch=true
# org.gradle.parallel=true
# org.gradle.workers.max=8
# org.gradle.configuration-cache.max-problems=8

@ -0,0 +1,34 @@
[versions]
kotlin = "2.2.10"
ksp = "2.2.10-2.0.2"
coroutines = "1.10.2"
junit = "5.10.1"
okhttp = "5.1.0"
okio = "3.16.0"
json = "20240303"
androidx-collection = "1.5.0"
jsoup = "1.21.2"
quickjs = "1.1.0"
korte = "4.0.10"
simplexml = "2.7.1"
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
[libraries]
ksp-symbol-processing-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
junit-launcher = { group = "org.junit.platform", name = "junit-platform-launcher" }
junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" }
junit-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit" }
junit-params = { group = "org.junit.jupiter", name = "junit-jupiter-params", version.ref = "junit" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
json = { module = "org.json:json", version.ref = "json" }
androidx-collection = { module = "androidx.collection:collection", version.ref = "androidx-collection" }
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
quickjs = { module = "io.webfolder:quickjs", version.ref = "quickjs" }
korte = { module = "com.soywiz.korlibs.korte:korte-jvm", version.ref = "korte" }
simplexml = { module = "org.simpleframework:simple-xml", version.ref = "simplexml" }

Binary file not shown.

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

49
gradlew vendored

@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -80,13 +82,11 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@ -133,22 +133,29 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@ -193,18 +200,28 @@ if "$cygwin" || "$msys" ; then
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

41
gradlew.bat vendored

@ -13,8 +13,10 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -25,7 +27,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@ -56,32 +59,34 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

@ -0,0 +1,2 @@
jdk:
- openjdk17

@ -1,7 +0,0 @@
plugins {
id 'org.jetbrains.kotlin.jvm'
}
dependencies {
implementation 'com.google.devtools.ksp:symbol-processing-api:1.6.21-1.0.5'
}

@ -0,0 +1,11 @@
plugins {
alias(libs.plugins.kotlin.jvm)
}
kotlin {
jvmToolchain(8)
}
dependencies {
implementation(libs.ksp.symbol.processing.api)
}

@ -7,146 +7,180 @@ import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSVisitorVoid
import com.google.devtools.ksp.validate
import java.io.File
import java.io.Writer
import java.util.*
class ParserProcessor(
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger,
private val options: Map<String, String>,
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger,
private val options: Map<String, String>,
) : SymbolProcessor {
private val availableLocales = Locale.getAvailableLocales().toSet()
private val sourceNamePattern = Regex("[A-Z_][A-Z0-9_]{3,}")
override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation("org.koitharu.kotatsu.parsers.MangaSourceParser")
val ret = symbols.filterNot { it.validate() }.toList()
if (!symbols.iterator().hasNext()) {
return ret
}
val dependencies = Dependencies.ALL_FILES
val factoryFile =
try {
codeGenerator.createNewFile(
dependencies = dependencies,
packageName = "org.koitharu.kotatsu.parsers",
fileName = "MangaParserFactory",
)
} catch (e: FileAlreadyExistsException) {
logger.warn(e.toString(), null)
null
}
val sourcesFile =
try {
codeGenerator.createNewFile(
dependencies = dependencies,
packageName = "org.koitharu.kotatsu.parsers.model",
fileName = "MangaSource",
)
} catch (e: FileAlreadyExistsException) {
logger.warn(e.toString(), null)
null
}
val totalCount = sourcesFile?.writer().use { sourcesWriter ->
factoryFile?.writer().use { factoryWriter ->
writeContent(sourcesWriter, factoryWriter, symbols)
}
}
writeSummary(totalCount)
return ret
}
private fun writeContent(
sourcesWriter: Writer?,
factoryWriter: Writer?,
symbols: Sequence<KSAnnotated>,
): Int {
if (sourcesWriter == null && factoryWriter == null) {
return 0
}
factoryWriter?.write(
"""
package org.koitharu.kotatsu.parsers
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.core.MangaParserWrapper
internal fun MangaParserSource.newParser(context: MangaLoaderContext): MangaParser = when (this) {
private val availableLocales = Locale.getAvailableLocales().toSet()
private val sourceNamePattern = Regex("[A-Z_][A-Z0-9_]{3,}")
override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver
.getSymbolsWithAnnotation("org.koitharu.kotatsu.parsers.MangaSourceParser")
val ret = symbols.filterNot { it.validate() }.toList()
if (!symbols.iterator().hasNext()) {
return ret
}
val dependencies = Dependencies.ALL_FILES
val factoryFile = try {
codeGenerator.createNewFile(
dependencies = dependencies,
packageName = "org.koitharu.kotatsu.parsers",
fileName = "MangaParserFactory",
)
} catch (e: FileAlreadyExistsException) {
logger.warn(e.toString(), null)
null
}
val sourcesFile = try {
codeGenerator.createNewFile(
dependencies = dependencies,
packageName = "org.koitharu.kotatsu.parsers.model",
fileName = "MangaSource",
)
} catch (e: FileAlreadyExistsException) {
logger.warn(e.toString(), null)
null
}
sourcesFile?.writer().use { sourcesWriter ->
factoryFile?.writer().use { factoryWriter ->
writeContent(sourcesWriter, factoryWriter, symbols)
}
}
return ret
}
private fun writeContent(
sourcesWriter: Writer?,
factoryWriter: Writer?,
symbols: Sequence<KSAnnotated>,
) {
if (sourcesWriter == null && factoryWriter == null) {
return
}
factoryWriter?.write(
"""
package org.koitharu.kotatsu.parsers
import org.koitharu.kotatsu.parsers.model.MangaSource
fun MangaSource.newParser(context: MangaLoaderContext): MangaParser = when (this) {
""".trimIndent(),
)
sourcesWriter?.write(
"""
package org.koitharu.kotatsu.parsers.model
enum class MangaSource(
val title: String,
val locale: String?,
) {
LOCAL("Local", null),
)
sourcesWriter?.write(
"""
package org.koitharu.kotatsu.parsers.model
public enum class MangaParserSource(
public val title: String,
public val locale: String,
public val contentType: ContentType,
public val isBroken: Boolean,
): MangaSource {
""".trimIndent(),
)
val visitor = ParserVisitor(sourcesWriter, factoryWriter)
symbols
.filter { it is KSClassDeclaration && it.validate() }
.forEach { it.accept(visitor, Unit) }
factoryWriter?.write(
"""
MangaSource.LOCAL -> throw NotImplementedError("Local manga parser is not supported")
MangaSource.DUMMY -> throw NotImplementedError("Dummy manga parser cannot be instantiated")
}.also {
)
val visitor = ParserVisitor(sourcesWriter, factoryWriter)
val totalCount = symbols
.filter { it is KSClassDeclaration && it.validate() }
.onEach { it.accept(visitor, Unit) }
.count()
factoryWriter?.write(
"""
}.let {
require(it.source == this) {
"Cannot instantiate manga parser: ${'$'}name mapped to ${'$'}{it.source}"
}
MangaParserWrapper(it)
}
""".trimIndent(),
)
sourcesWriter?.write(
"""
DUMMY("Dummy", null),
)
sourcesWriter?.write(
"""
;
}
""".trimIndent(),
)
}
)
return totalCount
}
private inner class ParserVisitor(
private val sourcesWriter: Writer?,
private val factoryWriter: Writer?,
) : KSVisitorVoid() {
private fun writeSummary(totalCount: Int) {
val file = File(options["summaryOutputDir"] ?: return, "summary.yaml")
file.writeText("total: $totalCount")
}
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
if (classDeclaration.classKind != ClassKind.CLASS || classDeclaration.isAbstract()) {
logger.error("Only non-abstract can be annotated with @MangaSourceParser", classDeclaration)
}
val annotation = classDeclaration.annotations.single { it.shortName.asString() == "MangaSourceParser" }
val name = annotation.arguments.single { it.name?.asString() == "name" }.value as String
val title = annotation.arguments.single { it.name?.asString() == "title" }.value as String
val locale = annotation.arguments.single { it.name?.asString() == "locale" }.value as String
val localeString = if (locale.isEmpty()) "null" else "\"$locale\""
val localeObj = if (locale.isEmpty()) null else Locale(locale)
val localeTitle = localeObj?.getDisplayLanguage(localeObj)
if (localeObj != null && localeObj !in availableLocales) {
logger.error("Manga source $name has wrong locale: $localeTitle")
}
private inner class ParserVisitor(
private val sourcesWriter: Writer?,
private val factoryWriter: Writer?,
) : KSVisitorVoid() {
private val titles = HashMap<String, String>()
if (!sourceNamePattern.matches(name)) {
logger.error("Manga source name must be uppercase: $name")
}
override fun visitClassDeclaration(
classDeclaration: KSClassDeclaration,
data: Unit,
) {
if (classDeclaration.classKind != ClassKind.CLASS || classDeclaration.isAbstract()) {
logger.error("Only non-abstract can be annotated with @MangaSourceParser", classDeclaration)
}
val annotation = classDeclaration.annotations.single { it.shortName.asString() == "MangaSourceParser" }
val deprecation = classDeclaration.annotations.singleOrNull { it.shortName.asString() == "Deprecated" }
val isBroken = classDeclaration.annotations.any { it.shortName.asString() == "Broken" }
val name = annotation.arguments.single { it.name?.asString() == "name" }.value as String
val title = annotation.arguments.single { it.name?.asString() == "title" }.value as String
val locale = annotation.arguments.single { it.name?.asString() == "locale" }.value as String
val type = annotation.arguments.single { it.name?.asString() == "type" }.value
val localeString = "\"$locale\""
val localeObj = if (locale.isEmpty()) null else Locale(locale)
val localeTitle = localeObj?.getDisplayLanguage(localeObj)
if (localeObj != null && localeObj !in availableLocales) {
logger.error("Manga source $name has wrong locale: $localeTitle")
}
val constructor = classDeclaration.primaryConstructor
if (constructor == null || constructor.parameters.count { !it.hasDefault } != 1) {
logger.error(
"Class with @MangaSourceParser must have a primary constructor with one parameter",
classDeclaration,
)
}
if (!sourceNamePattern.matches(name)) {
logger.error("Manga source name must be uppercase: $name")
}
val constructor = classDeclaration.primaryConstructor
if (constructor == null || constructor.parameters.count { !it.hasDefault } != 1) {
logger.error(
"Class with @MangaSourceParser must have a primary constructor with one parameter",
classDeclaration,
)
}
val className = checkNotNull(classDeclaration.qualifiedName?.asString()) { "Class name is null" }
val prevTitleClass = titles.put(title, className)
if (prevTitleClass != null) {
logger.warn("Source title duplication: \"$title\" is assigned to both $prevTitleClass and $className")
}
val className = classDeclaration.qualifiedName?.asString()
factoryWriter?.write("\tMangaSource.$name -> $className(context)\n")
val localeComment = localeTitle?.toTitleCase(localeObj)?.let { " /* $it */" }.orEmpty()
sourcesWriter?.write("\t$name(\"$title\", $localeString$localeComment),\n")
}
}
}
factoryWriter?.write("\tMangaParserSource.$name -> $className(context)\n")
val deprecationString =
if (deprecation != null) {
val reason =
deprecation.arguments
.find { it.name?.asString() == "message" }
?.value
?.toString() ?: "Unknown reason"
"@Deprecated(\"$reason\") "
} else {
""
}
val localeComment = localeTitle?.toTitleCase(localeObj)?.let { " /* $it */" }.orEmpty()
sourcesWriter?.write(
"\t$deprecationString$name(\"$title\", $localeString$localeComment, $type, $isBroken),\n",
)
}
}
}

@ -1,22 +0,0 @@
pluginManagement {
plugins {
id 'com.google.devtools.ksp' version '1.6.21-1.0.5'
id 'org.jetbrains.kotlin.jvm' version '1.6.21'
}
repositories {
gradlePluginPortal()
google()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = 'kotatsu-parsers'
include 'kotatsu-parsers-ksp'

@ -0,0 +1,18 @@
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
rootProject.name = "kotatsu-parsers"
include("kotatsu-parsers-ksp")

@ -0,0 +1,14 @@
package org.koitharu.kotatsu.parsers
/**
* Annotate [MangaParser] implementation to mark this parser as broken instead of removing it
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
internal annotation class Broken(
/**
* Reason why this parser is broken
*/
val message: String = "",
)

@ -0,0 +1,18 @@
package org.koitharu.kotatsu.parsers
public object ErrorMessages {
public const val FILTER_MULTIPLE_STATES_NOT_SUPPORTED: String = "Multiple states are not supported by this source"
public const val FILTER_MULTIPLE_GENRES_NOT_SUPPORTED: String = "Multiple genres are not supported by this source"
public const val FILTER_MULTIPLE_CONTENT_RATING_NOT_SUPPORTED: String =
"Multiple Content ratings are not supported by this source"
public const val FILTER_MULTIPLE_CONTENT_TYPES_NOT_SUPPORTED: String =
"Multiple Content types are not supported by this source"
public const val FILTER_MULTIPLE_DEMOGRAPHICS_NOT_SUPPORTED: String =
"Multiple Demographics are not supported by this source"
public const val FILTER_BOTH_LOCALE_GENRES_NOT_SUPPORTED: String =
"Filtering by both genres and locale is not supported by this source"
public const val FILTER_BOTH_STATES_GENRES_NOT_SUPPORTED: String =
"Filtering by both genres and states is not supported by this source"
public const val SEARCH_NOT_SUPPORTED: String = "Search is not supported by this source"
}

@ -11,4 +11,4 @@ package org.koitharu.kotatsu.parsers
@SinceKotlin("1.3")
@RequiresOptIn
@MustBeDocumented
annotation class InternalParsersApi()
public annotation class InternalParsersApi

@ -1,166 +1,78 @@
package org.koitharu.kotatsu.parsers
import okhttp3.*
import okhttp3.CookieJar
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
import org.jsoup.HttpStatusException
import okhttp3.OkHttpClient
import okhttp3.Response
import org.koitharu.kotatsu.parsers.bitmap.Bitmap
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
import org.koitharu.kotatsu.parsers.exception.GraphQLException
import org.koitharu.kotatsu.parsers.exception.NotFoundException
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.await
import org.koitharu.kotatsu.parsers.util.parseJson
import org.koitharu.kotatsu.parsers.util.LinkResolver
import java.util.*
abstract class MangaLoaderContext {
public abstract class MangaLoaderContext {
protected abstract val httpClient: OkHttpClient
public abstract val httpClient: OkHttpClient
abstract val cookieJar: CookieJar
public abstract val cookieJar: CookieJar
/**
* Do a GET http request to specific url
* @param url
* @param headers an additional headers for request, may be null
*/
suspend fun httpGet(url: HttpUrl, headers: Headers? = null): Response {
val request = Request.Builder()
.get()
.url(url)
if (headers != null) {
request.headers(headers)
}
return httpClient.newCall(request.build()).await().ensureSuccess()
}
public fun newParserInstance(source: MangaParserSource): MangaParser = source.newParser(this)
suspend fun httpGet(url: String, headers: Headers? = null): Response {
return httpGet(url.toHttpUrl(), headers)
}
public fun newLinkResolver(link: HttpUrl): LinkResolver = LinkResolver(this, link)
public fun newLinkResolver(link: String): LinkResolver = newLinkResolver(link.toHttpUrl())
/**
* Do a HEAD http request to specific url
* @param url
* @param headers an additional headers for request, may be null
*/
suspend fun httpHead(url: String, headers: Headers? = null): Response {
val request = Request.Builder()
.head()
.url(url)
if (headers != null) {
request.headers(headers)
}
return httpClient.newCall(request.build()).await().ensureSuccess()
}
public open fun encodeBase64(data: ByteArray): String = Base64.getEncoder().encodeToString(data)
public open fun decodeBase64(data: String): ByteArray = Base64.getDecoder().decode(data)
public open fun getPreferredLocales(): List<Locale> = listOf(Locale.getDefault())
/**
* Do a POST http request to specific url with `multipart/form-data` payload
* @param url
* @param form payload as key=>value map
* @param headers an additional headers for request, may be null
* Execute JavaScript code and return result
* @param script JavaScript source code
* @return execution result as string, may be null
*/
suspend fun httpPost(
url: String,
form: Map<String, String>,
headers: Headers? = null,
): Response {
val body = FormBody.Builder()
form.forEach { (k, v) ->
body.addEncoded(k, v)
}
val request = Request.Builder()
.post(body.build())
.url(url)
if (headers != null) {
request.headers(headers)
}
return httpClient.newCall(request.build()).await().ensureSuccess()
}
@Deprecated("Provide a base url")
public abstract suspend fun evaluateJs(script: String): String?
/**
* Do a POST http request to specific url with `multipart/form-data` payload
* @param url
* @param payload payload as `key=value` string with `&` separator
* @param headers an additional headers for request, may be null
* Execute JavaScript code and return result
* @param script JavaScript source code
* @param baseUrl url of page script will be executed in context of
* @return execution result as string, may be null
*/
suspend fun httpPost(
url: String,
payload: String,
headers: Headers?,
): Response {
val body = FormBody.Builder()
payload.split('&').forEach {
val pos = it.indexOf('=')
if (pos != -1) {
val k = it.substring(0, pos)
val v = it.substring(pos + 1)
body.addEncoded(k, v)
}
}
val request = Request.Builder()
.post(body.build())
.url(url)
if (headers != null) {
request.headers(headers)
}
return httpClient.newCall(request.build()).await().ensureSuccess()
}
public abstract suspend fun evaluateJs(baseUrl: String, script: String): String?
/**
* Do a GraphQL request to specific url
* @param endpoint an url
* @param query GraphQL request payload
* Open [url] in browser for some external action (e.g. captcha solving or non cookie-based authorization)
*/
suspend fun graphQLQuery(endpoint: String, query: String): JSONObject {
val body = JSONObject()
body.put("operationName", null as Any?)
body.put("variables", JSONObject())
body.put("query", "{$query}")
val mediaType = "application/json; charset=utf-8".toMediaType()
val requestBody = body.toString().toRequestBody(mediaType)
val request = Request.Builder()
.post(requestBody)
.url(endpoint)
val json = httpClient.newCall(request.build()).await().ensureSuccess().parseJson()
json.optJSONArray("errors")?.let {
if (it.length() != 0) {
throw GraphQLException(it)
}
}
return json
public open fun requestBrowserAction(parser: MangaParser, url: String): Nothing {
throw UnsupportedOperationException("Browser is not available")
}
open fun encodeBase64(data: ByteArray): String = Base64.getEncoder().encodeToString(data)
public abstract fun getConfig(source: MangaSource): MangaSourceConfig
open fun decodeBase64(data: String): ByteArray = Base64.getDecoder().decode(data)
public abstract fun getDefaultUserAgent(): String
open fun getPreferredLocales(): List<Locale> = listOf(Locale.getDefault())
/**
* Helper function to be used in an interceptor
* to descramble images
* @param response Image response
* @param redraw lambda function to implement descrambling logic
*/
public abstract fun redrawImageResponse(
response: Response,
redraw: (image: Bitmap) -> Bitmap,
): Response
/**
* Execute JavaScript code and return result
* @param script JavaScript source code
* @return execution result as string, may be null
* create a new empty Bitmap with given dimensions
*/
abstract suspend fun evaluateJs(script: String): String?
abstract fun getConfig(source: MangaSource): MangaSourceConfig
private fun Response.ensureSuccess(): Response {
val exception: Exception? = when (code) { // Catch some error codes, not all
404 -> NotFoundException(message, request.url.toString())
in 500..599 -> HttpStatusException(message, code, request.url.toString())
else -> null
}
if (exception != null) {
runCatching {
close()
}.onFailure {
exception.addSuppressed(it)
}
throw exception
}
return this
}
}
public abstract fun createBitmap(
width: Int,
height: Int,
): Bitmap
}

@ -1,200 +1,88 @@
package org.koitharu.kotatsu.parsers
import androidx.annotation.CallSuper
import androidx.annotation.VisibleForTesting
import okhttp3.Headers
import okhttp3.HttpUrl
import org.jsoup.nodes.Element
import okhttp3.Interceptor
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.FaviconParser
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
import org.koitharu.kotatsu.parsers.model.search.MangaSearchQuery
import org.koitharu.kotatsu.parsers.model.search.MangaSearchQueryCapabilities
import org.koitharu.kotatsu.parsers.util.LinkResolver
import org.koitharu.kotatsu.parsers.util.convertToMangaSearchQuery
import org.koitharu.kotatsu.parsers.util.toMangaListFilterCapabilities
import java.util.*
abstract class MangaParser @InternalParsersApi constructor(val source: MangaSource) {
public interface MangaParser : Interceptor {
protected abstract val context: MangaLoaderContext
public val source: MangaParserSource
/**
* Supported [SortOrder] variants. Must not be empty.
*
* For better performance use [EnumSet] for more than one item.
*/
abstract val sortOrders: Set<SortOrder>
public val availableSortOrders: Set<SortOrder>
val config by lazy { context.getConfig(source) }
@Deprecated("Too complex. Use filterCapabilities instead")
public val searchQueryCapabilities: MangaSearchQueryCapabilities
val sourceLocale: Locale?
get() = source.locale?.let { Locale(it) }
public val filterCapabilities: MangaListFilterCapabilities
/**
* Provide default domain and available alternatives, if any.
*
* Never hardcode domain in requests, use [getDomain] instead.
*/
protected abstract val configKeyDomain: ConfigKey.Domain
public val config: MangaSourceConfig
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
internal open val headers: Headers? = null
public val authorizationProvider: MangaParserAuthProvider?
get() = this as? MangaParserAuthProvider
/**
* Used as fallback if value of `sortOrder` passed to [getList] is null
*/
protected open val defaultSortOrder: SortOrder
get() {
val supported = sortOrders
return SortOrder.values().first { it in supported }
}
/**
* Parse list of manga by specified criteria
* Provide default domain and available alternatives, if any.
*
* @param offset starting from 0 and used for pagination.
* Note than passed value may not be divisible by internal page size, so you should adjust it manually.
* @param query search query, may be null or empty if no search needed
* @param tags genres for filtering, values from [getTags] and [Manga.tags]. May be null or empty
* @param sortOrder one of [sortOrders] or null for default value
* Never hardcode domain in requests, use [domain] instead.
*/
@JvmSynthetic
@InternalParsersApi
abstract suspend fun getList(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga>
public val configKeyDomain: ConfigKey.Domain
/**
* Parse list of manga with search by text query
*
* @param offset starting from 0 and used for pagination.
* @param query search query
*/
open suspend fun getList(offset: Int, query: String): List<Manga> {
return getList(offset, query, null, defaultSortOrder)
}
public val domain: String
/**
* Parse list of manga by specified criteria
*
* @param offset starting from 0 and used for pagination.
* Note than passed value may not be divisible by internal page size, so you should adjust it manually.
* @param tags genres for filtering, values from [getTags] and [Manga.tags]. May be null or empty
* @param sortOrder one of [sortOrders] or null for default value
*/
open suspend fun getList(offset: Int, tags: Set<MangaTag>?, sortOrder: SortOrder?): List<Manga> {
return getList(offset, null, tags, sortOrder ?: defaultSortOrder)
}
@Deprecated("Too complex. Use getList with filter instead")
public suspend fun getList(query: MangaSearchQuery): List<Manga>
public suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List<Manga>
/**
* Parse details for [Manga]: chapters list, description, large cover, etc.
* Must return the same manga, may change any fields excepts id, url and source
* @see Manga.copy
*/
abstract suspend fun getDetails(manga: Manga): Manga
public suspend fun getDetails(manga: Manga): Manga
/**
* Parse pages list for specified chapter.
* @see MangaPage for details
*/
abstract suspend fun getPages(chapter: MangaChapter): List<MangaPage>
public suspend fun getPages(chapter: MangaChapter): List<MangaPage>
/**
* Fetch direct link to the page image.
*/
open suspend fun getPageUrl(page: MangaPage): String = page.url.toAbsoluteUrl(getDomain())
public suspend fun getPageUrl(page: MangaPage): String
/**
* Fetch available tags (genres) for source
*/
abstract suspend fun getTags(): Set<MangaTag>
/**
* Returns direct link to the website favicon
*/
@Deprecated(
message = "Use parseFavicons() to get multiple favicons with different size",
replaceWith = ReplaceWith("parseFavicons()"),
)
open fun getFaviconUrl() = "https://${getDomain()}/favicon.ico"
public suspend fun getFilterOptions(): MangaListFilterOptions
/**
* Parse favicons from the main page of the source`s website
*/
open suspend fun getFavicons(): Favicons {
return FaviconParser(context, getDomain(), headers).parseFavicons()
}
@CallSuper
open fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
keys.add(configKeyDomain)
}
public suspend fun getFavicons(): Favicons
/* Utils */
public fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>)
fun getDomain(): String {
return config[configKeyDomain]
}
public suspend fun getRelatedManga(seed: Manga): List<Manga>
fun getDomain(subdomain: String): String {
val domain = getDomain()
return subdomain + "." + domain.removePrefix("www.")
}
fun urlBuilder(): HttpUrl.Builder {
return HttpUrl.Builder()
.scheme("https")
.host(getDomain())
}
public fun getRequestHeaders(): Headers
/**
* Create a unique id for [Manga]/[MangaChapter]/[MangaPage].
* @param url must be relative url, without a domain
* @see [Manga.id]
* @see [MangaChapter.id]
* @see [MangaPage.id]
* Return [Manga] object by web link to it
* @see [Manga.publicUrl]
*/
@InternalParsersApi
protected fun generateUid(url: String): Long {
var h = 1125899906842597L
source.name.forEach { c ->
h = 31 * h + c.code
}
url.forEach { c ->
h = 31 * h + c.code
}
return h
}
/**
* Create a unique id for [Manga]/[MangaChapter]/[MangaPage].
* @param id an internal identifier
* @see [Manga.id]
* @see [MangaChapter.id]
* @see [MangaPage.id]
*/
@InternalParsersApi
protected fun generateUid(id: Long): Long {
var h = 1125899906842597L
source.name.forEach { c ->
h = 31 * h + c.code
}
h = 31 * h + id
return h
}
@InternalParsersApi
protected fun Element.parseFailed(message: String? = null): Nothing {
throw ParseException(message, ownerDocument()?.location() ?: baseUri(), null)
}
@InternalParsersApi
protected fun Set<MangaTag>?.oneOrThrowIfMany(): MangaTag? {
return when {
isNullOrEmpty() -> null
size == 1 -> first()
else -> throw IllegalArgumentException("Multiple genres are not supported by this source")
}
}
}
public suspend fun resolveLink(resolver: LinkResolver, link: HttpUrl): Manga?
}

@ -6,19 +6,19 @@ import org.koitharu.kotatsu.parsers.exception.ParseException
/**
* Implement this in your parser for authorization support
*/
interface MangaParserAuthProvider {
public interface MangaParserAuthProvider {
/**
* Return link to the login page, which will be opened in browser.
* Must be an absolute url
*/
val authUrl: String
public val authUrl: String
/**
* Quick check if user is logged in.
* In most case you should check for cookies in [MangaLoaderContext.cookieJar].
*/
val isAuthorized: Boolean
public suspend fun isAuthorized(): Boolean
/**
* Fetch and return current user`s name or login.
@ -26,5 +26,5 @@ interface MangaParserAuthProvider {
* @throws [AuthRequiredException] if user is not logged in or authorization is expired
* @throws [ParseException] on parsing error
*/
suspend fun getUsername(): String
}
public suspend fun getUsername(): String
}

@ -1,20 +1,28 @@
package org.koitharu.kotatsu.parsers
import org.koitharu.kotatsu.parsers.model.ContentType
/**
* Annotate each [MangaParser] implementation with this annotation, used by codegen
*/
@Target(AnnotationTarget.CLASS)
annotation class MangaSourceParser(
@Retention(AnnotationRetention.SOURCE)
internal annotation class MangaSourceParser(
/**
* Name of manga source. Used as an Enum value, must be UPPER_CASE and unique.
*/
val name: String,
/**
* User-friendly title of manga source. In most case equals the website name.
* Avoid extra whitespaces between the words if it is not required.
*/
val title: String,
/**
* Language code (for example "en" or "ru") or blank if parser provide manga on different languages.
*/
val locale: String = "",
)
/**
* Type of content provided by parser. See [ContentType] for more info
*/
val type: ContentType = ContentType.MANGA,
)

@ -1,50 +0,0 @@
package org.koitharu.kotatsu.parsers
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.util.Paginator
@InternalParsersApi
abstract class PagedMangaParser(
source: MangaSource,
pageSize: Int,
searchPageSize: Int = pageSize,
) : MangaParser(source) {
protected val paginator = Paginator(pageSize)
protected val searchPaginator = Paginator(searchPageSize)
override suspend fun getList(offset: Int, query: String): List<Manga> {
return getList(searchPaginator, offset, query, null, defaultSortOrder)
}
override suspend fun getList(offset: Int, tags: Set<MangaTag>?, sortOrder: SortOrder?): List<Manga> {
return getList(paginator, offset, null, tags, sortOrder ?: defaultSortOrder)
}
@InternalParsersApi
@Deprecated("You should use getListPage for PagedMangaParser", level = DeprecationLevel.HIDDEN)
final override suspend fun getList(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> = throw UnsupportedOperationException("You should use getListPage for PagedMangaParser")
abstract suspend fun getListPage(page: Int, query: String?, tags: Set<MangaTag>?, sortOrder: SortOrder): List<Manga>
private suspend fun getList(
paginator: Paginator,
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
val page = paginator.getPage(offset)
val list = getListPage(page, query, tags, sortOrder)
paginator.onListReceived(offset, page, list.size)
return list
}
}

@ -0,0 +1,9 @@
package org.koitharu.kotatsu.parsers.bitmap
public interface Bitmap {
public val width: Int
public val height: Int
public fun drawBitmap(sourceBitmap: Bitmap, src: Rect, dst: Rect)
}

@ -0,0 +1,15 @@
package org.koitharu.kotatsu.parsers.bitmap
public data class Rect(
val left: Int = 0,
val top: Int = 0,
val right: Int = 0,
val bottom: Int = 0,
) {
val width: Int
get() = right - left
val height: Int
get() = bottom - top
}

@ -1,13 +1,37 @@
package org.koitharu.kotatsu.parsers.config
sealed class ConfigKey<T>(
val key: String,
public sealed class ConfigKey<T>(
@JvmField public val key: String,
) {
abstract val defaultValue: T
public abstract val defaultValue: T
class Domain(
public class Domain(
@JvmField @JvmSuppressWildcards public vararg val presetValues: String,
) : ConfigKey<String>("domain") {
init {
require(presetValues.isNotEmpty()) { "You must provide at least one domain" }
}
override val defaultValue: String
get() = presetValues.first()
}
public class ShowSuspiciousContent(
override val defaultValue: Boolean,
) : ConfigKey<Boolean>("show_suspicious")
public class UserAgent(
override val defaultValue: String,
val presetValues: Array<String>?,
) : ConfigKey<String>("domain")
}
) : ConfigKey<String>("user_agent")
public class SplitByTranslations(
override val defaultValue: Boolean,
) : ConfigKey<Boolean>("split_translations")
public class PreferredImageServer(
public val presetValues: Map<String?, String?>,
override val defaultValue: String?,
) : ConfigKey<String?>("img_server")
}

@ -1,6 +1,6 @@
package org.koitharu.kotatsu.parsers.config
interface MangaSourceConfig {
public interface MangaSourceConfig {
operator fun <T> get(key: ConfigKey<T>): T
}
public operator fun <T> get(key: ConfigKey<T>): T
}

@ -0,0 +1,105 @@
package org.koitharu.kotatsu.parsers.core
import androidx.annotation.CallSuper
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.Interceptor
import okhttp3.Response
import org.koitharu.kotatsu.parsers.InternalParsersApi
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.model.search.MangaSearchQuery
import org.koitharu.kotatsu.parsers.model.search.MangaSearchQueryCapabilities
import org.koitharu.kotatsu.parsers.network.OkHttpWebClient
import org.koitharu.kotatsu.parsers.network.WebClient
import org.koitharu.kotatsu.parsers.util.*
import java.util.*
@Suppress("OVERRIDE_DEPRECATION")
@InternalParsersApi
public abstract class AbstractMangaParser @InternalParsersApi constructor(
@property:InternalParsersApi public val context: MangaLoaderContext,
public final override val source: MangaParserSource,
) : MangaParser {
public final override val searchQueryCapabilities: MangaSearchQueryCapabilities
get() = filterCapabilities.toMangaSearchQueryCapabilities()
public override val config: MangaSourceConfig by lazy { context.getConfig(source) }
public open val sourceLocale: Locale
get() = if (source.locale.isEmpty()) Locale.ROOT else Locale(source.locale)
protected val sourceContentRating: ContentRating?
get() = if (source.contentType == ContentType.HENTAI) {
ContentRating.ADULT
} else {
null
}
protected val isNsfwSource: Boolean = source.contentType == ContentType.HENTAI
protected open val userAgentKey: ConfigKey.UserAgent = ConfigKey.UserAgent(context.getDefaultUserAgent())
override fun getRequestHeaders(): Headers = Headers.Builder()
.add("User-Agent", config[userAgentKey])
.build()
/**
* Used as fallback if value of `order` passed to [getList] is null
*/
public open val defaultSortOrder: SortOrder
get() {
val supported = availableSortOrders
return SortOrder.entries.first { it in supported }
}
final override val domain: String
get() = config[configKeyDomain]
@JvmField
protected val webClient: WebClient = OkHttpWebClient(context.httpClient, source)
/**
* Search list of manga by specified searchQuery
*
* @param query searchQuery
*/
public final override suspend fun getList(query: MangaSearchQuery): List<Manga> = getList(
offset = query.offset,
order = query.order ?: defaultSortOrder,
filter = convertToMangaListFilter(query),
)
/**
* Fetch direct link to the page image.
*/
public override suspend fun getPageUrl(page: MangaPage): String = page.url.toAbsoluteUrl(domain)
/**
* Parse favicons from the main page of the source`s website
*/
public override suspend fun getFavicons(): Favicons {
return FaviconParser(webClient, domain).parseFavicons()
}
@CallSuper
public override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
keys.add(configKeyDomain)
}
public override suspend fun getRelatedManga(seed: Manga): List<Manga> {
return RelatedMangaFinder(listOf(this)).invoke(seed)
}
/**
* Return [Manga] object by web link to it
* @see [Manga.publicUrl]
*/
override suspend fun resolveLink(resolver: LinkResolver, link: HttpUrl): Manga? = null
override fun intercept(chain: Interceptor.Chain): Response = chain.proceed(chain.request())
}

@ -0,0 +1,94 @@
package org.koitharu.kotatsu.parsers.core
import androidx.annotation.CallSuper
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.Interceptor
import okhttp3.Response
import org.koitharu.kotatsu.parsers.InternalParsersApi
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.network.OkHttpWebClient
import org.koitharu.kotatsu.parsers.network.WebClient
import org.koitharu.kotatsu.parsers.util.*
import java.util.*
@Deprecated("Too complex. Use AbstractMangaParser instead")
internal abstract class FlexibleMangaParser @InternalParsersApi constructor(
@property:InternalParsersApi val context: MangaLoaderContext,
final override val source: MangaParserSource,
) : MangaParser {
override val config: MangaSourceConfig by lazy { context.getConfig(source) }
open val sourceLocale: Locale
get() = if (source.locale.isEmpty()) Locale.ROOT else Locale(source.locale)
protected open val userAgentKey: ConfigKey.UserAgent = ConfigKey.UserAgent(context.getDefaultUserAgent())
final override val filterCapabilities: MangaListFilterCapabilities
get() = searchQueryCapabilities.toMangaListFilterCapabilities()
protected val sourceContentRating: ContentRating?
get() = if (source.contentType == ContentType.HENTAI) {
ContentRating.ADULT
} else {
null
}
final override val domain: String
get() = config[configKeyDomain]
@Deprecated("Override intercept() instead")
override fun getRequestHeaders(): Headers = Headers.Builder()
.add("User-Agent", config[userAgentKey])
.build()
/**
* Used as fallback if value of `order` passed to [getList] is null
*/
open val defaultSortOrder: SortOrder
get() {
val supported = availableSortOrders
return SortOrder.entries.first { it in supported }
}
@JvmField
protected val webClient: WebClient = OkHttpWebClient(context.httpClient, source)
/**
* Fetch direct link to the page image.
*/
override suspend fun getPageUrl(page: MangaPage): String = page.url.toAbsoluteUrl(domain)
final override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
return getList(convertToMangaSearchQuery(offset, order, filter))
}
/**
* Parse favicons from the main page of the source`s website
*/
override suspend fun getFavicons(): Favicons {
return FaviconParser(webClient, domain).parseFavicons()
}
@CallSuper
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
keys.add(configKeyDomain)
}
override suspend fun getRelatedManga(seed: Manga): List<Manga> {
return RelatedMangaFinder(listOf(this)).invoke(seed)
}
/**
* Return [Manga] object by web link to it
* @see [Manga.publicUrl]
*/
override suspend fun resolveLink(resolver: LinkResolver, link: HttpUrl): Manga? = null
override fun intercept(chain: Interceptor.Chain): Response = chain.proceed(chain.request())
}

@ -0,0 +1,60 @@
package org.koitharu.kotatsu.parsers.core
import androidx.annotation.VisibleForTesting
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.search.MangaSearchQuery
import org.koitharu.kotatsu.parsers.model.search.SearchableField
import org.koitharu.kotatsu.parsers.util.Paginator
@Deprecated("Too complex. Use PagedMangaParser instead")
internal abstract class FlexiblePagedMangaParser(
context: MangaLoaderContext,
source: MangaParserSource,
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) @JvmField public val pageSize: Int,
searchPageSize: Int = pageSize,
) : FlexibleMangaParser(context, source) {
@JvmField
protected val paginator: Paginator = Paginator(pageSize)
@JvmField
protected val searchPaginator: Paginator = Paginator(searchPageSize)
final override suspend fun getList(query: MangaSearchQuery): List<Manga> {
var containTitleNameCriteria = false
query.criteria.forEach {
if (it.field == SearchableField.TITLE_NAME) {
containTitleNameCriteria = true
}
}
return searchManga(
paginator = if (containTitleNameCriteria) {
paginator
} else {
searchPaginator
},
query = query,
)
}
public abstract suspend fun getListPage(query: MangaSearchQuery, page: Int): List<Manga>
protected fun setFirstPage(firstPage: Int, firstPageForSearch: Int = firstPage) {
paginator.firstPage = firstPage
searchPaginator.firstPage = firstPageForSearch
}
private suspend fun searchManga(
paginator: Paginator,
query: MangaSearchQuery,
): List<Manga> {
val offset: Int = query.offset
val page = paginator.getPage(offset)
val list = getListPage(query, page)
paginator.onListReceived(offset, page, list.size)
return list
}
}

@ -0,0 +1,77 @@
package org.koitharu.kotatsu.parsers.core
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.model.search.MangaSearchQuery
import org.koitharu.kotatsu.parsers.util.mergeWith
internal class MangaParserWrapper(
private val delegate: MangaParser,
) : MangaParser by delegate {
override val authorizationProvider: MangaParserAuthProvider?
get() = delegate as? MangaParserAuthProvider
@Deprecated("Too complex. Use getList with filter instead")
override suspend fun getList(query: MangaSearchQuery): List<Manga> = withContext(Dispatchers.Default) {
if (!query.skipValidation) {
searchQueryCapabilities.validate(query)
}
delegate.getList(query)
}
override suspend fun getList(
offset: Int,
order: SortOrder,
filter: MangaListFilter,
): List<Manga> = withContext(Dispatchers.Default) {
delegate.getList(offset, order, filter)
}
override suspend fun getDetails(manga: Manga): Manga = withContext(Dispatchers.Default) {
delegate.getDetails(manga)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> = withContext(Dispatchers.Default) {
delegate.getPages(chapter)
}
override suspend fun getPageUrl(page: MangaPage): String = withContext(Dispatchers.Default) {
delegate.getPageUrl(page)
}
override suspend fun getFilterOptions(): MangaListFilterOptions = withContext(Dispatchers.Default) {
delegate.getFilterOptions()
}
override suspend fun getFavicons(): Favicons = withContext(Dispatchers.Default) {
delegate.getFavicons()
}
override suspend fun getRelatedManga(seed: Manga): List<Manga> = withContext(Dispatchers.Default) {
delegate.getRelatedManga(seed)
}
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val headers = request.headers.newBuilder()
.mergeWith(delegate.getRequestHeaders(), replaceExisting = false)
.build()
val newRequest = request.newBuilder().headers(headers).build()
return delegate.intercept(ProxyChain(chain, newRequest))
}
private class ProxyChain(
private val delegate: Interceptor.Chain,
private val request: Request,
) : Interceptor.Chain by delegate {
override fun request(): Request = request
}
}

@ -0,0 +1,57 @@
package org.koitharu.kotatsu.parsers.core
import androidx.annotation.VisibleForTesting
import org.koitharu.kotatsu.parsers.InternalParsersApi
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaListFilter
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.util.Paginator
@InternalParsersApi
public abstract class PagedMangaParser(
context: MangaLoaderContext,
source: MangaParserSource,
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) @JvmField public val pageSize: Int,
searchPageSize: Int = pageSize,
) : AbstractMangaParser(context, source) {
@JvmField
protected val paginator: Paginator = Paginator(pageSize)
@JvmField
protected val searchPaginator: Paginator = Paginator(searchPageSize)
final override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
return getList(
paginator = if (filter.query.isNullOrEmpty()) {
paginator
} else {
searchPaginator
},
offset = offset,
order = order,
filter = filter,
)
}
public abstract suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga>
protected fun setFirstPage(firstPage: Int, firstPageForSearch: Int = firstPage) {
paginator.firstPage = firstPage
searchPaginator.firstPage = firstPageForSearch
}
private suspend fun getList(
paginator: Paginator,
offset: Int,
order: SortOrder,
filter: MangaListFilter,
): List<Manga> {
val page = paginator.getPage(offset)
val list = getListPage(page, order, filter)
paginator.onListReceived(offset, page, list.size)
return list
}
}

@ -0,0 +1,24 @@
package org.koitharu.kotatsu.parsers.core
import org.koitharu.kotatsu.parsers.InternalParsersApi
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaListFilter
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.SortOrder
@InternalParsersApi
public abstract class SinglePageMangaParser(
context: MangaLoaderContext,
source: MangaParserSource,
) : AbstractMangaParser(context, source) {
final override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
if (offset > 0) {
return emptyList()
}
return getList(order, filter)
}
public abstract suspend fun getList(order: SortOrder, filter: MangaListFilter): List<Manga>
}

@ -1,11 +1,13 @@
package org.koitharu.kotatsu.parsers.exception
import okio.IOException
import org.koitharu.kotatsu.parsers.InternalParsersApi
import org.koitharu.kotatsu.parsers.model.MangaSource
/**
* Authorization is required for access to the requested content
*/
class AuthRequiredException @InternalParsersApi constructor(
val source: MangaSource,
) : RuntimeException("Authorization required")
public class AuthRequiredException @InternalParsersApi @JvmOverloads constructor(
public val source: MangaSource,
cause: Throwable? = null,
) : IOException("Authorization required", cause)

@ -1,7 +0,0 @@
package org.koitharu.kotatsu.parsers.exception
import okio.IOException
class CloudFlareProtectedException(
val url: String,
) : IOException("Protected by CloudFlare: $url")

@ -1,3 +1,3 @@
package org.koitharu.kotatsu.parsers.exception
class ContentUnavailableException(message: String) : RuntimeException(message)
public class ContentUnavailableException(message: String) : RuntimeException(message)

@ -2,14 +2,15 @@ package org.koitharu.kotatsu.parsers.exception
import okio.IOException
import org.json.JSONArray
import org.koitharu.kotatsu.parsers.util.json.mapJSON
import org.koitharu.kotatsu.parsers.InternalParsersApi
import org.koitharu.kotatsu.parsers.util.json.mapJSONNotNull
class GraphQLException(private val errors: JSONArray) : IOException() {
public class GraphQLException @InternalParsersApi constructor(errors: JSONArray) : IOException() {
val messages = errors.mapJSON {
public val messages: List<String> = errors.mapJSONNotNull {
it.getString("message")
}
override val message: String
get() = messages.joinToString("\n")
}
}

@ -3,7 +3,7 @@ package org.koitharu.kotatsu.parsers.exception
import org.jsoup.HttpStatusException
import java.net.HttpURLConnection
class NotFoundException(
public class NotFoundException(
message: String,
url: String,
) : HttpStatusException(message, HttpURLConnection.HTTP_NOT_FOUND, url)
) : HttpStatusException(message, HttpURLConnection.HTTP_NOT_FOUND, url)

@ -2,8 +2,8 @@ package org.koitharu.kotatsu.parsers.exception
import org.koitharu.kotatsu.parsers.InternalParsersApi
class ParseException @InternalParsersApi @JvmOverloads constructor(
val shortMessage: String?,
val url: String,
public class ParseException @InternalParsersApi @JvmOverloads constructor(
public val shortMessage: String?,
public val url: String,
cause: Throwable? = null,
) : RuntimeException("$shortMessage at $url", cause)
) : RuntimeException("$shortMessage at $url", cause)

@ -0,0 +1,31 @@
package org.koitharu.kotatsu.parsers.exception
import okio.IOException
import java.time.Instant
import java.time.temporal.ChronoUnit
public class TooManyRequestExceptions(
public val url: String,
retryAfter: Long,
) : IOException("Too man requests") {
public val retryAt: Instant? = if (retryAfter > 0 && retryAfter < Long.MAX_VALUE) {
Instant.now().plusMillis(retryAfter)
} else {
null
}
public fun getRetryDelay(): Long {
if (retryAt == null) {
return -1L
}
return Instant.now().until(retryAt, ChronoUnit.MILLIS).coerceAtLeast(0L)
}
override val message: String?
get() = if (retryAt != null) {
"${super.message}, retry at $retryAt"
} else {
super.message
}
}

@ -2,4 +2,10 @@
package org.koitharu.kotatsu.parsers.model
const val RATING_UNKNOWN = -1f
public const val RATING_UNKNOWN: Float = -1f
public const val YEAR_UNKNOWN: Int = 0
public const val YEAR_MIN: Int = 1900
public const val YEAR_MAX: Int = 2099

@ -0,0 +1,7 @@
package org.koitharu.kotatsu.parsers.model
public enum class ContentRating {
SAFE,
SUGGESTIVE,
ADULT
}

@ -0,0 +1,36 @@
package org.koitharu.kotatsu.parsers.model
public enum class ContentType {
/**
* Standard manga, manhua, webtoons, etc
*/
MANGA,
MANHWA,
MANHUA,
/**
* Use this if the source provides mostly nsfw content.
*/
HENTAI,
/**
* Western comics
*/
COMICS,
NOVEL,
/**
* Use this type if no other suits your needs. For example, for an indie manga
*/
ONE_SHOT,
DOUJINSHI,
IMAGE_SET,
ARTIST_CG,
GAME_CG,
OTHER,
}

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.parsers.model
public enum class Demographic {
SHOUNEN,
SHOUJO,
SEINEN,
JOSEI,
KODOMO,
NONE,
}

@ -2,13 +2,14 @@ package org.koitharu.kotatsu.parsers.model
import okhttp3.HttpUrl.Companion.toHttpUrl
class Favicon internal constructor(
val url: String,
val size: Int,
internal val rel: String?,
public data class Favicon(
@JvmField public val url: String,
@JvmField public val size: Int,
@JvmField internal val rel: String?,
) : Comparable<Favicon> {
val type: String = url.toHttpUrl().pathSegments.last()
@JvmField
public val type: String = url.toHttpUrl().pathSegments.last()
.substringAfterLast('.', "").lowercase()
override fun compareTo(other: Favicon): Int {
@ -19,33 +20,9 @@ class Favicon internal constructor(
return relWeightOf(rel).compareTo(relWeightOf(other.rel))
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Favicon
if (url != other.url) return false
if (size != other.size) return false
if (rel != other.rel) return false
return true
}
override fun hashCode(): Int {
var result = url.hashCode()
result = 31 * result + size
result = 31 * result + rel.hashCode()
return result
}
override fun toString(): String {
return "Favicon(size=$size, type='$type', rel='$rel', url='$url')"
}
private fun relWeightOf(rel: String?) = when (rel) {
"apple-touch-icon" -> 1 // Prefer apple-touch-icon because it has a better quality
"mask-icon" -> -1
else -> 0
}
}
}

@ -1,8 +1,8 @@
package org.koitharu.kotatsu.parsers.model
class Favicons internal constructor(
public class Favicons(
favicons: Collection<Favicon>,
val referer: String,
@JvmField public val referer: String?,
) : Collection<Favicon> {
private val icons = favicons.sortedDescending()
@ -18,6 +18,11 @@ class Favicons internal constructor(
override fun iterator(): Iterator<Favicon> = icons.iterator()
public operator fun minus(victim: Favicon): Favicons = Favicons(
favicons = icons.filterNot { it == victim },
referer = referer,
)
/**
* Finds a favicon whose size in pixels is greater than or equal to the specified size.
* If such icon is not available returns the largest icon
@ -25,7 +30,7 @@ class Favicons internal constructor(
* @param types supported file types, e.g. png, svg, ico. May be null but not empty
*/
@JvmOverloads
fun find(size: Int, types: Set<String>? = null): Favicon? {
public fun find(size: Int, types: Set<String>? = null): Favicon? {
if (icons.isEmpty()) {
return null
}
@ -42,4 +47,13 @@ class Favicons internal constructor(
}
return result
}
}
public companion object {
@JvmStatic
public val EMPTY: Favicons = Favicons(emptySet(), null)
@JvmStatic
public fun single(url: String): Favicons = Favicons(setOf(Favicon(url, 0, null)), null)
}
}

@ -1,158 +1,203 @@
package org.koitharu.kotatsu.parsers.model
import org.koitharu.kotatsu.parsers.InternalParsersApi
import androidx.collection.ArrayMap
import org.koitharu.kotatsu.parsers.util.findById
import org.koitharu.kotatsu.parsers.util.nullIfEmpty
class Manga(
public data class Manga(
/**
* Unique identifier for manga
*/
val id: Long,
@JvmField public val id: Long,
/**
* Manga title, human-readable
*/
val title: String,
@JvmField public val title: String,
/**
* Alternative title (for example on other language), may be null
* Alternative titles (for example on other language), may be empty
*/
val altTitle: String?,
@JvmField public val altTitles: Set<String>,
/**
* Relative url to manga (**without** a domain) or any other uri.
* Used principally in parsers
*/
val url: String,
@JvmField public val url: String,
/**
* Absolute url to manga, must be ready to open in browser
*/
val publicUrl: String,
@JvmField public val publicUrl: String,
/**
* Normalized manga rating, must be in range of 0..1 or [RATING_UNKNOWN] if rating s unknown
* @see hasRating
*/
val rating: Float,
@JvmField public val rating: Float,
/**
* Indicates that manga may contain sensitive information (18+, NSFW)
*/
val isNsfw: Boolean,
@JvmField public val contentRating: ContentRating?,
/**
* Absolute link to the cover
* @see largeCoverUrl
*/
val coverUrl: String,
@JvmField public val coverUrl: String?,
/**
* Tags (genres) of the manga
*/
val tags: Set<MangaTag>,
@JvmField public val tags: Set<MangaTag>,
/**
* Manga status (ongoing, finished) or null if unknown
*/
val state: MangaState?,
@JvmField public val state: MangaState?,
/**
* Author of the manga, may be null
* Authors of the manga
*/
val author: String?,
@JvmField public val authors: Set<String>,
/**
* Large cover url (absolute), null if is no large cover
* @see coverUrl
*/
val largeCoverUrl: String? = null,
@JvmField public val largeCoverUrl: String? = null,
/**
* Manga description, may be html or null
*/
val description: String? = null,
@JvmField public val description: String? = null,
/**
* List of chapters
*/
val chapters: List<MangaChapter>? = null,
@JvmField public val chapters: List<MangaChapter>? = null,
/**
* Manga source
*/
val source: MangaSource,
@JvmField public val source: MangaSource,
) {
/**
* Return if manga has a specified rating
* @see rating
*/
val hasRating: Boolean
get() = rating > 0f && rating <= 1f
fun getChapters(branch: String?): List<MangaChapter>? {
return chapters?.filter { x -> x.branch == branch }
}
@InternalParsersApi
fun copy(
title: String = this.title,
altTitle: String? = this.altTitle,
publicUrl: String = this.publicUrl,
rating: Float = this.rating,
isNsfw: Boolean = this.isNsfw,
coverUrl: String = this.coverUrl,
tags: Set<MangaTag> = this.tags,
state: MangaState? = this.state,
author: String? = this.author,
largeCoverUrl: String? = this.largeCoverUrl,
description: String? = this.description,
chapters: List<MangaChapter>? = this.chapters,
) = Manga(
@Deprecated("Use other constructor")
public constructor(
/**
* Unique identifier for manga
*/
id: Long,
/**
* Manga title, human-readable
*/
title: String,
/**
* Alternative title (for example on other language), may be null
*/
altTitle: String?,
/**
* Relative url to manga (**without** a domain) or any other uri.
* Used principally in parsers
*/
url: String,
/**
* Absolute url to manga, must be ready to open in browser
*/
publicUrl: String,
/**
* Normalized manga rating, must be in range of 0..1 or [RATING_UNKNOWN] if rating s unknown
* @see hasRating
*/
rating: Float,
/**
* Indicates that manga may contain sensitive information (18+, NSFW)
*/
isNsfw: Boolean,
/**
* Absolute link to the cover
* @see largeCoverUrl
*/
coverUrl: String?,
/**
* Tags (genres) of the manga
*/
tags: Set<MangaTag>,
/**
* Manga status (ongoing, finished) or null if unknown
*/
state: MangaState?,
/**
* Authors of the manga
*/
author: String?,
/**
* Large cover url (absolute), null if is no large cover
* @see coverUrl
*/
largeCoverUrl: String? = null,
/**
* Manga description, may be html or null
*/
description: String? = null,
/**
* List of chapters
*/
chapters: List<MangaChapter>? = null,
/**
* Manga source
*/
source: MangaSource,
) : this(
id = id,
title = title,
altTitle = altTitle,
altTitles = setOfNotNull(altTitle?.nullIfEmpty()),
url = url,
publicUrl = publicUrl,
rating = rating,
isNsfw = isNsfw,
coverUrl = coverUrl,
contentRating = if (isNsfw) ContentRating.ADULT else null,
coverUrl = coverUrl?.nullIfEmpty(),
tags = tags,
state = state,
author = author,
largeCoverUrl = largeCoverUrl,
description = description,
authors = setOfNotNull(author),
largeCoverUrl = largeCoverUrl?.nullIfEmpty(),
description = description?.nullIfEmpty(),
chapters = chapters,
source = source,
)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
/**
* Author of the manga, may be null
*/
@Deprecated("Please use authors")
public val author: String?
get() = authors.firstOrNull()
/**
* Alternative title (for example on other language), may be null
*/
@Deprecated("Please use altTitles")
public val altTitle: String?
get() = altTitles.firstOrNull()
other as Manga
/**
* Return if manga has a specified rating
* @see rating
*/
public val hasRating: Boolean
get() = rating > 0f && rating <= 1f
if (id != other.id) return false
if (title != other.title) return false
if (altTitle != other.altTitle) return false
if (url != other.url) return false
if (publicUrl != other.publicUrl) return false
if (rating != other.rating) return false
if (isNsfw != other.isNsfw) return false
if (coverUrl != other.coverUrl) return false
if (tags != other.tags) return false
if (state != other.state) return false
if (author != other.author) return false
if (largeCoverUrl != other.largeCoverUrl) return false
if (description != other.description) return false
if (chapters != other.chapters) return false
if (source != other.source) return false
@Deprecated("Use contentRating instead", ReplaceWith("contentRating == ContentRating.ADULT"))
public val isNsfw: Boolean
get() = contentRating == ContentRating.ADULT
return true
public fun getChapters(branch: String?): List<MangaChapter> {
return chapters?.filter { x -> x.branch == branch }.orEmpty()
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + title.hashCode()
result = 31 * result + (altTitle?.hashCode() ?: 0)
result = 31 * result + url.hashCode()
result = 31 * result + publicUrl.hashCode()
result = 31 * result + rating.hashCode()
result = 31 * result + isNsfw.hashCode()
result = 31 * result + coverUrl.hashCode()
result = 31 * result + tags.hashCode()
result = 31 * result + (state?.hashCode() ?: 0)
result = 31 * result + (author?.hashCode() ?: 0)
result = 31 * result + (largeCoverUrl?.hashCode() ?: 0)
result = 31 * result + (description?.hashCode() ?: 0)
result = 31 * result + (chapters?.hashCode() ?: 0)
result = 31 * result + source.hashCode()
public fun findChapterById(id: Long): MangaChapter? = chapters?.findById(id)
public fun requireChapterById(id: Long): MangaChapter = findChapterById(id)
?: throw NoSuchElementException("Chapter with id $id not found")
public fun getBranches(): Map<String?, Int> {
if (chapters.isNullOrEmpty()) {
return emptyMap()
}
val result = ArrayMap<String?, Int>()
chapters.forEach {
val key = it.branch
result[key] = result.getOrDefault(key, 0) + 1
}
return result
}
}
}

@ -1,70 +1,65 @@
package org.koitharu.kotatsu.parsers.model
class MangaChapter(
import org.koitharu.kotatsu.parsers.util.formatSimple
import org.koitharu.kotatsu.parsers.util.ifNullOrEmpty
public data class MangaChapter(
/**
* An unique id of chapter
*/
val id: Long,
@JvmField public val id: Long,
/**
* User-readable name of chapter if provided by parser or null instead
* Do not pass manga title or chapter number here
*/
@JvmField public val title: String?,
/**
* User-readable name of chapter
* Chapter number starting from 1, 0 if unknown
*/
val name: String,
@JvmField public val number: Float,
/**
* Chapter number starting from 1
* Volume number starting from 1, 0 if unknown
*/
val number: Int,
@JvmField public val volume: Int,
/**
* Relative url to chapter (**without** a domain) or any other uri.
* Used principally in parsers
*/
val url: String,
@JvmField public val url: String,
/**
* User-readable name of scanlator (releaser) or null if unknown
*/
val scanlator: String?,
@JvmField public val scanlator: String?,
/**
* Chapter upload date in milliseconds
*/
val uploadDate: Long,
@JvmField public val uploadDate: Long,
/**
* User-readable name of branch.
* A branch is a group of chapters that overlap (e.g. different languages)
*/
val branch: String?,
val source: MangaSource,
) : Comparable<MangaChapter> {
override fun compareTo(other: MangaChapter): Int {
return number.compareTo(other.number)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MangaChapter
@JvmField public val branch: String?,
@JvmField public val source: MangaSource,
) {
if (id != other.id) return false
if (name != other.name) return false
if (number != other.number) return false
if (url != other.url) return false
if (scanlator != other.scanlator) return false
if (uploadDate != other.uploadDate) return false
if (branch != other.branch) return false
if (source != other.source) return false
@Deprecated("Use title instead", ReplaceWith("title"))
val name: String
get() = title.ifNullOrEmpty {
buildString {
if (volume > 0) append("Vol ").append(volume).append(' ')
if (number > 0) append("Chapter ").append(number.formatSimple()) else append("Unnamed")
}
}
return true
public fun numberString(): String? = if (number > 0f) {
number.formatSimple()
} else {
null
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + number
result = 31 * result + url.hashCode()
result = 31 * result + (scanlator?.hashCode() ?: 0)
result = 31 * result + uploadDate.hashCode()
result = 31 * result + (branch?.hashCode() ?: 0)
result = 31 * result + source.hashCode()
return result
public fun volumeString(): String? = if (volume > 0) {
volume.toString()
} else {
null
}
}
}

@ -0,0 +1,88 @@
package org.koitharu.kotatsu.parsers.model
import java.util.*
public data class MangaListFilter(
@JvmField val query: String? = null,
@JvmField val tags: Set<MangaTag> = emptySet(),
@JvmField val tagsExclude: Set<MangaTag> = emptySet(),
@JvmField val locale: Locale? = null,
@JvmField val originalLocale: Locale? = null,
@JvmField val states: Set<MangaState> = emptySet(),
@JvmField val contentRating: Set<ContentRating> = emptySet(),
@JvmField val types: Set<ContentType> = emptySet(),
@JvmField val demographics: Set<Demographic> = emptySet(),
@JvmField val year: Int = YEAR_UNKNOWN,
@JvmField val yearFrom: Int = YEAR_UNKNOWN,
@JvmField val yearTo: Int = YEAR_UNKNOWN,
@JvmField val author: String? = null,
) {
private fun isNonSearchOptionsEmpty(): Boolean = tags.isEmpty() &&
tagsExclude.isEmpty() &&
locale == null &&
originalLocale == null &&
states.isEmpty() &&
contentRating.isEmpty() &&
year == YEAR_UNKNOWN &&
yearFrom == YEAR_UNKNOWN &&
yearTo == YEAR_UNKNOWN &&
types.isEmpty() &&
demographics.isEmpty() &&
author.isNullOrEmpty()
public fun isEmpty(): Boolean = isNonSearchOptionsEmpty() && query.isNullOrEmpty()
public fun isNotEmpty(): Boolean = !isEmpty()
public fun hasNonSearchOptions(): Boolean = !isNonSearchOptionsEmpty()
public companion object {
@JvmStatic
public val EMPTY: MangaListFilter = MangaListFilter()
}
internal class Builder {
private var query: String? = null
private val tags: MutableSet<MangaTag> = mutableSetOf()
private val tagsExclude: MutableSet<MangaTag> = mutableSetOf()
private var locale: Locale? = null
private var originalLocale: Locale? = null
private val states: MutableSet<MangaState> = mutableSetOf()
private val contentRating: MutableSet<ContentRating> = mutableSetOf()
private val types: MutableSet<ContentType> = mutableSetOf()
private val demographics: MutableSet<Demographic> = mutableSetOf()
private var year: Int = YEAR_UNKNOWN
private var yearFrom: Int = YEAR_UNKNOWN
private var yearTo: Int = YEAR_UNKNOWN
fun query(query: String?): Builder = apply { this.query = query }
fun addTag(tag: MangaTag): Builder = apply { tags.add(tag) }
fun addTags(tags: Collection<MangaTag>): Builder = apply { this.tags.addAll(tags) }
fun excludeTag(tag: MangaTag): Builder = apply { tagsExclude.add(tag) }
fun excludeTags(tags: Collection<MangaTag>): Builder = apply { this.tagsExclude.addAll(tags) }
fun locale(locale: Locale?): Builder = apply { this.locale = locale }
fun originalLocale(locale: Locale?): Builder = apply { this.originalLocale = locale }
fun addState(state: MangaState): Builder = apply { states.add(state) }
fun addStates(states: Collection<MangaState>): Builder = apply { this.states.addAll(states) }
fun addContentRating(rating: ContentRating): Builder = apply { contentRating.add(rating) }
fun addContentRatings(ratings: Collection<ContentRating>): Builder =
apply { this.contentRating.addAll(ratings) }
fun addType(type: ContentType): Builder = apply { types.add(type) }
fun addTypes(types: Collection<ContentType>): Builder = apply { this.types.addAll(types) }
fun addDemographic(demographic: Demographic): Builder = apply { demographics.add(demographic) }
fun addDemographics(demographics: Collection<Demographic>): Builder =
apply { this.demographics.addAll(demographics) }
fun year(year: Int): Builder = apply { this.year = year }
fun yearFrom(year: Int): Builder = apply { this.yearFrom = year }
fun yearTo(year: Int): Builder = apply { this.yearTo = year }
fun build(): MangaListFilter = MangaListFilter(
query, tags, tagsExclude, locale, originalLocale, states,
contentRating, types, demographics, year, yearFrom, yearTo,
)
}
}

@ -0,0 +1,56 @@
package org.koitharu.kotatsu.parsers.model
import org.koitharu.kotatsu.parsers.InternalParsersApi
public data class MangaListFilterCapabilities @InternalParsersApi constructor(
/**
* Whether parser supports filtering by more than one tag
* @see [MangaListFilter.tags]
* @see [MangaListFilterOptions.availableTags]
*/
val isMultipleTagsSupported: Boolean = false,
/**
* Whether parser supports tagsExclude field in filter
* @see [MangaListFilter.tagsExclude]
* @see [MangaListFilterOptions.availableTags]
*/
val isTagsExclusionSupported: Boolean = false,
/**
* Whether parser supports searching by string query
* @see [MangaListFilter.query]
*/
val isSearchSupported: Boolean = false,
/**
* Whether parser supports searching by string query combined within other filters
*/
val isSearchWithFiltersSupported: Boolean = false,
/**
* Whether parser supports searching/filtering by year
* @see [MangaListFilter.year]
*/
val isYearSupported: Boolean = false,
/**
* Whether parser supports searching by year range
* @see [MangaListFilter.yearFrom] and [MangaListFilter.yearTo]
*/
val isYearRangeSupported: Boolean = false,
/**
* Whether parser supports searching Original Languages
* @see [MangaListFilter.originalLocale]
* @see [MangaListFilterOptions.availableLocales]
*/
val isOriginalLocaleSupported: Boolean = false,
/**
* Whether parser supports searching by author name
* @see [MangaListFilter.author]
*/
val isAuthorSearchSupported: Boolean = false,
)

@ -0,0 +1,45 @@
package org.koitharu.kotatsu.parsers.model
import org.koitharu.kotatsu.parsers.InternalParsersApi
import java.util.*
public data class MangaListFilterOptions @InternalParsersApi constructor(
/**
* Available tags (genres)
*/
public val availableTags: Set<MangaTag> = emptySet(),
/**
* Supported [MangaState] variants for filtering. May be empty.
*
* For better performance use [EnumSet] for more than one item.
*/
public val availableStates: Set<MangaState> = emptySet(),
/**
* Supported [ContentRating] variants for filtering. May be empty.
*
* For better performance use [EnumSet] for more than one item.
*/
public val availableContentRating: Set<ContentRating> = emptySet(),
/**
* Supported [ContentType] variants for filtering. May be empty.
*
* For better performance use [EnumSet] for more than one item.
*/
public val availableContentTypes: Set<ContentType> = emptySet(),
/**
* Supported [Demographic] variants for filtering. May be empty.
*
* For better performance use [EnumSet] for more than one item.
*/
public val availableDemographics: Set<Demographic> = emptySet(),
/**
* Supported content locales for multilingual sources
*/
public val availableLocales: Set<Locale> = emptySet(),
)

@ -2,51 +2,21 @@ package org.koitharu.kotatsu.parsers.model
import org.koitharu.kotatsu.parsers.MangaParser
class MangaPage(
public data class MangaPage(
/**
* Unique identifier for manga
* Unique identifier for page
*/
val id: Long,
@JvmField public val id: Long,
/**
* Relative url to page (**without** a domain) or any other uri.
* Used principally in parsers.
* May contain link to image or html page.
* @see MangaParser.getPageUrl
*/
val url: String,
/**
* Absolute link to the chapter or website home page.
* Used in Referer header
*/
val referer: String,
@JvmField public val url: String,
/**
* Absolute url of the small page image if exists, null otherwise
*/
val preview: String?,
val source: MangaSource,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MangaPage
if (id != other.id) return false
if (url != other.url) return false
if (referer != other.referer) return false
if (preview != other.preview) return false
if (source != other.source) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + url.hashCode()
result = 31 * result + referer.hashCode()
result = 31 * result + (preview?.hashCode() ?: 0)
result = 31 * result + source.hashCode()
return result
}
}
@JvmField public val preview: String?,
@JvmField public val source: MangaSource,
)

@ -0,0 +1,6 @@
package org.koitharu.kotatsu.parsers.model
public interface MangaSource {
public val name: String
}

@ -1,5 +1,5 @@
package org.koitharu.kotatsu.parsers.model
enum class MangaState {
ONGOING, FINISHED
}
public enum class MangaState {
ONGOING, FINISHED, ABANDONED, PAUSED, UPCOMING, RESTRICTED
}

@ -2,36 +2,15 @@ package org.koitharu.kotatsu.parsers.model
import org.koitharu.kotatsu.parsers.MangaParser
class MangaTag(
public data class MangaTag(
/**
* User-readable tag title, should be in Title case
*/
val title: String,
@JvmField public val title: String,
/**
* Identifier of a tag, must be unique among the source.
* @see MangaParser.getList
*/
val key: String,
val source: MangaSource,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MangaTag
if (title != other.title) return false
if (key != other.key) return false
if (source != other.source) return false
return true
}
override fun hashCode(): Int {
var result = title.hashCode()
result = 31 * result + key.hashCode()
result = 31 * result + source.hashCode()
return result
}
}
@JvmField public val key: String,
@JvmField public val source: MangaSource,
)

@ -1,9 +1,22 @@
package org.koitharu.kotatsu.parsers.model
enum class SortOrder {
public enum class SortOrder {
UPDATED,
UPDATED_ASC,
POPULARITY,
POPULARITY_ASC,
RATING,
RATING_ASC,
NEWEST,
ALPHABETICAL
}
NEWEST_ASC,
ALPHABETICAL,
ALPHABETICAL_DESC,
ADDED,
ADDED_ASC,
RELEVANCE,
POPULARITY_HOUR,
POPULARITY_TODAY,
POPULARITY_WEEK,
POPULARITY_MONTH,
POPULARITY_YEAR,
}

@ -3,9 +3,9 @@ package org.koitharu.kotatsu.parsers.model
import org.koitharu.kotatsu.parsers.InternalParsersApi
@InternalParsersApi
class WordSet(private vararg val words: String) {
public class WordSet(private vararg val words: String) {
fun anyWordIn(dateString: String): Boolean = words.any {
dateString.contains(it, ignoreCase = true)
}
}
public fun anyWordIn(dateString: String): Boolean = words.any { dateString.contains(it, ignoreCase = true) }
public fun startsWith(dateString: String): Boolean = words.any { dateString.startsWith(it, ignoreCase = true) }
public fun endsWith(dateString: String): Boolean = words.any { dateString.endsWith(it, ignoreCase = true) }
}

@ -0,0 +1,90 @@
package org.koitharu.kotatsu.parsers.model.search
import androidx.collection.ArrayMap
import androidx.collection.ArraySet
import org.koitharu.kotatsu.parsers.model.SortOrder
/**
* Represents a search query for filtering and sorting manga search results.
* This class is immutable and must be constructed using the [Builder].
*
* @property criteria The set of search criteria applied to the query.
* @property order The sorting order for the results (optional).
* @property offset The offset number for paginated search results (optional).
*/
@Deprecated("Too complex. Use MangaListFilter instead")
@ConsistentCopyVisibility
public data class MangaSearchQuery private constructor(
@JvmField public val criteria: Set<QueryCriteria<*>>,
@JvmField public val order: SortOrder?,
@JvmField public val offset: Int,
@JvmField public val skipValidation: Boolean,
) {
public fun newBuilder(): Builder = Builder(this)
public class Builder {
private val criteria = ArraySet<QueryCriteria<*>>()
private var order: SortOrder? = null
private var offset: Int = 0
private var skipValidation: Boolean = false
public constructor()
public constructor(query: MangaSearchQuery) : this() {
criteria.addAll(query.criteria)
order = query.order
offset = query.offset
}
public fun criterion(criterion: QueryCriteria<*>): Builder = apply { criteria.add(criterion) }
public fun order(order: SortOrder?): Builder = apply { this.order = order }
public fun offset(offset: Int): Builder = apply { this.offset = offset }
public fun skipValidation(skip: Boolean): Builder = apply { this.skipValidation = skip }
@Throws(IllegalArgumentException::class)
public fun build(): MangaSearchQuery {
return MangaSearchQuery(deduplicateCriteria(criteria), order, offset, skipValidation)
}
private fun deduplicateCriteria(criteria: Set<QueryCriteria<*>>): Set<QueryCriteria<*>> {
val uniqueCriteria =
ArrayMap<Pair<SearchableField, Class<out QueryCriteria<*>>>, QueryCriteria<*>>(criteria.size)
for (criterion in criteria) {
val key = criterion.field to criterion::class.java
val existing = uniqueCriteria[key]
when {
existing == null -> uniqueCriteria[key] = criterion
existing is QueryCriteria.Include<*> && criterion is QueryCriteria.Include<*> -> {
uniqueCriteria[key] =
QueryCriteria.Include(criterion.field, existing.values union criterion.values)
}
existing is QueryCriteria.Exclude<*> && criterion is QueryCriteria.Exclude<*> -> {
uniqueCriteria[key] =
QueryCriteria.Exclude(criterion.field, existing.values union criterion.values)
}
else -> throw IllegalArgumentException(
"Match and Range have only one criterion per type, but found duplicates for: ${criterion.field} in ${criterion::class.simpleName}",
)
}
}
return uniqueCriteria.values.toSet()
}
}
public companion object {
public val EMPTY: MangaSearchQuery = MangaSearchQuery(emptySet(), null, 0, false)
}
}

@ -0,0 +1,48 @@
package org.koitharu.kotatsu.parsers.model.search
import androidx.collection.ArraySet
import org.koitharu.kotatsu.parsers.model.search.QueryCriteria.*
import org.koitharu.kotatsu.parsers.util.mapToSet
@Deprecated("Too complex. Use MangaListFilterCapabilities instead")
@ExposedCopyVisibility
public data class MangaSearchQueryCapabilities internal constructor(
public val capabilities: Set<SearchCapability>,
) {
public constructor(vararg capabilities: SearchCapability) : this(ArraySet(capabilities))
internal fun validate(query: MangaSearchQuery) {
val strictFields = capabilities.filter { it.isExclusive }.mapToSet { it.field }
val usedStrictFields = query.criteria.mapToSet { it.field }.intersect(strictFields)
require(usedStrictFields.isEmpty() || query.criteria.size <= 1) {
"Query contains multiple criteria, but at least one field (${usedStrictFields.joinToString()}) does not support multiple criteria."
}
for (criterion in query.criteria) {
val capability = requireNotNull(capabilities.find { it.field == criterion.field }) {
"Unsupported search field: ${criterion.field}"
}
require(criterion::class in capability.criteriaTypes) {
"Unsupported search criterion: ${criterion::class.simpleName} for field ${criterion.field}"
}
// Ensure single value per criterion if supportMultiValue is false
if (!capability.isMultiple) {
when (criterion) {
is Include<*> -> require(criterion.values.size <= 1) {
"Multiple values are not allowed for field ${criterion.field}"
}
is Exclude<*> -> require(criterion.values.size <= 1) {
"Multiple values are not allowed for field ${criterion.field}"
}
is Range<*> -> Unit // Range is always valid (from, to)
is Match<*> -> Unit // Match always has a single value
}
}
}
}
}

@ -0,0 +1,106 @@
package org.koitharu.kotatsu.parsers.model.search
/**
* Represents a generic search criterion used for filtering manga search results.
* Each criterion applies a specific condition to a [SearchableField] and operates on values of type [T].
*
* @param T The type of value associated with the search criterion.
* @property field The field to which this search criterion applies.
*/
@Deprecated("Too complex")
public sealed interface QueryCriteria<T> {
public val field: SearchableField
override fun equals(other: Any?): Boolean
override fun hashCode(): Int
/**
* Represents an inclusion criterion that allows search results based on a set of allowed values.
*
* @param T The type of value being included in the search.
* @property values The set of values that should be included in the search results.
*
* ### Example Usage:
* ```kotlin
* val genreFilter = QueryCriteria.Include(SearchableField.STATE, setOf(MangaState.ONGOING, MangaState.FINISHED))
* ```
*/
public data class Include<T : Any>(
public override val field: SearchableField,
@JvmField public val values: Set<T>,
) : QueryCriteria<T> {
init {
check(values.all { x -> field.type.isInstance(x) })
}
}
/**
* Represents an exclusion criterion that exclude results containing certain values.
*
* @param T The type of value being excluded from the search.
* @property values The set of values that should be excluded from the search results.
*
* ### Example Usage:
* ```kotlin
* val excludeTag = QueryCriteria.Exclude(SearchableField.TAG, setOf(MangaTag(key, title, source)))
* ```
*/
public data class Exclude<T : Any>(
public override val field: SearchableField,
@JvmField public val values: Set<T>,
) : QueryCriteria<T> {
init {
check(values.all { x -> field.type.isInstance(x) })
}
}
/**
* Represents a range criterion that allows search based on a range of values.
*
* @param T The type of value used in the range (must be comparable).
* @property from The starting value of the range (inclusive).
* @property to The ending value of the range (inclusive).
*
* ### Example Usage:
* ```kotlin
* val yearRange = QueryCriteria.Range(SearchableField.PUBLICATION_YEAR, 2000, 2020)
* ```
*/
public data class Range<T : Comparable<T>>(
public override val field: SearchableField,
@JvmField public val from: T,
@JvmField public val to: T,
) : QueryCriteria<T> {
init {
check(field.type.isInstance(from))
check(field.type.isInstance(to))
}
}
/**
* Represents a match criterion that search results based on an exact match of a value.
*
* @param T The type of value being matched.
* @property value The exact value that must be matched.
*
* ### Example Usage:
* ```kotlin
* val titleMatch = QueryCriteria.Match(SearchableField.TITLE, "manga title")
* ```
*/
public data class Match<T : Any>(
public override val field: SearchableField,
@JvmField public val value: T,
) : QueryCriteria<T> {
init {
check(field.type.isInstance(value))
}
}
}

@ -0,0 +1,34 @@
package org.koitharu.kotatsu.parsers.model.search
import kotlin.reflect.KClass
/**
* Defines the search capabilities of a given field in the manga search query.
*
* @property field The searchable field that this capability applies to.
* Example values:
* - `SearchableField.TITLE_NAME` for searching by title.
* - `SearchableField.AUTHOR` for searching by author names.
* - `SearchableField.TAG` for filtering by tags.
* @property criteriaTypes The set of supported criteria types for the field.
* Example values:
* - `setOf(Include::class, Exclude::class)` selected field supports inclusion/exclusion criteria.
* - `setOf(Range::class)` selected field support numerical range criteria.
* @property isMultiValue Indicates whether the field supports multiple values.
* - `true` if multiple values can be provided (e.g., multiple tags or authors).
* - `false` if only a single value is allowed (e.g., only one tag or author).
* @property isExclusive Specifies whether the field can be used alongside other criteria.
* - `true` if this field can be used with other search criteria.
* - `false` if using this field requires it to be the only criterion in query.
*/
@Deprecated("Too complex")
public data class SearchCapability(
/** The searchable field that this capability applies to. */
@JvmField public val field: SearchableField,
/** The set of supported criteria types for this field. */
@JvmField public val criteriaTypes: Set<KClass<out QueryCriteria<*>>>,
/** Indicates whether the field supports multiple values. */
@JvmField public val isMultiple: Boolean,
/** Specifies whether the field can be used alongside other criteria. */
@JvmField public val isExclusive: Boolean = false,
)

@ -0,0 +1,24 @@
package org.koitharu.kotatsu.parsers.model.search
import org.koitharu.kotatsu.parsers.model.*
import java.util.*
/**
* Represents the various fields that can be used for searching manga.
* Each field is associated with a specific data type that defines its expected values.
*
* @property type The Java class representing the expected type of values for this field.
*/
@Deprecated("Too complex")
public enum class SearchableField(public val type: Class<*>) {
TITLE_NAME(String::class.java),
TAG(MangaTag::class.java),
AUTHOR(MangaTag::class.java),
LANGUAGE(Locale::class.java),
ORIGINAL_LANGUAGE(Locale::class.java),
STATE(MangaState::class.java),
CONTENT_TYPE(ContentType::class.java),
CONTENT_RATING(ContentRating::class.java),
DEMOGRAPHIC(Demographic::class.java),
PUBLICATION_YEAR(Int::class.javaObjectType);
}

@ -0,0 +1,47 @@
package org.koitharu.kotatsu.parsers.network
import okhttp3.CookieJar
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Response
import org.jsoup.Jsoup
import java.net.HttpURLConnection.HTTP_FORBIDDEN
import java.net.HttpURLConnection.HTTP_UNAVAILABLE
public object CloudFlareHelper {
public const val PROTECTION_NOT_DETECTED: Int = 0
public const val PROTECTION_CAPTCHA: Int = 1
public const val PROTECTION_BLOCKED: Int = 2
private const val CF_CLEARANCE = "cf_clearance"
public fun checkResponseForProtection(response: Response): Int {
if (response.code != HTTP_FORBIDDEN && response.code != HTTP_UNAVAILABLE) {
return PROTECTION_NOT_DETECTED
}
val content = try {
response.peekBody(Long.MAX_VALUE).use {
Jsoup.parse(it.byteStream(), Charsets.UTF_8.name(), response.request.url.toString())
}
} catch (_: IllegalStateException) {
return PROTECTION_NOT_DETECTED
}
return when {
content.selectFirst("h2[data-translate=\"blocked_why_headline\"]") != null -> PROTECTION_BLOCKED
content.getElementById("challenge-error-title") != null || content.getElementById("challenge-error-text") != null -> PROTECTION_CAPTCHA
else -> PROTECTION_NOT_DETECTED
}
}
public fun getClearanceCookie(cookieJar: CookieJar, url: String): String? {
return cookieJar.loadForRequest(url.toHttpUrl()).find { it.name == CF_CLEARANCE }?.value
}
public fun isCloudFlareCookie(name: String): Boolean {
return name.startsWith("cf_")
|| name.startsWith("_cf")
|| name.startsWith("__cf")
|| name == "csrftoken"
}
}

@ -0,0 +1,133 @@
package org.koitharu.kotatsu.parsers.network
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
import org.jsoup.HttpStatusException
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.exception.GraphQLException
import org.koitharu.kotatsu.parsers.exception.NotFoundException
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.await
import org.koitharu.kotatsu.parsers.util.parseJson
import java.net.HttpURLConnection
public class OkHttpWebClient(
private val httpClient: OkHttpClient,
private val mangaSource: MangaSource,
) : WebClient {
override suspend fun httpGet(url: HttpUrl, extraHeaders: Headers?): Response {
val request = Request.Builder()
.get()
.url(url)
.addTags()
.addExtraHeaders(extraHeaders)
return httpClient.newCall(request.build()).await().ensureSuccess()
}
override suspend fun httpHead(url: HttpUrl): Response {
val request = Request.Builder()
.head()
.url(url)
.addTags()
return httpClient.newCall(request.build()).await().ensureSuccess()
}
override suspend fun httpPost(url: HttpUrl, form: Map<String, String>, extraHeaders: Headers?): Response {
val body = FormBody.Builder()
form.forEach { (k, v) ->
body.addEncoded(k, v)
}
val request = Request.Builder()
.post(body.build())
.url(url)
.addTags()
.addExtraHeaders(extraHeaders)
return httpClient.newCall(request.build()).await().ensureSuccess()
}
override suspend fun httpPost(url: HttpUrl, payload: String, extraHeaders: Headers?): Response {
val body = FormBody.Builder()
payload.split('&').forEach {
val pos = it.indexOf('=')
if (pos != -1) {
val k = it.substring(0, pos)
val v = it.substring(pos + 1)
body.addEncoded(k, v)
}
}
val request = Request.Builder()
.post(body.build())
.url(url)
.addTags()
.addExtraHeaders(extraHeaders)
return httpClient.newCall(request.build()).await().ensureSuccess()
}
override suspend fun httpPost(url: HttpUrl, body: JSONObject, extraHeaders: Headers?): Response {
val mediaType = "application/json; charset=utf-8".toMediaType()
val requestBody = body.toString().toRequestBody(mediaType)
val request = Request.Builder()
.post(requestBody)
.url(url)
.addTags()
.addExtraHeaders(extraHeaders)
return httpClient.newCall(request.build()).await().ensureSuccess()
}
override suspend fun graphQLQuery(endpoint: String, query: String): JSONObject {
val body = JSONObject()
body.put("operationName", null as Any?)
body.put("variables", JSONObject())
body.put("query", "{$query}")
val mediaType = "application/json; charset=utf-8".toMediaType()
val requestBody = body.toString().toRequestBody(mediaType)
val request = Request.Builder()
.post(requestBody)
.url(endpoint)
.addTags()
val json = httpClient.newCall(request.build()).await().parseJson()
json.optJSONArray("errors")?.let {
if (it.length() != 0) {
throw GraphQLException(it)
}
}
return json
}
private fun Request.Builder.addTags(): Request.Builder {
tag(MangaSource::class.java, mangaSource)
return this
}
private fun Request.Builder.addExtraHeaders(headers: Headers?): Request.Builder {
if (headers != null) {
headers(headers)
}
return this
}
private fun Response.ensureSuccess(): Response {
val exception: Exception? = when (code) { // Catch some error codes, not all
HttpURLConnection.HTTP_NOT_FOUND -> NotFoundException(message, request.url.toString())
HttpURLConnection.HTTP_UNAUTHORIZED -> request.tag(MangaSource::class.java)?.let {
AuthRequiredException(it)
} ?: HttpStatusException(message, code, request.url.toString())
in 400..599 -> HttpStatusException(message, code, request.url.toString())
else -> null
}
if (exception != null) {
runCatching {
close()
}.onFailure {
exception.addSuppressed(it)
}
throw exception
}
return this
}
}

@ -0,0 +1,17 @@
package org.koitharu.kotatsu.parsers.network
public object UserAgents {
public const val CHROME_MOBILE: String =
"Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 Mobile Safari/537.36"
public const val FIREFOX_MOBILE: String =
"Mozilla/5.0 (Android 14; Mobile; LG-M255; rv:123.0) Gecko/123.0 Firefox/123.0"
public const val CHROME_DESKTOP: String =
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
public const val FIREFOX_DESKTOP: String = "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0"
public const val KOTATSU: String = "Kotatsu/6.8 (Android 13;;; en)"
}

@ -0,0 +1,117 @@
package org.koitharu.kotatsu.parsers.network
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Response
import org.json.JSONObject
public interface WebClient {
/**
* Do a GET http request to specific url
* @param url
*/
public suspend fun httpGet(url: String): Response = httpGet(url.toHttpUrl())
public suspend fun httpGet(url: String, extraHeaders: Headers?): Response = httpGet(url.toHttpUrl(), extraHeaders)
/**
* Do a GET http request to specific url
* @param url
*/
public suspend fun httpGet(url: HttpUrl): Response = httpGet(url, null)
/**
* Do a GET http request to specific url
* @param url
* @param extraHeaders additional HTTP headers for request
*/
public suspend fun httpGet(url: HttpUrl, extraHeaders: Headers?): Response
/**
* Do a HEAD http request to specific url
* @param url
*/
public suspend fun httpHead(url: String): Response = httpHead(url.toHttpUrl())
/**
* Do a HEAD http request to specific url
* @param url
*/
public suspend fun httpHead(url: HttpUrl): Response
/**
* Do a POST http request to specific url with `multipart/form-data` payload
* @param url
* @param form payload as key=>value map
*/
public suspend fun httpPost(url: String, form: Map<String, String>): Response =
httpPost(url.toHttpUrl(), form, null)
/**
* Do a POST http request to specific url with `multipart/form-data` payload
* @param url
* @param form payload as key=>value map
*/
public suspend fun httpPost(url: HttpUrl, form: Map<String, String>): Response = httpPost(url, form, null)
/**
* Do a POST http request to specific url with `multipart/form-data` payload
* @param url
* @param form payload as key=>value map
* @param extraHeaders additional HTTP headers for request
*/
public suspend fun httpPost(url: HttpUrl, form: Map<String, String>, extraHeaders: Headers?): Response
/**
* Do a POST http request to specific url with `multipart/form-data` payload
* @param url
* @param payload payload as `key=value` string with `&` separator
*/
public suspend fun httpPost(url: String, payload: String): Response = httpPost(url.toHttpUrl(), payload, null)
/**
* Do a POST http request to specific url with `multipart/form-data` payload
* @param url
* @param payload payload as `key=value` string with `&` separator
*/
public suspend fun httpPost(url: HttpUrl, payload: String): Response = httpPost(url, payload, null)
/**
* Do a POST http request to specific url with `multipart/form-data` payload
* @param url
* @param payload payload as `key=value` string with `&` separator
* @param extraHeaders additional HTTP headers for request
*/
public suspend fun httpPost(url: HttpUrl, payload: String, extraHeaders: Headers?): Response
/**
* Do a POST http request to specific url with json payload
* @param url
* @param body
*/
public suspend fun httpPost(url: String, body: JSONObject): Response = httpPost(url.toHttpUrl(), body, null)
/**
* Do a POST http request to specific url with json payload
* @param url
* @param body
*/
public suspend fun httpPost(url: HttpUrl, body: JSONObject): Response = httpPost(url, body, null)
/**
* Do a POST http request to specific url with json payload
* @param url
* @param body
* @param extraHeaders additional HTTP headers for request
*/
public suspend fun httpPost(url: HttpUrl, body: JSONObject, extraHeaders: Headers?): Response
/**
* Do a GraphQL request to specific url
* @param endpoint an url
* @param query GraphQL request payload
*/
public suspend fun graphQLQuery(endpoint: String, query: String): JSONObject
}

@ -1,248 +0,0 @@
package org.koitharu.kotatsu.parsers.site
import androidx.collection.ArrayMap
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.json.JSONArray
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.PagedMangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.HashSet
@MangaSourceParser("BLOGTRUYEN", "BlogTruyen", "vi")
class BlogTruyenParser(override val context: MangaLoaderContext) :
PagedMangaParser(MangaSource.BLOGTRUYEN, pageSize = 20) {
override val configKeyDomain: ConfigKey.Domain
get() = ConfigKey.Domain("blogtruyen.vn", null)
override val sortOrders: Set<SortOrder>
get() = EnumSet.of(SortOrder.UPDATED)
private val mutex = Mutex()
private val dateFormat = SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.US)
private var cacheTags: ArrayMap<String, MangaTag>? = null
override suspend fun getDetails(manga: Manga): Manga {
val doc = context.httpGet(manga.url.toAbsoluteUrl(getDomain())).parseHtml()
val descriptionElement = doc.selectFirstOrThrow("div.description")
val statusText = descriptionElement
.selectFirst("p:contains(Trạng thái) > span.color-red")
?.text()
val state = when (statusText) {
"Đang tiến hành" -> MangaState.ONGOING
"Đã hoàn thành" -> MangaState.FINISHED
else -> null
}
val rating = doc.selectFirst("span.total-vote")?.attr("ng-init")?.let { text ->
val like = text.substringAfter("TotalLike=")
.substringBefore(';')
.toIntOrNull() ?: return@let RATING_UNKNOWN
val dislike = text.substringAfter("TotalDisLike=")
.toIntOrNull() ?: return@let RATING_UNKNOWN
when {
like == 0 && dislike == 0 -> RATING_UNKNOWN
else -> like.toFloat() / (like + dislike)
}
}
val tagMap = getOrCreateTagMap()
val tags = descriptionElement.select("p > span.category").mapNotNullToSet {
val tagName = it.selectFirst("a")?.text()?.trim() ?: return@mapNotNullToSet null
tagMap[tagName]
}
return manga.copy(
tags = tags,
author = descriptionElement.selectFirst("p:contains(Tác giả) > a")?.text(),
description = doc.selectFirst(".detail .content")?.html(),
chapters = parseChapterList(doc),
largeCoverUrl = doc.selectLast("div.thumbnail > img")?.attrAsAbsoluteUrlOrNull("src"),
state = state,
rating = rating ?: RATING_UNKNOWN,
isNsfw = doc.getElementById("warningCategory") != null
)
}
private fun parseChapterList(doc: Document): List<MangaChapter> {
val chapterList = doc.select("#list-chapters > p")
return chapterList.asReversed().mapChapters { index, element ->
val titleElement = element.selectFirst("span.title > a") ?: return@mapChapters null
val name = titleElement.text()
val relativeUrl = titleElement.attrAsRelativeUrl("href")
val id = relativeUrl.substringAfter('/').substringBefore('/')
val uploadDate = dateFormat.tryParse(element.select("span.publishedDate").text())
MangaChapter(
id = generateUid(id),
name = name,
number = index + 1,
url = relativeUrl,
scanlator = null,
uploadDate = uploadDate,
branch = null,
source = source
)
}
}
override suspend fun getListPage(
page: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
return when {
!query.isNullOrEmpty() -> {
val searchUrl = "https://${getDomain()}/timkiem/nangcao/1/0/-1/-1?txt=${query.urlEncoded()}&p=$page"
val searchContent = context.httpGet(searchUrl).parseHtml()
.selectFirst("section.list-manga-bycate > div.list")
parseMangaList(searchContent)
}
!tags.isNullOrEmpty() -> {
val tag = tags.oneOrThrowIfMany()!!
val categoryAjax = "https://${getDomain()}/ajax/Category/AjaxLoadMangaByCategory?id=${tag.key}&orderBy=5&p=$page"
val listContent = context.httpGet(categoryAjax).parseHtml().selectFirst("div.list")
parseMangaList(listContent)
}
else -> getNormalList(page)
}
}
private suspend fun getNormalList(page: Int): List<Manga> {
val pageLink = "https://${getDomain()}/page-$page"
val doc = context.httpGet(pageLink).parseHtml()
val listElements = doc.selectFirstOrThrow("section.list-mainpage.listview")
.select("div.bg-white.storyitem")
return listElements.mapNotNull {
val linkTag = it.selectFirst("div.fl-l > a") ?: return@mapNotNull null
val relativeUrl = linkTag.attrAsRelativeUrl("href")
val tagMap = getOrCreateTagMap()
val tags = it.select("footer > div.category > a").mapNotNullToSet { a ->
tagMap[a.text()]
}
Manga(
id = generateUid(relativeUrl),
title = linkTag.attr("title"),
altTitle = null,
description = it.selectFirst("p.al-j.break.line-height-15")?.text(),
url = relativeUrl,
publicUrl = relativeUrl.toAbsoluteUrl(getDomain()),
coverUrl = linkTag.selectLast("img")?.attr("src").orEmpty(),
source = source,
tags = tags,
isNsfw = false,
rating = RATING_UNKNOWN,
author = null,
state = null,
)
}
}
private fun parseMangaList(listElement: Element?): List<Manga> {
listElement ?: return emptyList()
return listElement.select("span.tiptip[data-tiptip]").mapNotNull {
val mangaInfo = listElement.getElementById(it.attr("data-tiptip")) ?: return@mapNotNull null
val a = it.selectFirst("a") ?: return@mapNotNull null
val relativeUrl = a.attrAsRelativeUrl("href")
Manga(
id = generateUid(relativeUrl),
title = a.text(),
altTitle = null,
description = mangaInfo.select("div.al-j.fs-12").text(),
url = relativeUrl,
publicUrl = relativeUrl.toAbsoluteUrl(getDomain()),
coverUrl = mangaInfo.selectFirst("div > img.img")?.absUrl("src").orEmpty(),
isNsfw = false,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
state = null,
source = source,
)
}
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
fun generateImageId(index: Int) = generateUid("${chapter.url}/$index")
val doc = context.httpGet(chapter.url.toAbsoluteUrl(getDomain())).parseHtml()
val pages = ArrayList<MangaPage>()
val referer = chapter.url.toAbsoluteUrl(getDomain())
doc.select("#content > img").forEach { img ->
val url = img.attrAsRelativeUrl("src")
pages.add(
MangaPage(
id = generateImageId(pages.lastIndex),
url = url,
referer = referer,
preview = null,
source = source,
)
)
}
// Some chapters use js script to render images
val script = doc.selectLast("#content > script")
if (script != null && script.data().contains("listImageCaption")) {
val imagesStr = script.data().substringBefore(';').substringAfterLast('=').trim()
val imageArr = JSONArray(imagesStr)
for (i in 0 until imageArr.length()) {
val imageUrl = imageArr.getJSONObject(i).getString("url")
pages.add(
MangaPage(
id = generateImageId(pages.lastIndex),
url = imageUrl,
referer = referer,
preview = null,
source = source
)
)
}
}
return pages
}
override suspend fun getTags(): Set<MangaTag> {
val map = getOrCreateTagMap()
val tags = HashSet<MangaTag>(map.size)
for (entry in map) {
tags.add(entry.value)
}
return tags
}
private suspend fun getOrCreateTagMap(): ArrayMap<String, MangaTag> = mutex.withLock {
cacheTags?.let { return@withLock it }
val doc = context.httpGet("/timkiem/nangcao".toAbsoluteUrl(getDomain())).parseHtml()
val tagItems = doc.select("li[data-id]")
val tagMap = ArrayMap<String, MangaTag>(tagItems.size)
for (tag in tagItems) {
val title = tag.text().trim()
tagMap[tag.text().trim()] = MangaTag(
title = title,
key = tag.attr("data-id"),
source = source
)
}
cacheTags = tagMap
tagMap
}
}

@ -1,216 +0,0 @@
package org.koitharu.kotatsu.parsers.site
import androidx.collection.ArraySet
import androidx.collection.SparseArrayCompat
import org.json.JSONArray
import org.json.JSONObject
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.*
import java.text.SimpleDateFormat
import java.util.*
/**
* https://api.comick.fun/docs/static/index.html
*/
private const val PAGE_SIZE = 20
private const val CHAPTERS_LIMIT = 99999
@MangaSourceParser("COMICK_FUN", "ComicK")
internal class ComickFunParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.COMICK_FUN) {
override val configKeyDomain = ConfigKey.Domain("comick.fun", null)
override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.POPULARITY,
SortOrder.UPDATED,
SortOrder.RATING,
)
@Volatile
private var cachedTags: SparseArrayCompat<MangaTag>? = null
override suspend fun getList(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
val domain = getDomain()
val url = buildString {
append("https://api.")
append(domain)
append("/search?tachiyomi=true")
if (!query.isNullOrEmpty()) {
if (offset > 0) {
return emptyList()
}
append("&q=")
append(query.urlEncoded())
} else {
append("&limit=")
append(PAGE_SIZE)
append("&page=")
append((offset / PAGE_SIZE) + 1)
if (!tags.isNullOrEmpty()) {
append("&genres=")
appendAll(tags, "&genres=", MangaTag::key)
}
append("&sort=") // view, uploaded, rating, follow, user_follow_count
append(
when (sortOrder) {
SortOrder.POPULARITY -> "view"
SortOrder.RATING -> "rating"
else -> "uploaded"
},
)
}
}
val ja = context.httpGet(url).parseJsonArray()
val tagsMap = cachedTags ?: loadTags()
return ja.mapJSON { jo ->
val slug = jo.getString("slug")
Manga(
id = generateUid(slug),
title = jo.getString("title"),
altTitle = null,
url = slug,
publicUrl = "https://$domain/comic/$slug",
rating = jo.getDoubleOrDefault("rating", -10.0).toFloat() / 10f,
isNsfw = false,
coverUrl = jo.getString("cover_url"),
largeCoverUrl = null,
description = jo.getStringOrNull("desc"),
tags = jo.selectGenres("genres", tagsMap),
state = runCatching {
if (jo.getBoolean("translation_completed")) {
MangaState.FINISHED
} else {
MangaState.ONGOING
}
}.getOrNull(),
author = null,
source = source,
)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val domain = getDomain()
val url = "https://api.$domain/comic/${manga.url}?tachiyomi=true"
val jo = context.httpGet(url).parseJson()
val comic = jo.getJSONObject("comic")
return manga.copy(
title = comic.getString("title"),
altTitle = null, // TODO
isNsfw = jo.getBoolean("matureContent") || comic.getBoolean("hentai"),
description = comic.getStringOrNull("parsed") ?: comic.getString("desc"),
tags = manga.tags + jo.getJSONArray("genres").mapJSONToSet {
MangaTag(
title = it.getString("name"),
key = it.getString("slug"),
source = source,
)
},
author = jo.getJSONArray("artists").optJSONObject(0)?.getString("name"),
chapters = getChapters(comic.getLong("id")),
)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val jo = context.httpGet(
"https://api.${getDomain()}/chapter/${chapter.url}?tachiyomi=true",
).parseJson().getJSONObject("chapter")
val referer = "https://${getDomain()}/"
return jo.getJSONArray("images").mapJSON {
val url = it.getString("url")
MangaPage(
id = generateUid(url),
url = url,
referer = referer,
preview = null,
source = source,
)
}
}
override suspend fun getTags(): Set<MangaTag> {
val sparseArray = cachedTags ?: loadTags()
val set = ArraySet<MangaTag>(sparseArray.size())
for (i in 0 until sparseArray.size()) {
set.add(sparseArray.valueAt(i))
}
return set
}
private suspend fun loadTags(): SparseArrayCompat<MangaTag> {
val ja = context.httpGet("https://api.${getDomain()}/genre").parseJsonArray()
val tags = SparseArrayCompat<MangaTag>(ja.length())
for (jo in ja.JSONIterator()) {
tags.append(
jo.getInt("id"),
MangaTag(
title = jo.getString("name"),
key = jo.getString("slug"),
source = source,
),
)
}
cachedTags = tags
return tags
}
private suspend fun getChapters(id: Long): List<MangaChapter> {
val ja = context.httpGet(
url = "https://api.${getDomain()}/comic/$id/chapter?tachiyomi=true&limit=$CHAPTERS_LIMIT",
).parseJson().getJSONArray("chapters")
val dateFormat = SimpleDateFormat("yyyy-MM-dd")
val counters = HashMap<Locale, Int>()
return ja.mapReversed { jo ->
val locale = Locale.forLanguageTag(jo.getString("lang"))
var number = counters[locale] ?: 0
number++
counters[locale] = number
MangaChapter(
id = generateUid(jo.getLong("id")),
name = buildString {
jo.getStringOrNull("vol")?.let { append("Vol ").append(it).append(' ') }
jo.getStringOrNull("chap")?.let { append("Chap ").append(it) }
jo.getStringOrNull("title")?.let { append(": ").append(it) }
},
number = number,
url = jo.getString("hid"),
scanlator = jo.optJSONArray("group_name")?.optString(0),
uploadDate = dateFormat.tryParse(jo.getString("created_at").substringBefore('T')),
branch = locale.getDisplayName(locale).toTitleCase(locale),
source = source,
)
}
}
private inline fun <R> JSONArray.mapReversed(block: (JSONObject) -> R): List<R> {
val len = length()
val destination = ArrayList<R>(len)
for (i in (0 until len).reversed()) {
val jo = getJSONObject(i)
destination.add(block(jo))
}
return destination
}
private fun JSONObject.selectGenres(name: String, tags: SparseArrayCompat<MangaTag>): Set<MangaTag> {
val array = optJSONArray(name) ?: return emptySet()
val res = ArraySet<MangaTag>(array.length())
for (i in 0 until array.length()) {
val id = array.getInt(i)
val tag = tags.get(id) ?: continue
res.add(tag)
}
return res
}
}

@ -1,161 +0,0 @@
package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.PagedMangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.mapJSON
import org.koitharu.kotatsu.parsers.util.json.mapJSONIndexed
import org.koitharu.kotatsu.parsers.util.json.mapJSONToSet
import java.util.*
@MangaSourceParser("DESUME", "Desu.me", "ru")
internal class DesuMeParser(override val context: MangaLoaderContext) : PagedMangaParser(MangaSource.DESUME, 20) {
override val configKeyDomain = ConfigKey.Domain("desu.me", null)
override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.UPDATED,
SortOrder.POPULARITY,
SortOrder.NEWEST,
SortOrder.ALPHABETICAL,
)
override suspend fun getListPage(
page: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
if (query != null && page != searchPaginator.firstPage) {
return emptyList()
}
val domain = getDomain()
val url = buildString {
append("https://")
append(domain)
append("/manga/api/?limit=20&order=")
append(getSortKey(sortOrder))
append("&page=")
append(page)
if (!tags.isNullOrEmpty()) {
append("&genres=")
appendAll(tags, ",") { it.key }
}
if (query != null) {
append("&search=")
append(query)
}
}
val json = context.httpGet(url).parseJson().getJSONArray("response")
?: throw ParseException("Invalid response", url)
val total = json.length()
val list = ArrayList<Manga>(total)
for (i in 0 until total) {
val jo = json.getJSONObject(i)
val cover = jo.getJSONObject("image")
val id = jo.getLong("id")
list += Manga(
url = "/manga/api/$id",
publicUrl = jo.getString("url"),
source = MangaSource.DESUME,
title = jo.getString("russian"),
altTitle = jo.getString("name"),
coverUrl = cover.getString("preview"),
largeCoverUrl = cover.getString("original"),
state = when {
jo.getInt("ongoing") == 1 -> MangaState.ONGOING
else -> null
},
rating = jo.getDouble("score").toFloat().coerceIn(0f, 1f),
id = generateUid(id),
isNsfw = false,
tags = emptySet(),
author = null,
description = jo.getString("description"),
)
}
return list
}
override suspend fun getDetails(manga: Manga): Manga {
val url = manga.url.toAbsoluteUrl(getDomain())
val json = context.httpGet(url).parseJson().getJSONObject("response")
?: throw ParseException("Invalid response", url)
val baseChapterUrl = manga.url + "/chapter/"
val chaptersList = json.getJSONObject("chapters").getJSONArray("list")
val totalChapters = chaptersList.length()
return manga.copy(
tags = json.getJSONArray("genres").mapJSONToSet {
MangaTag(
key = it.getString("text"),
title = it.getString("russian").toTitleCase(),
source = manga.source,
)
},
publicUrl = json.getString("url"),
description = json.getString("description"),
chapters = chaptersList.mapJSONIndexed { i, it ->
val chid = it.getLong("id")
val volChap = "Том " + it.optString("vol", "0") + ". " + "Глава " + it.optString("ch", "0")
val title = it.optString("title", "null").takeUnless { it == "null" }
MangaChapter(
id = generateUid(chid),
source = manga.source,
url = "$baseChapterUrl$chid",
uploadDate = it.getLong("date") * 1000,
name = if (title.isNullOrEmpty()) volChap else "$volChap: $title",
number = totalChapters - i,
scanlator = null,
branch = null,
)
}.reversed(),
)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(getDomain())
val json = context.httpGet(fullUrl)
.parseJson()
.getJSONObject("response") ?: throw ParseException("Invalid response", fullUrl)
return json.getJSONObject("pages").getJSONArray("list").mapJSON { jo ->
MangaPage(
id = generateUid(jo.getLong("id")),
referer = fullUrl,
preview = null,
source = chapter.source,
url = jo.getString("img"),
)
}
}
override suspend fun getTags(): Set<MangaTag> {
val doc = context.httpGet("https://${getDomain()}/manga/").parseHtml()
val root = doc.body().requireElementById("animeFilter")
.selectFirstOrThrow(".catalog-genres")
return root.select("li").mapToSet {
val input = it.selectFirstOrThrow("input")
MangaTag(
source = source,
key = input.attr("data-genre-slug").ifEmpty {
it.parseFailed("data-genre-slug is empty")
},
title = input.attr("data-genre-name").toTitleCase().ifEmpty {
it.parseFailed("data-genre-name is empty")
},
)
}
}
private fun getSortKey(sortOrder: SortOrder) =
when (sortOrder) {
SortOrder.ALPHABETICAL -> "name"
SortOrder.POPULARITY -> "popular"
SortOrder.UPDATED -> "updated"
SortOrder.NEWEST -> "id"
else -> "updated"
}
}

@ -1,279 +0,0 @@
package org.koitharu.kotatsu.parsers.site
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.PagedMangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.util.*
import kotlin.math.pow
private const val DOMAIN_UNAUTHORIZED = "e-hentai.org"
private const val DOMAIN_AUTHORIZED = "exhentai.org"
@MangaSourceParser("EXHENTAI", "ExHentai")
internal class ExHentaiParser(
override val context: MangaLoaderContext,
) : PagedMangaParser(MangaSource.EXHENTAI, pageSize = 25), MangaParserAuthProvider {
override val sortOrders: Set<SortOrder> = Collections.singleton(
SortOrder.NEWEST,
)
override val configKeyDomain: ConfigKey.Domain
get() = ConfigKey.Domain(if (isAuthorized) DOMAIN_AUTHORIZED else DOMAIN_UNAUTHORIZED, null)
override val authUrl: String
get() = "https://${getDomain()}/bounce_login.php"
private val ratingPattern = Regex("-?[0-9]+px")
private val authCookies = arrayOf("ipb_member_id", "ipb_pass_hash")
private var updateDm = false
override val isAuthorized: Boolean
get() {
val authorized = isAuthorized(DOMAIN_UNAUTHORIZED)
if (authorized) {
if (!isAuthorized(DOMAIN_AUTHORIZED)) {
context.cookieJar.copyCookies(
DOMAIN_UNAUTHORIZED,
DOMAIN_AUTHORIZED,
authCookies,
)
context.cookieJar.insertCookies(DOMAIN_AUTHORIZED, "yay=louder")
}
return true
}
return false
}
init {
context.cookieJar.insertCookies(DOMAIN_AUTHORIZED, "nw=1", "sl=dm_2")
context.cookieJar.insertCookies(DOMAIN_UNAUTHORIZED, "nw=1", "sl=dm_2")
}
override suspend fun getListPage(
page: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
var search = query?.urlEncoded().orEmpty()
val url = buildString {
append("https://")
append(getDomain())
append("/?page=")
append(page)
if (!tags.isNullOrEmpty()) {
var fCats = 0
for (tag in tags) {
tag.key.toIntOrNull()?.let { fCats = fCats or it } ?: run {
search += tag.key + " "
}
}
if (fCats != 0) {
append("&f_cats=")
append(1023 - fCats)
}
}
if (search.isNotEmpty()) {
append("&f_search=")
append(search.trim().replace(' ', '+'))
}
// by unknown reason cookie "sl=dm_2" is ignored, so, we should request it again
if (updateDm) {
append("&inline_set=dm_e")
}
}
val body = context.httpGet(url).parseHtml().body()
val root = body.selectFirst("table.itg")
?.selectFirst("tbody")
?: if (updateDm) {
body.parseFailed("Cannot find root")
} else {
updateDm = true
return getListPage(page, query, tags, sortOrder)
}
updateDm = false
return root.children().mapNotNull { tr ->
if (tr.childrenSize() != 2) return@mapNotNull null
val (td1, td2) = tr.children()
val glink = td2.selectFirstOrThrow("div.glink")
val a = glink.parents().select("a").first() ?: glink.parseFailed("link not found")
val href = a.attrAsRelativeUrl("href")
val tagsDiv = glink.nextElementSibling() ?: glink.parseFailed("tags div not found")
val mainTag = td2.selectFirst("div.cn")?.let { div ->
MangaTag(
title = div.text().toTitleCase(),
key = tagIdByClass(div.classNames()) ?: return@let null,
source = source,
)
}
Manga(
id = generateUid(href),
title = glink.text().cleanupTitle(),
altTitle = null,
url = href,
publicUrl = a.absUrl("href"),
rating = td2.selectFirst("div.ir")?.parseRating() ?: RATING_UNKNOWN,
isNsfw = true,
coverUrl = td1.selectFirst("img")?.absUrl("src").orEmpty(),
tags = setOfNotNull(mainTag),
state = null,
author = tagsDiv.getElementsContainingOwnText("artist:").first()
?.nextElementSibling()?.text(),
source = source,
)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val doc = context.httpGet(manga.url.toAbsoluteUrl(getDomain())).parseHtml()
val root = doc.body().selectFirstOrThrow("div.gm")
val cover = root.getElementById("gd1")?.children()?.first()
val title = root.getElementById("gd2")
val taglist = root.getElementById("taglist")
val tabs = doc.body().selectFirst("table.ptt")?.selectFirst("tr")
return manga.copy(
title = title?.getElementById("gn")?.text()?.cleanupTitle() ?: manga.title,
altTitle = title?.getElementById("gj")?.text()?.cleanupTitle() ?: manga.altTitle,
publicUrl = doc.baseUri().ifEmpty { manga.publicUrl },
rating = root.getElementById("rating_label")?.text()
?.substringAfterLast(' ')
?.toFloatOrNull()
?.div(5f) ?: manga.rating,
largeCoverUrl = cover?.styleValueOrNull("background")?.cssUrl(),
description = taglist?.select("tr")?.joinToString("<br>") { tr ->
val (tc, td) = tr.children()
val subtags = td.select("a").joinToString { it.html() }
"<b>${tc.html()}</b> $subtags"
},
chapters = tabs?.select("a")?.findLast { a ->
a.text().toIntOrNull() != null
}?.let { a ->
val count = a.text().toInt()
val chapters = ChaptersListBuilder(count)
for (i in 1..count) {
val url = "${manga.url}?p=${i - 1}"
chapters += MangaChapter(
id = generateUid(url),
name = "${manga.title} #$i",
number = i,
url = url,
uploadDate = 0L,
source = source,
scanlator = null,
branch = null,
)
}
chapters.toList()
},
)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val doc = context.httpGet(chapter.url.toAbsoluteUrl(getDomain())).parseHtml()
val root = doc.body().requireElementById("gdt")
return root.select("a").map { a ->
val url = a.attrAsRelativeUrl("href")
MangaPage(
id = generateUid(url),
url = url,
referer = a.absUrl("href"),
preview = null,
source = source,
)
}
}
override suspend fun getPageUrl(page: MangaPage): String {
val doc = context.httpGet(page.url.toAbsoluteUrl(getDomain())).parseHtml()
return doc.body().requireElementById("img").attrAsAbsoluteUrl("src")
}
override suspend fun getTags(): Set<MangaTag> {
val doc = context.httpGet("https://${getDomain()}").parseHtml()
val root = doc.body().requireElementById("searchbox").selectFirstOrThrow("table")
return root.select("div.cs").mapNotNullToSet { div ->
val id = div.id().substringAfterLast('_').toIntOrNull()
?: return@mapNotNullToSet null
MangaTag(
title = div.text().toTitleCase(),
key = id.toString(),
source = source,
)
}
}
override suspend fun getUsername(): String {
val doc = context.httpGet("https://forums.$DOMAIN_UNAUTHORIZED/").parseHtml().body()
val username = doc.getElementById("userlinks")
?.getElementsByAttributeValueContaining("href", "showuser=")
?.firstOrNull()
?.ownText()
?: if (doc.getElementById("userlinksguest") != null) {
throw AuthRequiredException(source)
} else {
doc.parseFailed()
}
return username
}
private fun isAuthorized(domain: String): Boolean {
val cookies = context.cookieJar.getCookies(domain).mapToSet { x -> x.name }
return authCookies.all { it in cookies }
}
private fun Element.parseRating(): Float {
return runCatching {
val style = requireNotNull(attr("style"))
val (v1, v2) = ratingPattern.find(style)!!.destructured
var p1 = v1.dropLast(2).toInt()
val p2 = v2.dropLast(2).toInt()
if (p2 != -1) {
p1 += 8
}
(80 - p1) / 80f
}.getOrDefault(RATING_UNKNOWN)
}
private fun String.cleanupTitle(): String {
val result = StringBuilder(length)
var skip = false
for (c in this) {
when {
c == '[' -> skip = true
c == ']' -> skip = false
c.isWhitespace() && result.isEmpty() -> continue
!skip -> result.append(c)
}
}
while (result.lastOrNull()?.isWhitespace() == true) {
result.deleteCharAt(result.lastIndex)
}
return result.toString()
}
private fun String.cssUrl(): String? {
val fromIndex = indexOf("url(")
if (fromIndex == -1) {
return null
}
val toIndex = indexOf(')', startIndex = fromIndex)
return if (toIndex == -1) {
null
} else {
substring(fromIndex + 4, toIndex).trim()
}
}
private fun tagIdByClass(classNames: Collection<String>): String? {
val className = classNames.find { x -> x.startsWith("ct") } ?: return null
val num = className.drop(2).toIntOrNull(16) ?: return null
return 2.0.pow(num).toInt().toString()
}
}

@ -1,269 +0,0 @@
package org.koitharu.kotatsu.parsers.site
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import org.json.JSONObject
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.*
import java.text.SimpleDateFormat
import java.util.*
private const val PAGE_SIZE = 20
private const val CHAPTERS_FIRST_PAGE_SIZE = 120
private const val CHAPTERS_MAX_PAGE_SIZE = 500
private const val CHAPTERS_PARALLELISM = 3
private const val CONTENT_RATING =
"contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic"
private const val LOCALE_FALLBACK = "en"
@MangaSourceParser("MANGADEX", "MangaDex")
internal class MangaDexParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.MANGADEX) {
override val configKeyDomain = ConfigKey.Domain("mangadex.org", null)
override val sortOrders: EnumSet<SortOrder> = EnumSet.of(
SortOrder.UPDATED,
SortOrder.ALPHABETICAL,
SortOrder.NEWEST,
SortOrder.POPULARITY,
)
override suspend fun getList(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
val domain = getDomain()
val url = buildString {
append("https://api.")
append(domain)
append("/manga?limit=")
append(PAGE_SIZE)
append("&offset=")
append(offset)
append("&includes[]=cover_art&includes[]=author&includes[]=artist&")
tags?.forEach { tag ->
append("includedTags[]=")
append(tag.key)
append('&')
}
if (!query.isNullOrEmpty()) {
append("title=")
append(query.urlEncoded())
append('&')
}
append(CONTENT_RATING)
append("&order")
append(
when (sortOrder) {
SortOrder.UPDATED,
-> "[latestUploadedChapter]=desc"
SortOrder.ALPHABETICAL -> "[title]=asc"
SortOrder.NEWEST -> "[createdAt]=desc"
SortOrder.POPULARITY -> "[followedCount]=desc"
else -> "[followedCount]=desc"
},
)
}
val json = context.httpGet(url).parseJson().getJSONArray("data")
return json.mapJSON { jo ->
val id = jo.getString("id")
val attrs = jo.getJSONObject("attributes")
val relations = jo.getJSONArray("relationships").associateByKey("type")
val cover = relations["cover_art"]
?.getJSONObject("attributes")
?.getString("fileName")
?.let {
"https://uploads.$domain/covers/$id/$it"
}
Manga(
id = generateUid(id),
title = requireNotNull(attrs.getJSONObject("title").selectByLocale()) {
"Title should not be null"
},
altTitle = attrs.optJSONObject("altTitles")?.selectByLocale(),
url = id,
publicUrl = "https://$domain/title/$id",
rating = RATING_UNKNOWN,
isNsfw = attrs.getStringOrNull("contentRating") == "erotica",
coverUrl = cover?.plus(".256.jpg").orEmpty(),
largeCoverUrl = cover,
description = attrs.optJSONObject("description")?.selectByLocale(),
tags = attrs.getJSONArray("tags").mapJSONToSet { tag ->
MangaTag(
title = tag.getJSONObject("attributes")
.getJSONObject("name")
.firstStringValue()
.toTitleCase(),
key = tag.getString("id"),
source = source,
)
},
state = when (jo.getStringOrNull("status")) {
"ongoing" -> MangaState.ONGOING
"completed" -> MangaState.FINISHED
else -> null
},
author = (relations["author"] ?: relations["artist"])
?.getJSONObject("attributes")
?.getStringOrNull("name"),
source = source,
)
}
}
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
val domain = getDomain()
val mangaId = manga.url.removePrefix("/")
val attrsDeferred = async {
context.httpGet(
"https://api.$domain/manga/${mangaId}?includes[]=artist&includes[]=author&includes[]=cover_art",
).parseJson().getJSONObject("data").getJSONObject("attributes")
}
val feedDeferred = async { loadChapters(mangaId) }
val mangaAttrs = attrsDeferred.await()
val feed = feedDeferred.await()
// 2022-01-02T00:27:11+00:00
val dateFormat = SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss'+00:00'",
Locale.ROOT,
)
manga.copy(
description = mangaAttrs.getJSONObject("description").selectByLocale()
?: manga.description,
chapters = feed.mapChapters { _, jo ->
val id = jo.getString("id")
val attrs = jo.getJSONObject("attributes")
if (!attrs.isNull("externalUrl")) {
return@mapChapters null
}
val locale = attrs.getStringOrNull("translatedLanguage")?.let { Locale.forLanguageTag(it) }
val relations = jo.getJSONArray("relationships").associateByKey("type")
val number = attrs.getIntOrDefault("chapter", 0)
MangaChapter(
id = generateUid(id),
name = attrs.getStringOrNull("title")?.takeUnless(String::isEmpty)
?: "Chapter #$number",
number = number,
url = id,
scanlator = relations["scanlation_group"]?.getStringOrNull("name"),
uploadDate = dateFormat.tryParse(attrs.getString("publishAt")),
branch = locale?.getDisplayName(locale)?.toTitleCase(locale),
source = source,
)
},
)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val domain = getDomain()
val chapterJson = context.httpGet("https://api.$domain/at-home/server/${chapter.url}?forcePort443=false")
.parseJson()
.getJSONObject("chapter")
val pages = chapterJson.getJSONArray("data")
val prefix = "https://uploads.$domain/data/${chapterJson.getString("hash")}/"
val referer = "https://$domain/"
return List(pages.length()) { i ->
val url = prefix + pages.getString(i)
MangaPage(
id = generateUid(url),
url = url,
referer = referer,
preview = null, // TODO prefix + dataSaver.getString(i),
source = source,
)
}
}
override suspend fun getTags(): Set<MangaTag> {
val tags = context.httpGet("https://api.${getDomain()}/manga/tag").parseJson()
.getJSONArray("data")
return tags.mapJSONToSet { jo ->
MangaTag(
title = jo.getJSONObject("attributes").getJSONObject("name").firstStringValue().toTitleCase(),
key = jo.getString("id"),
source = source,
)
}
}
private fun JSONObject.firstStringValue() = values().next() as String
private fun JSONObject.selectByLocale(): String? {
val preferredLocales = context.getPreferredLocales()
for (locale in preferredLocales) {
getStringOrNull(locale.language)?.let { return it }
getStringOrNull(locale.toLanguageTag())?.let { return it }
}
return getStringOrNull(LOCALE_FALLBACK) ?: values().nextOrNull() as? String
}
private suspend fun loadChapters(mangaId: String): List<JSONObject> {
val firstPage = loadChapters(mangaId, offset = 0, limit = CHAPTERS_FIRST_PAGE_SIZE)
if (firstPage.size >= firstPage.total) {
return firstPage.data
}
val tail = coroutineScope {
val leftCount = firstPage.total - firstPage.size
val pages = (leftCount / CHAPTERS_MAX_PAGE_SIZE.toFloat()).toIntUp()
val dispatcher = Dispatchers.Default.limitedParallelism(CHAPTERS_PARALLELISM)
List(pages) { page ->
val offset = page * CHAPTERS_MAX_PAGE_SIZE + firstPage.size
async(dispatcher) {
loadChapters(mangaId, offset, CHAPTERS_MAX_PAGE_SIZE)
}
}.awaitAll()
}
val result = ArrayList<JSONObject>(firstPage.total)
result += firstPage.data
tail.flatMapTo(result) { it.data }
return result
}
private suspend fun loadChapters(mangaId: String, offset: Int, limit: Int): Chapters {
val url = buildString {
append("https://api.")
append(getDomain())
append("/manga/")
append(mangaId)
append("/feed")
append("?limit=")
append(limit)
append("&includes[]=scanlation_group&order[volume]=asc&order[chapter]=asc&offset=")
append(offset)
append('&')
append(CONTENT_RATING)
}
val json = context.httpGet(url).parseJson()
if (json.getString("result") == "ok") {
return Chapters(
data = json.optJSONArray("data")?.toJSONList().orEmpty(),
total = json.getInt("total"),
)
} else {
val error = json.optJSONArray("errors").mapJSON { jo ->
jo.getString("detail")
}.joinToString("\n")
throw ParseException(error, url)
}
}
private class Chapters(
val data: List<JSONObject>,
val total: Int,
) {
val size: Int
get() = data.size
}
}

@ -1,152 +0,0 @@
package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.PagedMangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.*
private const val DEF_BRANCH_NAME = "Основний переклад"
@MangaSourceParser("MANGAINUA", "MANGA/in/UA", "uk")
class MangaInUaParser(override val context: MangaLoaderContext) : PagedMangaParser(
source = MangaSource.MANGAINUA,
pageSize = 24,
searchPageSize = 10,
) {
override val sortOrders: Set<SortOrder>
get() = Collections.singleton(SortOrder.UPDATED)
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("manga.in.ua", null)
override suspend fun getListPage(
page: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
val url = when {
!query.isNullOrEmpty() -> (
"/index.php?do=search" +
"&subaction=search" +
"&search_start=$page" +
"&full_search=1" +
"&story=$query" +
"&titleonly=3"
).toAbsoluteUrl(getDomain())
tags.isNullOrEmpty() -> "/mangas/page/$page".toAbsoluteUrl(getDomain())
tags.size == 1 -> "${tags.first().key}/page/$page"
tags.size > 1 -> throw IllegalArgumentException("This source supports only 1 genre")
else -> "/mangas/page/$page".toAbsoluteUrl(getDomain())
}
val doc = context.httpGet(url).parseHtml()
val container = doc.body().requireElementById("dle-content")
val items = container.select("div.col-6")
return items.mapNotNull { item ->
val href = item.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapNotNull null
Manga(
id = generateUid(href),
title = item.selectFirst("h3.card__title")?.text() ?: return@mapNotNull null,
coverUrl = item.selectFirst("header.card__cover")?.selectFirst("img")?.run {
attrAsAbsoluteUrlOrNull("data-src") ?: attrAsAbsoluteUrlOrNull("src")
}.orEmpty(),
altTitle = null,
author = null,
rating = item.selectFirst("div.card__short-rate--num")
?.text()
?.toFloatOrNull()
?.div(10F) ?: RATING_UNKNOWN,
url = href,
isNsfw = item.selectFirst("ul.card__list")?.select("li")?.lastOrNull()?.text() == "18+",
tags = runCatching {
item.selectFirst("div.card__category")?.select("a")?.mapToSet {
MangaTag(
title = it.ownText(),
key = it.attr("href").removeSuffix("/"),
source = source,
)
}
}.getOrNull().orEmpty(),
state = null,
publicUrl = href.toAbsoluteUrl(container.host ?: getDomain()),
source = source,
)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val doc = context.httpGet(manga.url.toAbsoluteUrl(getDomain())).parseHtml()
val root = doc.body().requireElementById("dle-content")
val dateFormat = SimpleDateFormat("dd.MM.yyyy", Locale.US)
val chapterNodes = root.selectFirstOrThrow(".linkstocomics").select(".ltcitems")
var prevChapterName: String? = null
var i = 0
return manga.copy(
description = root.selectFirst("div.item__full-description")?.text(),
largeCoverUrl = root.selectFirst("div.item__full-sidebar--poster")?.selectFirst("img")
?.attrAsAbsoluteUrlOrNull("src"),
chapters = chapterNodes.mapChapters { _, item ->
val href = item?.selectFirst("a")?.attrAsRelativeUrlOrNull("href")
?: return@mapChapters null
val isAlternative = item.styleValueOrNull("background") != null
val name = item.selectFirst("a")?.text().orEmpty()
if (!isAlternative) i++
MangaChapter(
id = generateUid(href),
name = if (isAlternative) {
prevChapterName ?: return@mapChapters null
} else {
prevChapterName = name
name
},
number = i,
url = href,
scanlator = null,
branch = if (isAlternative) {
name.substringAfterLast(':').trim()
} else {
DEF_BRANCH_NAME
},
uploadDate = dateFormat.tryParse(item.selectFirst("div.ltcright")?.text()),
source = source,
)
},
)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(getDomain())
val doc = context.httpGet(fullUrl).parseHtml()
val root = doc.body().requireElementById("comics").selectFirstOrThrow("ul.xfieldimagegallery")
return root.select("li").map { ul ->
val img = ul.selectFirstOrThrow("img")
val url = img.attrAsAbsoluteUrl("data-src")
MangaPage(
id = generateUid(url),
url = url,
preview = null,
referer = fullUrl,
source = source,
)
}
}
override suspend fun getTags(): Set<MangaTag> {
val domain = getDomain()
val doc = context.httpGet("https://$domain/mangas").parseHtml()
val root = doc.body().requireElementById("menu_1").selectFirstOrThrow("div.menu__wrapper")
return root.select("li").mapNotNullToSet { li ->
val a = li.selectFirst("a") ?: return@mapNotNullToSet null
MangaTag(
title = a.ownText(),
key = a.attr("href").removeSuffix("/"),
source = source,
)
}
}
}

@ -1,186 +0,0 @@
package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.*
@MangaSourceParser("MANGAOWL", "MangaOwl", "en")
internal class MangaOwlParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.MANGAOWL) {
override val configKeyDomain = ConfigKey.Domain("mangaowls.com", null)
override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.POPULARITY,
SortOrder.NEWEST,
SortOrder.UPDATED,
)
private val regexNsfw = Regex("(yaoi)|(yuri)|(smut)|(mature)|(adult)", RegexOption.IGNORE_CASE)
override suspend fun getList(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
val page = (offset / 36f).toIntUp().inc()
val link = buildString {
append("https://")
append(getDomain())
when {
!query.isNullOrEmpty() -> {
append("/search/$page?search=")
append(query.urlEncoded())
}
!tags.isNullOrEmpty() -> {
for (tag in tags) {
append(tag.key)
}
append("/$page?type=${getAlternativeSortKey(sortOrder)}")
}
else -> {
append("/${getSortKey(sortOrder)}/$page")
}
}
}
val doc = context.httpGet(link).parseHtml()
val slides = doc.body().selectOrThrow("ul.slides")
val items = slides.select("div.col-md-2")
return items.mapNotNull { item ->
val href = item.selectFirst("h6 a")?.attrAsRelativeUrlOrNull("href") ?: return@mapNotNull null
Manga(
id = generateUid(href),
title = item.selectFirst("h6 a")?.text() ?: return@mapNotNull null,
coverUrl = item.select("div.img-responsive").attr("abs:data-background-image"),
altTitle = null,
author = null,
rating = runCatching {
item.selectFirst("div.block-stars")
?.text()
?.toFloatOrNull()
?.div(10f)
}.getOrNull() ?: RATING_UNKNOWN,
url = href,
isNsfw = false,
tags = emptySet(),
state = null,
publicUrl = href.toAbsoluteUrl(getDomain()),
source = source,
)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val doc = context.httpGet(manga.publicUrl).parseHtml()
val info = doc.body().selectFirstOrThrow("div.single_detail")
val table = doc.body().selectFirstOrThrow("div.single-grid-right")
val dateFormat = SimpleDateFormat("MM/dd/yyyy", Locale.US)
val trRegex = "window\\['tr'] = '([^']*)';".toRegex(RegexOption.IGNORE_CASE)
val trElement = doc.getElementsByTag("script").find { trRegex.find(it.data()) != null }
?: doc.parseFailed("Oops, tr not found")
val tr = trRegex.find(trElement.data())!!.groups[1]!!.value
val s = context.encodeBase64(getDomain().toByteArray())
var isNsfw = manga.isNsfw
val parsedTags = info.select("div.col-xs-12.col-md-8.single-right-grid-right > p > a[href*=genres]")
.mapNotNullToSet {
val a = it.selectFirst("a") ?: return@mapNotNullToSet null
val name = a.text()
if (!isNsfw && isNsfwGenre(name)) {
isNsfw = true
}
MangaTag(
title = name.toTitleCase(),
key = a.attr("href"),
source = source,
)
}
return manga.copy(
description = info.selectFirst(".description")?.html(),
largeCoverUrl = info.select("img").first()?.let { img ->
if (img.hasAttr("data-src")) img.attr("abs:data-src") else img.attr("abs:src")
},
isNsfw = isNsfw,
author = info.selectFirst("p.fexi_header_para a.author_link")?.text(),
state = parseStatus(info.select("p.fexi_header_para:contains(status)").first()?.ownText()),
tags = manga.tags + parsedTags,
chapters = table.select("div.table.table-chapter-list").select("li.list-group-item.chapter_list")
.asReversed().mapChapters { i, li ->
val a = li.select("a")
val href = a.attr("data-href").ifEmpty {
li.parseFailed("Link is missing")
}
MangaChapter(
id = generateUid(href),
name = a.select("label").text(),
number = i + 1,
url = "$href?tr=$tr&s=$s",
scanlator = null,
branch = null,
uploadDate = dateFormat.tryParse(li.selectFirst("small:last-of-type")?.text()),
source = MangaSource.MANGAOWL,
)
},
)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(getDomain())
val doc = context.httpGet(fullUrl).parseHtml()
val root = doc.body().selectOrThrow("div.item img.owl-lazy")
return root.map { div ->
val url = div?.attrAsRelativeUrlOrNull("data-src") ?: doc.parseFailed("Page image not found")
MangaPage(
id = generateUid(url),
url = url,
preview = null,
referer = url,
source = MangaSource.MANGAOWL,
)
}
}
private fun parseStatus(status: String?) = when {
status == null -> null
status.contains("Ongoing") -> MangaState.ONGOING
status.contains("Completed") -> MangaState.FINISHED
else -> null
}
override suspend fun getTags(): Set<MangaTag> {
val doc = context.httpGet("https://${getDomain()}/").parseHtml()
val root = doc.body().select("ul.dropdown-menu.multi-column.columns-3").select("li")
return root.mapToSet { p ->
val a = p.selectFirstOrThrow("a")
MangaTag(
title = a.text().toTitleCase(),
key = a.attr("href"),
source = source,
)
}
}
private fun getSortKey(sortOrder: SortOrder) =
when (sortOrder) {
SortOrder.POPULARITY -> "popular"
SortOrder.NEWEST -> "new_release"
SortOrder.UPDATED -> "lastest"
else -> "lastest"
}
private fun getAlternativeSortKey(sortOrder: SortOrder) =
when (sortOrder) {
SortOrder.POPULARITY -> "0"
SortOrder.NEWEST -> "2"
SortOrder.UPDATED -> "3"
else -> "3"
}
private fun isNsfwGenre(name: String): Boolean = regexNsfw.containsMatchIn(name)
}

@ -1,217 +0,0 @@
package org.koitharu.kotatsu.parsers.site
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
@MangaSourceParser("MANGATOWN", "MangaTown", "en")
internal class MangaTownParser(override val context: MangaLoaderContext) : MangaParser(MangaSource.MANGATOWN) {
override val configKeyDomain = ConfigKey.Domain("www.mangatown.com", null)
override val sortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.ALPHABETICAL,
SortOrder.RATING,
SortOrder.POPULARITY,
SortOrder.UPDATED,
)
private val regexTag = Regex("[^\\-]+-[^\\-]+-[^\\-]+-[^\\-]+-[^\\-]+-[^\\-]+")
override suspend fun getList(
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
val sortKey = when (sortOrder) {
SortOrder.ALPHABETICAL -> "?name.az"
SortOrder.RATING -> "?rating.za"
SortOrder.UPDATED -> "?last_chapter_time.za"
else -> ""
}
val page = (offset / 30) + 1
val url = when {
!query.isNullOrEmpty() -> {
if (offset != 0) {
return emptyList()
}
"/search?name=${query.urlEncoded()}".toAbsoluteUrl(getDomain())
}
tags.isNullOrEmpty() -> "/directory/$page.htm$sortKey".toAbsoluteUrl(getDomain())
tags.size == 1 -> "/directory/${tags.first().key}/$page.htm$sortKey".toAbsoluteUrl(getDomain())
else -> tags.joinToString(
prefix = "/search?page=$page".toAbsoluteUrl(getDomain()),
) { tag ->
"&genres[${tag.key}]=1"
}
}
val doc = context.httpGet(url).parseHtml()
val root = doc.body().selectFirstOrThrow("ul.manga_pic_list")
return root.select("li").mapNotNull { li ->
val a = li.selectFirst("a.manga_cover")
val href = a?.attrAsRelativeUrlOrNull("href")
?: return@mapNotNull null
val views = li.select("p.view")
val status = views.firstNotNullOfOrNull { it.ownText().takeIf { x -> x.startsWith("Status:") } }
?.substringAfter(':')?.trim()?.lowercase(Locale.ROOT)
Manga(
id = generateUid(href),
title = a.attr("title"),
coverUrl = a.selectFirst("img")?.absUrl("src").orEmpty(),
source = MangaSource.MANGATOWN,
altTitle = null,
rating = li.selectFirst("p.score")?.selectFirst("b")
?.ownText()?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
author = views.firstNotNullOfOrNull { it.text().takeIf { x -> x.startsWith("Author:") } }
?.substringAfter(':')
?.trim(),
state = when (status) {
"ongoing" -> MangaState.ONGOING
"completed" -> MangaState.FINISHED
else -> null
},
tags = li.selectFirst("p.keyWord")?.select("a")?.mapNotNullToSet tags@{ x ->
MangaTag(
title = x.attr("title").toTitleCase(),
key = x.attr("href").parseTagKey() ?: return@tags null,
source = MangaSource.MANGATOWN,
)
}.orEmpty(),
url = href,
isNsfw = false,
publicUrl = href.toAbsoluteUrl(a.host ?: getDomain()),
)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val doc = context.httpGet(manga.url.toAbsoluteUrl(getDomain())).parseHtml()
val root = doc.body().selectFirstOrThrow("section.main")
.selectFirstOrThrow("div.article_content")
val info = root.selectFirst("div.detail_info")?.selectFirst("ul")
val chaptersList = root.selectFirst("div.chapter_content")
?.selectFirst("ul.chapter_list")?.select("li")?.asReversed()
val dateFormat = SimpleDateFormat("MMM dd,yyyy", Locale.US)
return manga.copy(
tags = manga.tags + info?.select("li")?.find { x ->
x.selectFirst("b")?.ownText() == "Genre(s):"
}?.select("a")?.mapNotNull { a ->
MangaTag(
title = a.attr("title").toTitleCase(),
key = a.attr("href").parseTagKey() ?: return@mapNotNull null,
source = MangaSource.MANGATOWN,
)
}.orEmpty(),
description = info?.getElementById("show")?.ownText(),
chapters = chaptersList?.mapChapters { i, li ->
val href = li.selectFirst("a")?.attrAsRelativeUrlOrNull("href")
?: return@mapChapters null
val name = li.select("span")
.filter { x -> x.className().isEmpty() }
.joinToString(" - ") { it.text() }.trim()
MangaChapter(
id = generateUid(href),
url = href,
source = MangaSource.MANGATOWN,
number = i + 1,
uploadDate = parseChapterDate(
dateFormat,
li.selectFirst("span.time")?.text(),
),
name = name.ifEmpty { "${manga.title} - ${i + 1}" },
scanlator = null,
branch = null,
)
} ?: bypassLicensedChapters(manga),
)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(getDomain())
val doc = context.httpGet(fullUrl).parseHtml()
val root = doc.body().selectFirstOrThrow("div.page_select")
return root.selectFirstOrThrow("select").selectOrThrow("option").mapNotNull {
val href = it.attrAsRelativeUrlOrNull("value")
if (href == null || href.endsWith("featured.html")) {
return@mapNotNull null
}
MangaPage(
id = generateUid(href),
url = href,
preview = null,
referer = fullUrl,
source = MangaSource.MANGATOWN,
)
}
}
override suspend fun getPageUrl(page: MangaPage): String {
val doc = context.httpGet(page.url.toAbsoluteUrl(getDomain())).parseHtml()
return doc.requireElementById("image").attrAsAbsoluteUrl("src")
}
override suspend fun getTags(): Set<MangaTag> {
val doc = context.httpGet("/directory/".toAbsoluteUrl(getDomain())).parseHtml()
val root = doc.body().selectFirst("aside.right")
?.getElementsContainingOwnText("Genres")
?.first()
?.nextElementSibling() ?: doc.parseFailed("Root not found")
return root.select("li").mapNotNullToSet { li ->
val a = li.selectFirst("a") ?: return@mapNotNullToSet null
val key = a.attr("href").parseTagKey()
if (key.isNullOrEmpty()) {
return@mapNotNullToSet null
}
MangaTag(
source = MangaSource.MANGATOWN,
key = key,
title = a.text().toTitleCase(),
)
}
}
private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long {
return when {
date.isNullOrEmpty() -> 0L
date.contains("Today") -> Calendar.getInstance().timeInMillis
date.contains("Yesterday") -> Calendar.getInstance().apply { add(Calendar.DAY_OF_MONTH, -1) }.timeInMillis
else -> dateFormat.tryParse(date)
}
}
private suspend fun bypassLicensedChapters(manga: Manga): List<MangaChapter> {
val doc = context.httpGet(manga.url.toAbsoluteUrl(getDomain("m"))).parseHtml()
val list = doc.body().selectFirst("ul.detail-ch-list") ?: return emptyList()
val dateFormat = SimpleDateFormat("MMM dd,yyyy", Locale.US)
return list.select("li").asReversed().mapIndexedNotNull { i, li ->
val a = li.selectFirst("a") ?: return@mapIndexedNotNull null
val href = a.attrAsRelativeUrl("href")
val name = a.selectFirst("span.vol")?.text().orEmpty().ifEmpty {
a.ownText()
}
MangaChapter(
id = generateUid(href),
url = href,
source = MangaSource.MANGATOWN,
number = i + 1,
uploadDate = parseChapterDate(
dateFormat,
li.selectFirst("span.time")?.text(),
),
name = name.ifEmpty { "${manga.title} - ${i + 1}" },
scanlator = null,
branch = null,
)
}
}
private fun String.parseTagKey() = split('/').findLast { regexTag matches it }
}

@ -1,188 +0,0 @@
package org.koitharu.kotatsu.parsers.site
import androidx.collection.ArraySet
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.PagedMangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.*
@MangaSourceParser("NHENTAI", "N-Hentai")
class NHentaiParser(override val context: MangaLoaderContext) : PagedMangaParser(MangaSource.NHENTAI, pageSize = 25) {
override val configKeyDomain: ConfigKey.Domain
get() = ConfigKey.Domain("nhentai.net", null)
override val sortOrders: Set<SortOrder>
get() = EnumSet.of(SortOrder.NEWEST, SortOrder.POPULARITY)
override suspend fun getListPage(
page: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
if (query.isNullOrEmpty() && tags != null && tags.size > 1) {
return getListPage(page, buildQuery(tags), emptySet(), sortOrder)
}
val domain = getDomain()
val url = buildString {
append("https://")
append(domain)
if (!query.isNullOrEmpty()) {
append("/search/?q=")
append(query.urlEncoded())
append("&page=")
append(page)
if (sortOrder == SortOrder.POPULARITY) {
append("&sort=popular")
}
} else {
append('/')
if (!tags.isNullOrEmpty()) {
val tag = tags.single()
append("tag/")
append(tag.key)
append('/')
if (sortOrder == SortOrder.POPULARITY) {
append("popular")
}
append("?page=")
append(page)
} else {
if (sortOrder == SortOrder.POPULARITY) {
append("?sort=popular&page=")
} else {
append("?page=")
}
append(page)
}
}
}
val root = context.httpGet(url).parseHtml().body().requireElementById("content")
.selectLastOrThrow("div.index-container")
val regexBrackets = Regex("\\[[^]]+]|\\([^)]+\\)")
val regexSpaces = Regex("\\s+")
return root.select(".gallery").map { div ->
val a = div.selectFirstOrThrow("a.cover")
val href = a.attrAsRelativeUrl("href")
val img = div.selectFirstOrThrow("img")
val title = div.selectFirstOrThrow(".caption").text()
Manga(
id = generateUid(href),
title = title.replace(regexBrackets, "")
.replace(regexSpaces, " ")
.trim(),
altTitle = null,
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = true,
coverUrl = img.attrAsAbsoluteUrlOrNull("data-src")
?: img.attrAsAbsoluteUrl("src"),
tags = setOf(),
state = null,
author = null,
largeCoverUrl = null,
description = null,
chapters = listOf(),
source = source,
)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val root = context.httpGet(
url = manga.url.toAbsoluteUrl(getDomain()),
).parseHtml().body().requireElementById("bigcontainer")
val img = root.requireElementById("cover").selectFirstOrThrow("img")
val tagContainers = root.requireElementById("tags").select(".tag-container")
val dateFormat = SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss.SSSSSS'+00:00'",
Locale.ROOT,
)
return manga.copy(
tags = tagContainers.find { x -> x.ownText() == "Tags:" }?.parseTags() ?: manga.tags,
author = tagContainers.find { x -> x.ownText() == "Artists:" }
?.selectFirst("span.name")?.text()?.toCamelCase(),
largeCoverUrl = img.attrAsAbsoluteUrlOrNull("data-src")
?: img.attrAsAbsoluteUrl("src"),
description = null,
chapters = listOf(
MangaChapter(
id = manga.id,
name = manga.title,
number = 1,
url = manga.url,
scanlator = null,
uploadDate = dateFormat.tryParse(
tagContainers.find { x -> x.ownText() == "Uploaded:" }
?.selectFirst("time")
?.attr("datetime"),
),
branch = null,
source = source,
),
),
)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val url = chapter.url.toAbsoluteUrl(getDomain())
val root = context.httpGet(url).parseHtml().requireElementById("thumbnail-container")
return root.select(".thumb-container").map { div ->
val a = div.selectFirstOrThrow("a")
val img = div.selectFirstOrThrow("img")
val href = a.attrAsRelativeUrl("href")
MangaPage(
id = generateUid(href),
url = href,
referer = url,
preview = img.attrAsAbsoluteUrlOrNull("data-src")
?: img.attrAsAbsoluteUrl("src"),
source = source,
)
}
}
override suspend fun getPageUrl(page: MangaPage): String {
val root = context.httpGet(page.url.toAbsoluteUrl(getDomain())).parseHtml().body()
.requireElementById("image-container")
return root.selectFirstOrThrow("img").attrAsAbsoluteUrl("src")
}
override suspend fun getTags(): Set<MangaTag> {
return coroutineScope {
// parse first 3 pages of tags
(1..3).map { page ->
async { getTags(page) }
}
}.awaitAll().flattenTo(ArraySet(360))
}
private suspend fun getTags(page: Int): Set<MangaTag> {
val root = context.httpGet("https://${getDomain()}/tags/popular?page=$page").parseHtml().body()
.getElementById("tag-container")
return root?.parseTags().orEmpty()
}
private fun Element.parseTags() = select("a.tag").mapToSet { a ->
val href = a.attr("href").removeSuffix('/')
MangaTag(
title = a.selectFirstOrThrow(".name").text().toTitleCase(),
key = href.substringAfterLast('/'),
source = source,
)
}
private fun buildQuery(tags: Collection<MangaTag>) = tags.joinToString(separator = " ") { tag ->
"tag:\"${tag.key}\""
}
}

@ -1,221 +0,0 @@
package org.koitharu.kotatsu.parsers.site
import androidx.collection.ArrayMap
import androidx.collection.ArraySet
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.koitharu.kotatsu.parsers.*
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.NotFoundException
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.*
@MangaSourceParser("NETTRUYEN", "NetTruyen", "vi")
class NetTruyenParser(override val context: MangaLoaderContext) :
PagedMangaParser(MangaSource.NETTRUYEN, pageSize = 36) {
override val configKeyDomain: ConfigKey.Domain
get() = ConfigKey.Domain("www.nettruyenme.com", null)
override val sortOrders: Set<SortOrder>
get() = EnumSet.of(SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.RATING)
private val mutex = Mutex()
private val dateFormat = SimpleDateFormat("dd/MM/yy", Locale.US)
private var tagCache: ArrayMap<String, MangaTag>? = null
override suspend fun getDetails(manga: Manga): Manga {
val doc = context.httpGet(manga.url.toAbsoluteUrl(getDomain())).parseHtml()
val rating = doc.selectFirst("span[itemprop=ratingValue]")
?.ownText()
?.toFloatOrNull() ?: 0f
val chapterElements = doc.getElementById("nt_listchapter")?.select("ul > li") ?: doc.parseFailed()
val chapters = chapterElements.asReversed().mapChapters { index, element ->
val a = element.selectFirst("div.chapter > a") ?: return@mapChapters null
val relativeUrl = a.attrAsRelativeUrlOrNull("href") ?: return@mapChapters null
val timeText = element.selectFirst("div.col-xs-4.text-center.no-wrap.small")?.text()
MangaChapter(
id = generateUid(relativeUrl),
name = a.text(),
number = index + 1,
url = relativeUrl,
scanlator = null,
uploadDate = parseChapterTime(timeText),
branch = null,
source = source,
)
}
return manga.copy(
rating = rating / 5,
chapters = chapters,
description = doc.selectFirst("div.detail-content > p")?.html(),
isNsfw = doc.selectFirst("div.alert.alert-danger > strong:contains(Cảnh báo độ tuổi)") != null,
)
}
// 20 giây trước
// 52 phút trước
// 6 giờ trước
// 2 ngày trước
// 19:09 30/07
// 23/12/21
private fun parseChapterTime(timeText: String?): Long {
if (timeText.isNullOrEmpty()) {
return 0L
}
val timeWords = arrayOf("giây", "phút", "giờ", "ngày")
val calendar = Calendar.getInstance()
val timeArr = timeText.split(' ')
if (WordSet(*timeWords).anyWordIn(timeText)) {
val timeSuffix = timeArr.getOrNull(1)
val timeDiff = timeArr.getOrNull(0)?.toIntOrNull() ?: return 0L
when (timeSuffix) {
timeWords[0] -> calendar.add(Calendar.SECOND, -timeDiff)
timeWords[1] -> calendar.add(Calendar.MINUTE, -timeDiff)
timeWords[2] -> calendar.add(Calendar.HOUR, -timeDiff)
timeWords[3] -> calendar.add(Calendar.DATE, -timeDiff)
else -> return 0L
}
} else {
val relativeDate = timeArr.lastOrNull() ?: return 0L
val dateString = when (relativeDate.split('/').size) {
2 -> {
val currentYear = calendar.get(Calendar.YEAR).toString().takeLast(2)
"$relativeDate/$currentYear"
}
3 -> relativeDate
else -> return 0L
}
calendar.timeInMillis = dateFormat.tryParse(dateString)
}
return calendar.time.time
}
override suspend fun getListPage(
page: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder,
): List<Manga> {
val isSearching = !query.isNullOrEmpty()
val url = buildString {
append("https://")
append(getDomain())
if (isSearching) {
append("/tim-truyen?keyword=")
append(query!!.urlEncoded())
append("&page=")
append(page)
} else {
val tagQuery = tags.orEmpty().joinToString(",") { it.key }
append("/tim-truyen-nang-cao?genres=$tagQuery")
append("&notgenres=&gender=-1&status=-1&minchapter=1&sort=${getSortOrderKey(sortOrder)}")
append("&page=$page")
}
}
val response = if (isSearching) {
val result = runCatching { context.httpGet(url) }
val exception = result.exceptionOrNull()
if (exception is NotFoundException) {
return emptyList()
}
result.getOrThrow()
} else {
context.httpGet(url)
}
val itemsElements = response.parseHtml()
.select("div.ModuleContent > div.items")
.select("div.item")
return itemsElements.mapNotNull { item ->
val tooltipElement = item.selectFirst("div.box_tootip") ?: return@mapNotNull null
val absUrl = item.selectFirst("div.image > a")?.attrAsAbsoluteUrlOrNull("href") ?: return@mapNotNull null
val slug = absUrl.substringAfterLast('/')
val mangaState = when (tooltipElement.selectFirst("div.message_main > p:contains(Tình trạng)")?.ownText()) {
"Đang tiến hành" -> MangaState.ONGOING
"Hoàn thành" -> MangaState.FINISHED
else -> null
}
val tagMap = getOrCreateTagMap()
val tagsElement = tooltipElement.selectFirst("div.message_main > p:contains(Thể loại)")?.ownText().orEmpty()
val mangaTags = tagsElement.split(',').mapNotNullToSet { tagMap[it.trim()] }
Manga(
id = generateUid(slug),
title = tooltipElement.selectFirst("div.title")?.text().orEmpty(),
altTitle = null,
url = absUrl.toRelativeUrl(getDomain()),
publicUrl = absUrl,
rating = RATING_UNKNOWN,
isNsfw = false,
coverUrl = item.selectFirst("div.image a img")?.absUrl("data-original").orEmpty(),
largeCoverUrl = null,
tags = mangaTags,
state = mangaState,
author = tooltipElement.selectFirst("div.message_main > p:contains(Tác giả)")?.ownText(),
description = tooltipElement.selectFirst("div.box_text")?.text(),
chapters = null,
source = source,
)
}
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val pageElements = context.httpGet(chapter.url.toAbsoluteUrl(getDomain())).parseHtml()
.select("div.reading-detail.box_doc > div img")
return pageElements.map { element ->
val url = element.attrAsAbsoluteUrl("data-original")
MangaPage(
id = generateUid(url),
url = url,
referer = getDomain(),
preview = null,
source = source,
)
}
}
override suspend fun getTags(): Set<MangaTag> {
val map = getOrCreateTagMap()
val tagSet = ArraySet<MangaTag>(map.size)
for (entry in map) {
tagSet.add(entry.value)
}
return tagSet
}
private suspend fun getOrCreateTagMap(): ArrayMap<String, MangaTag> = mutex.withLock {
tagCache?.let { return@withLock it }
val doc = context.httpGet("/tim-truyen-nang-cao".toAbsoluteUrl(getDomain())).parseHtml()
val tagItems = doc.select("div.genre-item")
val result = ArrayMap<String, MangaTag>(tagItems.size)
for (item in tagItems) {
val title = item.text().trim()
val key = item.select("span[data-id]").attr("data-id")
result[title] = MangaTag(title = title, key = key, source = source)
}
tagCache = result
result
}
private fun getSortOrderKey(sortOrder: SortOrder) = when (sortOrder) {
SortOrder.UPDATED -> 0
SortOrder.POPULARITY -> 10
SortOrder.NEWEST -> 15
SortOrder.RATING -> 20
else -> throw IllegalArgumentException("Sort order ${sortOrder.name} not supported")
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save