Misc code cleanup

This commit is contained in:
Eugene 2019-10-27 15:41:40 -04:00
parent 170c382b15
commit 40de8b9723
No known key found for this signature in database
GPG Key ID: E1FD745328866B0A
32 changed files with 777 additions and 691 deletions

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: ComiCake'
pkgNameSuffix = 'all.comicake'
extClass = '.ComiCakeFactory'
extVersionCode = 6
extVersionCode = 7
libVersion = '1.2'
}

View File

@ -1,9 +1,13 @@
package eu.kanade.tachiyomi.extension.all.comicake
import android.os.Build
import eu.kanade.tachiyomi.extension.BuildConfig
import eu.kanade.tachiyomi.extensions.BuildConfig
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.Headers
import okhttp3.Request
@ -12,23 +16,22 @@ import org.json.JSONArray
import org.json.JSONObject
import java.text.SimpleDateFormat
const val COMICAKE_DEFAULT_API_ENDPOINT = "/api" // Highly unlikely to change
const val COMICAKE_DEFAULT_READER_ENDPOINT = "/r" // Can change based on CC config
abstract class ComiCake(
override val name: String,
final override val baseUrl: String,
override val lang: String,
readerEndpoint: String = COMICAKE_DEFAULT_READER_ENDPOINT,
apiEndpoint: String = COMICAKE_DEFAULT_API_ENDPOINT
) : HttpSource() {
open class ComiCake(override val name: String,
final override val baseUrl: String,
override val lang: String,
readerEndpoint: String = COMICAKE_DEFAULT_READER_ENDPOINT,
apiEndpoint: String = COMICAKE_DEFAULT_API_ENDPOINT) : HttpSource() {
override val versionId = 1
override val supportsLatest = true
private val readerBase = baseUrl + readerEndpoint
private var apiBase = baseUrl + apiEndpoint
private val userAgent = "Mozilla/5.0 (" +
"Android ${Build.VERSION.RELEASE}; Mobile) " +
"Tachiyomi/${BuildConfig.VERSION_NAME}"
"Android ${Build.VERSION.RELEASE}; Mobile) " +
"Tachiyomi/${BuildConfig.VERSION_NAME}"
override fun headersBuilder() = Headers.Builder().apply {
add("User-Agent", userAgent)
@ -69,6 +72,7 @@ open class ComiCake(override val name: String,
mangas.add(parseComicJson(obj))
}
}
return MangasPage(mangas, !(response.getString("next").isNullOrEmpty() || response.getString("next") == "null"))
}
@ -156,4 +160,9 @@ open class ComiCake(override val name: String,
}
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("This method should not be called!")
companion object {
private const val COMICAKE_DEFAULT_API_ENDPOINT = "/api" // Highly unlikely to change
private const val COMICAKE_DEFAULT_READER_ENDPOINT = "/r" // Can change based on CC config
}
}

View File

@ -4,19 +4,13 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
class ComiCakeFactory : SourceFactory {
override fun createSources(): List<Source> = getAllComiCake()
}
fun getAllComiCake(): List<Source> {
return listOf(
WhimSubs(),
override fun createSources(): List<Source> = listOf(
LetItGoScans(),
PTScans(),
LetItGoScans()
WhimSubs()
)
}
class WhimSubs : ComiCake("WhimSubs", "https://whimsubs.xyz", "en")
class PTScans : ComiCake("ProjectTime Scans", "https://read.ptscans.com", "en", "/")
class LetItGoScans : ComiCake("LetItGo Scans", "https://reader.letitgo.scans.today", "en", "/")
class PTScans : ComiCake("ProjectTime Scans", "https://read.ptscans.com", "en", "/")
class WhimSubs : ComiCake("WhimSubs", "https://whimsubs.xyz", "en")

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: FMReader (multiple aggregators)'
pkgNameSuffix = 'all.fmreader'
extClass = '.FMReaderFactory'
extVersionCode = 2
extVersionCode = 3
libVersion = '1.2'
}

View File

@ -1,17 +1,27 @@
package eu.kanade.tachiyomi.extension.all.fmreader
// For sites based on the Flat-Manga CMS
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.*
import java.util.*
import java.util.Calendar
abstract class FMReader (
/**
* For sites based on the Flat-Manga CMS
*/
abstract class FMReader(
override val name: String,
override val baseUrl: String,
override val lang: String
@ -43,10 +53,8 @@ abstract class FMReader (
}
is TextField -> url.addQueryParameter(filter.key, filter.state)
is GenreList -> {
var genre = String()
var ungenre = String()
filter.state.forEach {
if (it.isIncluded()) genre += ",${it.name}"
if (it.isExcluded()) ungenre += ",${it.name}"
@ -79,7 +87,7 @@ abstract class FMReader (
val mangas = mutableListOf<SManga>()
var hasNextPage = true
document.select(popularMangaSelector()).map{ mangas.add(popularMangaFromElement(it)) }
document.select(popularMangaSelector()).map { mangas.add(popularMangaFromElement(it)) }
// check if there's a next page
document.select(popularMangaNextPageSelector()).first().text().split(" ").let {
@ -108,7 +116,7 @@ abstract class FMReader (
manga.setUrlWithoutDomain(it.attr("abs:href"))
manga.title = it.text()
}
manga.thumbnail_url = element.select("img").let{
manga.thumbnail_url = element.select("img").let {
if (it.hasAttr("src")) {
it.attr("abs:src")
} else {
@ -160,11 +168,11 @@ abstract class FMReader (
override fun chapterFromElement(element: Element): SChapter {
val chapter = SChapter.create()
element.select(chapterUrlSelector).first().let{
element.select(chapterUrlSelector).first().let {
chapter.setUrlWithoutDomain(it.attr("abs:href"))
chapter.name = it.text()
}
chapter.date_upload = element.select(chapterTimeSelector).let{ if(it.hasText()) parseChapterDate(it.text()) else 0 }
chapter.date_upload = element.select(chapterTimeSelector).let { if (it.hasText()) parseChapterDate(it.text()) else 0 }
return chapter
}
@ -177,7 +185,7 @@ abstract class FMReader (
private fun parseChapterDate(date: String): Long {
val value = date.split(' ')[dateValueIndex].toInt()
val dateWord = date.split(' ')[dateWordIndex].let{
val dateWord = date.split(' ')[dateWordIndex].let {
if (it.contains("(")) {
it.substringBefore("(")
} else {
@ -227,7 +235,7 @@ abstract class FMReader (
val pages = mutableListOf<Page>()
document.select("img.chapter-img").forEachIndexed { i, img ->
pages.add(Page(i, "", img.attr("abs:data-src").let{ if (it.isNotEmpty()) it else img.attr("abs:src") }))
pages.add(Page(i, "", img.attr("abs:data-src").let { if (it.isNotEmpty()) it else img.attr("abs:src") }))
}
return pages
}

View File

@ -5,9 +5,17 @@ import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.*
import okhttp3.FormBody
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
@ -40,6 +48,7 @@ class FMReaderFactory : SourceFactory {
* most likely the fix is to override popularMangaNextPageSelector() */
class LHTranslation : FMReader("LHTranslation", "https://lhtranslation.net", "en")
class MangaHato : FMReader("MangaHato", "https://mangahato.com", "ja")
class ManhwaScan : FMReader("ManhwaScan", "https://manhwascan.com", "en")
class MangaTiki : FMReader("MangaTiki", "https://mangatiki.com", "ja")
@ -47,16 +56,20 @@ class MangaBone : FMReader("MangaBone", "https://mangabone.com", "en")
class YoloManga : FMReader("Yolo Manga", "https://yolomanga.ca", "es") {
override fun chapterListSelector() = "div#tab-chapper ~ div#tab-chapper table tr"
}
class MangaLeer : FMReader("MangaLeer", "https://mangaleer.com", "es") {
override val dateValueIndex = 1
override val dateWordIndex = 2
}
class AiLoveManga : FMReader("AiLoveManga", "https://ailovemanga.com", "vi") {
override val requestPath = "danh-sach-truyen.html"
// TODO: could add a genre search (different URL paths for genres)
override fun getFilterList() = FilterList()
// I don't know why, but I have to override searchMangaRequest to make it work for this source
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = GET("$baseUrl/$requestPath?name=$query&page=$page")
override fun chapterListSelector() = "div#tab-chapper table tr"
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
@ -72,10 +85,12 @@ class AiLoveManga : FMReader("AiLoveManga", "https://ailovemanga.com", "vi") {
return manga
}
}
class ReadComicOnlineOrg : FMReader("ReadComicOnline.org", "https://readcomiconline.org", "en") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addInterceptor { requestIntercept(it) }
.build()
private fun requestIntercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
@ -85,46 +100,55 @@ class ReadComicOnlineOrg : FMReader("ReadComicOnline.org", "https://readcomiconl
.add("dqh_firewall", "%2F")
.build()
val cookie = mutableListOf<String>()
response.headers("set-cookie").map{ cookie.add(it.substringBefore(" ")) }
response.headers("set-cookie").map { cookie.add(it.substringBefore(" ")) }
headers.newBuilder().add("Cookie", cookie.joinToString { " " }).build()
client.newCall(POST(request.url().toString(), headers, body)).execute()
} else {
response
}
}
override val requestPath = "comic-list.html"
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
document.select("div#divImage > select:first-of-type option").forEachIndexed{ i, imgPage ->
document.select("div#divImage > select:first-of-type option").forEachIndexed { i, imgPage ->
pages.add(Page(i, imgPage.attr("value"), ""))
}
return pages.dropLast(1) // last page is a comments page
}
override fun imageUrlRequest(page: Page): Request = GET(baseUrl + page.url, headers)
override fun imageUrlParse(document: Document): String = document.select("img.chapter-img").attr("abs:src").trim()
override fun getGenreList() = getComicsGenreList()
}
class MangaWeek : FMReader("MangaWeek", "https://mangaweek.com", "en")
class HanaScan : FMReader("HanaScan (RawQQ)", "http://rawqq.com", "ja") {
override fun popularMangaNextPageSelector() = "div.col-md-8 button"
}
class RawLH : FMReader("RawLH", "https://lhscan.net", "ja") {
override fun popularMangaNextPageSelector() = "div.col-md-8 button"
}
class Manhwa18 : FMReader("Manhwa18", "https://manhwa18.com", "en") {
override fun getGenreList() = getAdultGenreList()
}
class TruyenTranhLH : FMReader("TruyenTranhLH", "https://truyentranhlh.net", "vi") {
override val requestPath = "danh-sach-truyen.html"
}
class EighteenLHPlus : FMReader("18LHPlus", "https://18lhplus.com", "en") {
override fun getGenreList() = getAdultGenreList()
}
class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
override fun popularMangaNextPageSelector() = "div.btn-group:not(div.btn-block) button.btn-info"
// TODO: genre search possible but a bit of a pain
override fun getFilterList() = FilterList()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/arama.html?icerik=$query", headers)
override fun searchMangaParse(response: Response): MangasPage {
val mangas = mutableListOf<SManga>()
@ -135,6 +159,7 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
return MangasPage(mangas, false)
}
override fun searchMangaFromElement(element: Element): SManga {
val manga = SManga.create()
@ -143,6 +168,7 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
return manga
}
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
val infoElement = document.select("div#tab1").first()
@ -156,6 +182,7 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
return manga
}
override fun chapterListSelector() = "tr.table-bordered"
override val chapterUrlSelector = "td[align=left] > a"
override val chapterTimeSelector = "td[align=right]"
@ -172,6 +199,7 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
Observable.error(Exception("Licensed - No chapters to show"))
}
}
private fun chapterListParse(response: Response, requestUrl: String): List<SChapter> {
val chapters = mutableListOf<SChapter>()
var document = response.asJsoup()
@ -180,7 +208,7 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
// chapters are paginated
while (moreChapters) {
document.select(chapterListSelector()).map{ chapters.add(chapterFromElement(it)) }
document.select(chapterListSelector()).map { chapters.add(chapterFromElement(it)) }
if (document.select("a[data-page=$nextPage]").isNotEmpty()) {
val body = FormBody.Builder()
.add("page", nextPage.toString())
@ -193,21 +221,25 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") {
}
return chapters
}
override fun pageListRequest(chapter: SChapter): Request = GET("$baseUrl/${chapter.url.substringAfter("cek/")}", headers)
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
document.select("div.chapter-content select:first-of-type option").forEachIndexed{ i, imgPage ->
document.select("div.chapter-content select:first-of-type option").forEachIndexed { i, imgPage ->
pages.add(Page(i, "$baseUrl/${imgPage.attr("value")}", ""))
}
return pages.dropLast(1) // last page is a comments page
}
override fun imageUrlParse(document: Document): String = document.select("img.chapter-img").attr("abs:src").trim()
}
class Comicastle : FMReader("Comicastle", "https://www.comicastle.org", "en") {
override val requestPath = "comic-dir"
// this source doesn't have the "page x of y" element
override fun popularMangaNextPageSelector() = "li:contains(»)"
override fun popularMangaParse(response: Response) = defaultMangaParse(response)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/comic-dir?q=$query", headers)
override fun searchMangaParse(response: Response): MangasPage = defaultMangaParse(response)
@ -224,39 +256,43 @@ class Comicastle : FMReader("Comicastle", "https://www.comicastle.org", "en") {
return manga
}
override fun chapterListSelector() = "div.col-md-9 table:last-of-type tr"
override fun chapterListParse(response: Response): List<SChapter> = super.chapterListParse(response).reversed()
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
document.select("div.text-center select option").forEachIndexed{ i, imgPage ->
document.select("div.text-center select option").forEachIndexed { i, imgPage ->
pages.add(Page(i, imgPage.attr("value"), ""))
}
return pages
}
override fun imageUrlParse(document: Document): String = document.select("img.chapter-img").attr("abs:src").trim()
override fun getGenreList() = getComicsGenreList()
}
class Manhwa18Net : FMReader("Manhwa18.net", "https://manhwa18.net", "en") {
override fun popularMangaRequest(page: Int): Request =
GET("$baseUrl/$requestPath?listType=pagination&page=$page&sort=views&sort_type=DESC&ungenre=raw", headers)
override fun latestUpdatesRequest(page: Int): Request =
GET("$baseUrl/$requestPath?listType=pagination&page=$page&sort=last_update&sort_type=DESC&ungenre=raw", headers)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val noRawsUrl = super.searchMangaRequest(page, query, filters).url().newBuilder().addQueryParameter("ungenre", "raw").toString()
return GET(noRawsUrl, headers)
}
override fun getGenreList() = getAdultGenreList()
}
class Manhwa18NetRaw : FMReader("Manhwa18.net Raw", "https://manhwa18.net", "ko") {
override val requestPath = "manga-list-genre-raw.html"
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val onlyRawsUrl = super.searchMangaRequest(page, query, filters).url().newBuilder().addQueryParameter("genre", "raw").toString()
return GET(onlyRawsUrl, headers)
}
override fun getFilterList() = FilterList(super.getFilterList().filterNot { it == GenreList(getGenreList()) })
}

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: FoolSlide (multiple sources)'
pkgNameSuffix = 'all.foolslide'
extClass = '.FoolSlideFactory'
extVersionCode = 28
extVersionCode = 29
libVersion = '1.2'
}

View File

@ -4,7 +4,11 @@ import com.github.salomonbrys.kotson.get
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.FormBody
import okhttp3.Request
@ -13,13 +17,17 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
import java.util.Calendar
import java.util.Date
import java.util.HashSet
import java.util.Locale
open class FoolSlide(override val name: String,
override val baseUrl: String,
override val lang: String,
val urlModifier: String = "") : ParsedHttpSource() {
abstract class FoolSlide(
override val name: String,
override val baseUrl: String,
override val lang: String,
val urlModifier: String = ""
) : ParsedHttpSource() {
protected open val dedupeLatestUpdates = true
@ -81,7 +89,7 @@ open class FoolSlide(override val name: String,
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val form = FormBody.Builder()
.add("search", query)
.add("search", query)
return POST("$baseUrl$urlModifier/search/", headers, form.build())
}
@ -123,8 +131,8 @@ open class FoolSlide(override val name: String,
private fun allowAdult(url: String): Request {
return POST(url, body = FormBody.Builder()
.add("adult", "true")
.build())
.add("adult", "true")
.build())
}
override fun chapterListRequest(manga: SManga) = allowAdult(super.chapterListRequest(manga))
@ -142,7 +150,7 @@ open class FoolSlide(override val name: String,
chapter.setUrlWithoutDomain(urlElement.attr("href"))
chapter.name = urlElement.text()
chapter.date_upload = dateElement.text()?.let { parseChapterDate(it.substringAfter(", ")) }
?: 0
?: 0
return chapter
}
@ -247,8 +255,8 @@ open class FoolSlide(override val name: String,
json.forEach {
// Create dummy element to resolve relative URL
val absUrl = document.createElement("a")
.attr("href", it["url"].asString)
.absUrl("href")
.attr("href", it["url"].asString)
.absUrl("href")
pages.add(Page(pages.size, "", absUrl))
}

View File

@ -12,41 +12,36 @@ import okhttp3.Request
import org.jsoup.nodes.Document
class FoolSlideFactory : SourceFactory {
override fun createSources(): List<Source> = getAllFoolSlide()
}
fun getAllFoolSlide(): List<Source> {
return listOf(
JaminisBox(),
SenseScans(),
KireiCake(),
SilentSky(),
Mangatellers(),
IskultripScans(),
AnataNoMotokare(),
DeathTollScans(),
DKThias(),
WorldThree(),
DokiFansubs(),
YuriIsm(),
AjiaNoScantrad(),
OneTimeScans(),
TsubasaSociety(),
MangaScouts(),
StormInHeaven(),
Lilyreader(),
Russification(),
EvilFlowers(),
AkaiYuhiMunTeam(),
LupiTeam(),
HentaiCafe(),
TheCatScans(),
ZandynoFansub()
override fun createSources(): List<Source> = listOf(
JaminisBox(),
SenseScans(),
KireiCake(),
SilentSky(),
Mangatellers(),
IskultripScans(),
AnataNoMotokare(),
DeathTollScans(),
DKThias(),
WorldThree(),
DokiFansubs(),
YuriIsm(),
AjiaNoScantrad(),
OneTimeScans(),
TsubasaSociety(),
MangaScouts(),
StormInHeaven(),
Lilyreader(),
Russification(),
EvilFlowers(),
AkaiYuhiMunTeam(),
LupiTeam(),
HentaiCafe(),
TheCatScans(),
ZandynoFansub()
)
}
class JaminisBox : FoolSlide("Jaimini's Box", "https://jaiminisbox.com", "en", "/reader") {
override fun pageListParse(document: Document): List<Page> {
val doc = document.toString()
var jsonstr = doc.substringAfter("var pages = ").substringBefore(";")
@ -132,7 +127,6 @@ class LupiTeam : FoolSlide("LupiTeam", "https://lupiteam.net", "it", "/reader")
return manga
}
}
class ZandynoFansub : FoolSlide("Zandy no Fansub", "http://zandynofansub.aishiteru.org", "en", "/reader")

View File

@ -2,7 +2,11 @@ package eu.kanade.tachiyomi.extension.all.foolslide
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Request
import okhttp3.Response
@ -56,11 +60,11 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
// we still need to parse the manga info page
// Example: https://hentai.cafe/aiya-youngest-daughters-circumstances/
override fun chapterListParse(response: Response) = listOf(
SChapter.create().apply {
setUrlWithoutDomain(response.asJsoup().select("[title=Read]").attr("href"))
name = "Chapter"
chapter_number = 1f
}
SChapter.create().apply {
setUrlWithoutDomain(response.asJsoup().select("[title=Read]").attr("href"))
name = "Chapter"
chapter_number = 1f
}
)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
@ -74,9 +78,9 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
if (f.state.isNotBlank()) {
requireNoUrl()
url = "/hc.fyi/artist/${f.state
.trim()
.toLowerCase()
.replace(ARTIST_INVALID_CHAR_REGEX, "-")}/"
.trim()
.toLowerCase()
.replace(ARTIST_INVALID_CHAR_REGEX, "-")}/"
}
}
filters.findInstance<BookFilter>()?.let { f ->
@ -113,79 +117,79 @@ class HentaiCafe : FoolSlide("Hentai Cafe", "https://hentai.cafe", "en", "/manga
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return client.newCall(searchMangaRequest(page, query, filters))
.asObservable().doOnNext { response ->
if (!response.isSuccessful) {
response.close()
// Better error message for invalid artist
if (response.code() == 404
&& !filters.findInstance<ArtistFilter>()?.state.isNullOrBlank())
error("Invalid artist!")
else throw Exception("HTTP error ${response.code()}")
}
}
.map { response ->
searchMangaParse(response)
.asObservable().doOnNext { response ->
if (!response.isSuccessful) {
response.close()
// Better error message for invalid artist
if (response.code() == 404
&& !filters.findInstance<ArtistFilter>()?.state.isNullOrBlank())
error("Invalid artist!")
else throw Exception("HTTP error ${response.code()}")
}
}
.map { response ->
searchMangaParse(response)
}
}
override fun getFilterList() = FilterList(
Filter.Header("Filters cannot be used while searching."),
Filter.Header("Only one filter may be used at a time."),
Filter.Separator(),
ArtistFilter(),
BookFilter(),
TagFilter()
Filter.Header("Filters cannot be used while searching."),
Filter.Header("Only one filter may be used at a time."),
Filter.Separator(),
ArtistFilter(),
BookFilter(),
TagFilter()
)
class ArtistFilter : Filter.Text("Artist (must be exact match)")
class BookFilter : Filter.CheckBox("Show books only", false)
class TagFilter : Filter.Select<Tag>("Tag", arrayOf(
Tag("", "<select>"),
Tag("ahegao", "Ahegao"),
Tag("anal", "Anal"),
Tag("big-ass", "Big ass"),
Tag("big-breast", "Big breast"),
Tag("big-dick", "Big dick"),
Tag("bondage", "Bondage"),
Tag("cheating", "Cheating"),
Tag("chubby", "Chubby"),
Tag("color", "Color"),
Tag("condom", "Condom"),
Tag("cosplay", "Cosplay"),
Tag("cunnilingus", "Cunnilingus"),
Tag("dark-skin", "Dark skin"),
Tag("exhibitionism", "Exhibitionism"),
Tag("fellatio", "Fellatio"),
Tag("femdom", "Femdom"),
Tag("flat-chest", "Flat chest"),
Tag("full-color", "Full color"),
Tag("glasses", "Glasses"),
Tag("group", "Group"),
Tag("hairy", "Hairy"),
Tag("handjob", "Handjob"),
Tag("heart-pupils", "Heart pupils"),
Tag("housewife", "Housewife"),
Tag("incest", "Incest"),
Tag("lingerie", "Lingerie"),
Tag("loli", "Loli"),
Tag("masturbation", "Masturbation"),
Tag("nakadashi", "Nakadashi"),
Tag("osananajimi", "Osananajimi"),
Tag("paizuri", "Paizuri"),
Tag("pettanko", "Pettanko"),
Tag("rape", "Rape"),
Tag("schoolgirl", "Schoolgirl"),
Tag("sex-toys", "Sex toys"),
Tag("shota", "Shota"),
Tag("socks", "Socks"),
Tag("stocking", "Stocking"),
Tag("stockings", "Stockings"),
Tag("swimsuit", "Swimsuit"),
Tag("teacher", "Teacher"),
Tag("tsundere", "Tsundere"),
Tag("uncensored", "Uncensored"),
Tag("vanilla", "Vanilla"),
Tag("x-ray", "X-Ray")
Tag("", "<select>"),
Tag("ahegao", "Ahegao"),
Tag("anal", "Anal"),
Tag("big-ass", "Big ass"),
Tag("big-breast", "Big breast"),
Tag("big-dick", "Big dick"),
Tag("bondage", "Bondage"),
Tag("cheating", "Cheating"),
Tag("chubby", "Chubby"),
Tag("color", "Color"),
Tag("condom", "Condom"),
Tag("cosplay", "Cosplay"),
Tag("cunnilingus", "Cunnilingus"),
Tag("dark-skin", "Dark skin"),
Tag("exhibitionism", "Exhibitionism"),
Tag("fellatio", "Fellatio"),
Tag("femdom", "Femdom"),
Tag("flat-chest", "Flat chest"),
Tag("full-color", "Full color"),
Tag("glasses", "Glasses"),
Tag("group", "Group"),
Tag("hairy", "Hairy"),
Tag("handjob", "Handjob"),
Tag("heart-pupils", "Heart pupils"),
Tag("housewife", "Housewife"),
Tag("incest", "Incest"),
Tag("lingerie", "Lingerie"),
Tag("loli", "Loli"),
Tag("masturbation", "Masturbation"),
Tag("nakadashi", "Nakadashi"),
Tag("osananajimi", "Osananajimi"),
Tag("paizuri", "Paizuri"),
Tag("pettanko", "Pettanko"),
Tag("rape", "Rape"),
Tag("schoolgirl", "Schoolgirl"),
Tag("sex-toys", "Sex toys"),
Tag("shota", "Shota"),
Tag("socks", "Socks"),
Tag("stocking", "Stocking"),
Tag("stockings", "Stockings"),
Tag("swimsuit", "Swimsuit"),
Tag("teacher", "Teacher"),
Tag("tsundere", "Tsundere"),
Tag("uncensored", "Uncensored"),
Tag("vanilla", "Vanilla"),
Tag("x-ray", "X-Ray")
))
class Tag(val name: String, private val displayName: String) {

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: Genkan (multiple sources)'
pkgNameSuffix = 'all.genkan'
extClass = '.GenkanFactory'
extVersionCode = 7
extVersionCode = 8
libVersion = '1.2'
}

View File

@ -1,7 +1,11 @@
package eu.kanade.tachiyomi.extension.all.genkan
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
@ -11,12 +15,13 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.select.Elements
import java.text.SimpleDateFormat
import java.util.*
import java.util.Calendar
import java.util.Locale
abstract class Genkan(
override val name: String,
override val baseUrl: String,
override val lang: String
override val name: String,
override val baseUrl: String,
override val lang: String
) : ParsedHttpSource() {
override val supportsLatest = true
@ -88,7 +93,7 @@ abstract class Genkan(
private fun styleToUrl(element: Element): String {
return element.attr("style").substringAfter("(").substringBefore(")")
.let{ if (it.startsWith("http")) it else baseUrl + it }
.let { if (it.startsWith("http")) it else baseUrl + it }
}
override fun mangaDetailsParse(document: Document): SManga {
@ -155,11 +160,11 @@ abstract class Genkan(
val pages = mutableListOf<Page>()
val allImages = document.select("div#pages-container + script").first().data()
.substringAfter("[").substringBefore("];")
.replace(Regex("""["\\]"""), "")
.split(",")
.substringAfter("[").substringBefore("];")
.replace(Regex("""["\\]"""), "")
.split(",")
for (i in 0 until allImages.size) {
for (i in allImages.indices) {
pages.add(Page(i, "", allImages[i]))
}
@ -178,9 +183,9 @@ abstract class Genkan(
// For sites using the older Genkan CMS that didn't have a search function
abstract class GenkanOriginal(
override val name: String,
override val baseUrl: String,
override val lang: String
override val name: String,
override val baseUrl: String,
override val lang: String
) : Genkan(name, baseUrl, lang) {
private var searchQuery = ""
@ -223,7 +228,7 @@ abstract class GenkanOriginal(
// search additional pages if called
private fun searchMorePages(): MutableList<SManga> {
searchPage++
val nextPage = client.newCall(popularMangaRequest(searchPage)).execute().asJsoup()
val nextPage = client.newCall(popularMangaRequest(searchPage)).execute().asJsoup()
val searchMatches = mutableListOf<SManga>()
searchMatches.addAll(getMatchesFrom(nextPage))
nextPageSelectorElement = nextPage.select(searchMangaNextPageSelector())
@ -238,4 +243,3 @@ abstract class GenkanOriginal(
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
}

View File

@ -5,14 +5,14 @@ import eu.kanade.tachiyomi.source.SourceFactory
class GenkanFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(
LeviatanScans(),
LeviatanScansES(),
PsychoPlay(),
OneShotScans(),
KaguyaDex(),
KomiScans(),
HunlightScans(),
WoweScans()
LeviatanScans(),
LeviatanScansES(),
PsychoPlay(),
OneShotScans(),
KaguyaDex(),
KomiScans(),
HunlightScans(),
WoweScans()
)
}

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: Madara (multiple sources)'
pkgNameSuffix = "all.madara"
extClass = '.MadaraFactory'
extVersionCode = 22
extVersionCode = 23
libVersion = '1.2'
}

View File

@ -3,31 +3,43 @@ package eu.kanade.tachiyomi.extension.all.madara
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.*
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import okhttp3.CacheControl
import okhttp3.FormBody
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
import java.util.concurrent.TimeUnit
open class Madara(
override val name: String,
override val baseUrl: String,
override val lang: String,
private val dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US)
abstract class Madara(
override val name: String,
override val baseUrl: String,
override val lang: String,
private val dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US)
) : ParsedHttpSource() {
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
override val supportsLatest = true
// Popular Manga
override fun popularMangaSelector() = "div.page-item-detail"
@ -42,7 +54,7 @@ open class Madara(
}
select("img").first()?.let {
manga.thumbnail_url = it.absUrl(if(it.hasAttr("data-src")) "data-src" else "src")
manga.thumbnail_url = it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src")
}
}
@ -52,7 +64,7 @@ open class Madara(
override fun popularMangaRequest(page: Int): Request {
val form = FormBody.Builder().apply {
add("action", "madara_load_more")
add("page", (page-1).toString())
add("page", (page - 1).toString())
add("template", "madara-core/content/content-archive")
add("vars[orderby]", "meta_value_num")
add("vars[paged]", "1")
@ -81,7 +93,7 @@ open class Madara(
override fun latestUpdatesRequest(page: Int): Request {
val form = FormBody.Builder().apply {
add("action", "madara_load_more")
add("page", (page-1).toString())
add("page", (page - 1).toString())
add("template", "madara-core/content/content-archive")
add("vars[orderby]", "meta_value_num")
add("vars[paged]", "1")
@ -107,7 +119,7 @@ open class Madara(
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return client.newCall(searchMangaRequest(page, query, filters))
.asObservable().doOnNext { response ->
if(!response.isSuccessful) {
if (!response.isSuccessful) {
response.close()
// Error message for exceeding last page
if (response.code() == 404)
@ -129,17 +141,17 @@ open class Madara(
filters.forEach { filter ->
when (filter) {
is AuthorFilter -> {
if(filter.state.isNotBlank()) {
if (filter.state.isNotBlank()) {
url.addQueryParameter("author", filter.state)
}
}
is ArtistFilter -> {
if(filter.state.isNotBlank()) {
if (filter.state.isNotBlank()) {
url.addQueryParameter("artist", filter.state)
}
}
is YearFilter -> {
if(filter.state.isNotBlank()) {
if (filter.state.isNotBlank()) {
url.addQueryParameter("release", filter.state)
}
}
@ -151,7 +163,7 @@ open class Madara(
}
}
is OrderByFilter -> {
if(filter.state != 0) {
if (filter.state != 0) {
url.addQueryParameter("m_orderby", filter.toUriPart())
}
}
@ -165,28 +177,28 @@ open class Madara(
private class YearFilter : Filter.Text("Year of Released")
private class StatusFilter(status: List<Tag>) : Filter.Group<Tag>("Status", status)
private class OrderByFilter : UriPartFilter("Order By", arrayOf(
Pair("<select>", ""),
Pair("Latest", "latest"),
Pair("A-Z", "alphabet"),
Pair("Rating", "rating"),
Pair("Trending", "trending"),
Pair("Most Views", "views"),
Pair("New", "new-manga")
Pair("<select>", ""),
Pair("Latest", "latest"),
Pair("A-Z", "alphabet"),
Pair("Rating", "rating"),
Pair("Trending", "trending"),
Pair("Most Views", "views"),
Pair("New", "new-manga")
))
override fun getFilterList() = FilterList(
AuthorFilter(),
ArtistFilter(),
YearFilter(),
StatusFilter(getStatusList()),
OrderByFilter()
AuthorFilter(),
ArtistFilter(),
YearFilter(),
StatusFilter(getStatusList()),
OrderByFilter()
)
private fun getStatusList() = listOf(
Tag("end" , "Completed"),
Tag("on-going" , "Ongoing"),
Tag("canceled" , "Canceled"),
Tag("on-hold" , "On Hold")
Tag("end", "Completed"),
Tag("on-going", "Ongoing"),
Tag("canceled", "Canceled"),
Tag("on-hold", "On Hold")
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
@ -207,7 +219,7 @@ open class Madara(
manga.title = it.ownText()
}
select("img").first()?.let {
manga.thumbnail_url = it.absUrl(if(it.hasAttr("data-src")) "data-src" else "src")
manga.thumbnail_url = it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src")
}
}
@ -241,10 +253,10 @@ open class Madara(
}
}
select("div.summary_image img").first()?.let {
manga.thumbnail_url = it.absUrl(if(it.hasAttr("data-src")) "data-src" else "src")
manga.thumbnail_url = it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src")
}
select("div.summary-content").last()?.let {
manga.status = when(it.text()) {
manga.status = when (it.text()) {
// I don't know what's the corresponding for COMPLETED and LICENSED
// There's no support for "Canceled" or "On Hold"
"Completed" -> SManga.COMPLETED
@ -271,7 +283,7 @@ open class Madara(
with(element) {
select("a").first()?.let { urlElement ->
chapter.url = urlElement.attr("abs:href").let {
it.substringBefore("?style=paged") + if(!it.endsWith("?style=list")) "?style=list" else ""
it.substringBefore("?style=paged") + if (!it.endsWith("?style=list")) "?style=list" else ""
}
chapter.name = urlElement.text()
}
@ -353,8 +365,8 @@ open class Madara(
override fun pageListParse(document: Document): List<Page> {
return document.select(pageListParseSelector).mapIndexed { index, element ->
Page(index, "", element.select("img").first()?.let{
it.absUrl(if(it.hasAttr("data-src")) "data-src" else "src")
Page(index, "", element.select("img").first()?.let {
it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src")
})
}
}

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.extension.all.madara
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
@ -11,10 +10,10 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.Request
import okhttp3.Response
import java.text.SimpleDateFormat
import java.util.Locale
import okhttp3.Response
import okhttp3.Request
class MadaraFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(
@ -56,11 +55,13 @@ class MadaraFactory : SourceFactory {
class Mangasushi : Madara("Mangasushi", "https://mangasushi.net", "en") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class NinjaScans : Madara("NinjaScans", "https://ninjascans.com", "en")
class ReadManhua : Madara("ReadManhua", "https://readmanhua.net", "en",
dateFormat = SimpleDateFormat("dd MMM yy", Locale.US)) {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class ZeroScans : Madara("ZeroScans", "https://zeroscans.com", "en")
class IsekaiScanCom : Madara("IsekaiScan.com", "https://isekaiscan.com/", "en")
class HappyTeaScans : Madara("Happy Tea Scans", "https://happyteascans.com/", "en")
@ -68,7 +69,8 @@ class JustForFun : Madara("Just For Fun", "https://just-for-fun.ru/", "ru",
dateFormat = SimpleDateFormat("dd/MM/yy", Locale.US)) {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class AoCTranslations : Madara("Agent of Change Translations", "https://aoc.moe/", "en"){
class AoCTranslations : Madara("Agent of Change Translations", "https://aoc.moe/", "en") {
override fun chapterListParse(response: Response): List<SChapter> {
val chapters = mutableListOf<SChapter>()
val document = response.asJsoup()
@ -80,7 +82,7 @@ class AoCTranslations : Madara("Agent of Change Translations", "https://aoc.moe/
} else {
// For their "fancy" volume/chapter lists
document.select("div.wpb_wrapper:contains(volume) a")
.filter { it.attr("href").contains(baseUrl) && !it.attr("href").contains("imgur")}
.filter { it.attr("href").contains(baseUrl) && !it.attr("href").contains("imgur") }
.map { it ->
val chapter = SChapter.create()
if (it.attr("href").contains("volume")) {
@ -99,9 +101,11 @@ class AoCTranslations : Madara("Agent of Change Translations", "https://aoc.moe/
return chapters.reversed()
}
}
class KomikGo : Madara("KomikGo", "https://komikgo.com", "id") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class LuxyScans : Madara("Luxy Scans", "https://luxyscans.com/", "en")
class TritiniaScans : Madara("Tritinia Scans", "http://ghajik.ml/", "en",
dateFormat = SimpleDateFormat("dd/MM/yy", Locale.US)) {
@ -110,25 +114,32 @@ class TritiniaScans : Madara("Tritinia Scans", "http://ghajik.ml/", "en",
override fun latestUpdatesNextPageSelector(): String? = null
override fun popularMangaNextPageSelector(): String? = null
}
class TsubakiNoScan : Madara("Tsubaki No Scan", "https://tsubakinoscan.com/",
"fr", dateFormat = SimpleDateFormat("dd/MM/yy", Locale.US))
class YokaiJump : Madara("Yokai Jump", "https://yokaijump.fr/", "fr",
dateFormat = SimpleDateFormat("dd/MM/yy", Locale.US)) {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class ZManga : Madara("ZManga", "https://zmanga.org/", "es") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class MangazukiMe : Madara("Mangazuki.me", "https://mangazuki.me/", "en")
class MangazukiOnline : Madara("Mangazuki.online", "https://www.mangazuki.online/", "en") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class MangazukiClubJP : Madara("Mangazuki.club", "https://mangazuki.club/", "ja") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class MangazukiClubKO : Madara("Mangazuki.club", "https://mangazuki.club/", "ko") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class FirstKissManga : Madara("1st Kiss", "https://1stkissmanga.com/", "en") {
override val pageListParseSelector = "div.reading-content img"
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
@ -141,12 +152,15 @@ class FirstKissManga : Madara("1st Kiss", "https://1stkissmanga.com/", "en") {
return if (page.imageUrl!!.contains(cdnUrl)) GET(page.imageUrl!!, cdnHeaders) else GET(page.imageUrl!!, headers)
}
}
class MangaKomi : Madara("MangaKomi", "https://mangakomi.com/", "en") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class MangaSY : Madara("Manga SY", "https://www.mangasy.com/", "en") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class ManwhaClub : Madara("Manwha Club", "https://manhwa.club/", "en")
class WuxiaWorld : Madara("WuxiaWorld", "https://wuxiaworld.site/", "en") {
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/tag/webcomic/page/$page/?m_orderby=views", headers)
@ -154,6 +168,7 @@ class WuxiaWorld : Madara("WuxiaWorld", "https://wuxiaworld.site/", "en") {
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = super.searchMangaRequest(page, "$query comics", filters)
override fun popularMangaNextPageSelector() = "div.nav-previous.float-left"
}
class WordRain : Madara("WordRain Translation", "https://wordrain69.com", "en") {
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/manga-genre/manga/page/$page/?m_orderby=views", headers)
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/manga-genre/manga/page/$page/?m_orderby=latest", headers)
@ -167,24 +182,31 @@ class WordRain : Madara("WordRain Translation", "https://wordrain69.com", "en")
return super.searchMangaParse(res)
}
}
class YoManga : Madara("Yo Manga", "https://yomanga.info/", "en") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class ManyToon : Madara("ManyToon", "https://manytoon.com/", "en") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class ChibiManga : Madara("Chibi Manga", "http://www.cmreader.info/", "en") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class ZinManga : Madara("Zin Translator", "https://zinmanga.com/", "en") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class ManwahentaiMe : Madara("Manwahentai.me", "https://manhwahentai.me", "en")
class Manga3asq: Madara("مانجا العاشق", "https://3asq.org", "ar") {
class Manga3asq : Madara("مانجا العاشق", "https://3asq.org", "ar") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class NManhwa: Madara("N Manhwa", "https://nmanhwa.com", "en") {
class NManhwa : Madara("N Manhwa", "https://nmanhwa.com", "en") {
override fun searchMangaNextPageSelector() = "nav.navigation-ajax"
}
class Indiancomicsonline: Madara("Indian Comics Online", "http://www.indiancomicsonline.com", "hi")
class Indiancomicsonline : Madara("Indian Comics Online", "http://www.indiancomicsonline.com", "hi")

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: MangaCards (Valhalla, NANI?)'
pkgNameSuffix = 'all.mangacards'
extClass = '.MangaCardsFactory'
extVersionCode = 4
extVersionCode = 5
libVersion = '1.2'
}

View File

@ -2,19 +2,23 @@ package eu.kanade.tachiyomi.extension.all.mangacards
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.*
import java.util.Locale
abstract class MangaCards (
abstract class MangaCards(
override val name: String,
override val baseUrl: String,
override val lang: String

View File

@ -14,4 +14,3 @@ class MangaCardsFactory : SourceFactory {
class ValhallaScans : MangaCards("Valhalla Scans", "https://valhallascans.com", "en")
class NaniScans : MangaCards("NANI? Scans", "https://naniscans.xyz", "en")
class IneptBastards : MangaCards("Inept Bastards", "https://ineptbastards.xyz", "en")

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: MangaDex'
pkgNameSuffix = 'all.mangadex'
extClass = '.MangadexFactory'
extVersionCode = 68
extVersionCode = 69
libVersion = '1.2'
}

View File

@ -43,7 +43,11 @@ import java.util.Date
import java.util.concurrent.TimeUnit
import kotlin.collections.set
open class Mangadex(override val lang: String, private val internalLang: String, private val langCode: Int) : ConfigurableSource, ParsedHttpSource() {
abstract class Mangadex(
override val lang: String,
private val internalLang: String,
private val langCode: Int
) : ConfigurableSource, ParsedHttpSource() {
override val name = "MangaDex"
@ -60,27 +64,27 @@ open class Mangadex(override val lang: String, private val internalLang: String,
private val rateLimitInterceptor = RateLimitInterceptor(4)
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addNetworkInterceptor(rateLimitInterceptor)
.build()
.addNetworkInterceptor(rateLimitInterceptor)
.build()
private fun clientBuilder(): OkHttpClient = clientBuilder(getShowR18())
private fun clientBuilder(r18Toggle: Int): OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addNetworkInterceptor(rateLimitInterceptor)
.addNetworkInterceptor { chain ->
val originalCookies = chain.request().header("Cookie") ?: ""
val newReq = chain
.request()
.newBuilder()
.header("Cookie", "$originalCookies; ${cookiesHeader(r18Toggle, langCode)}")
.build()
chain.proceed(newReq)
}.build()!!
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addNetworkInterceptor(rateLimitInterceptor)
.addNetworkInterceptor { chain ->
val originalCookies = chain.request().header("Cookie") ?: ""
val newReq = chain
.request()
.newBuilder()
.header("Cookie", "$originalCookies; ${cookiesHeader(r18Toggle, langCode)}")
.build()
chain.proceed(newReq)
}.build()!!
override fun headersBuilder() = Headers.Builder().apply {
add("User-Agent", "Tachiyomi "+ System.getProperty("http.agent"))
add("User-Agent", "Tachiyomi " + System.getProperty("http.agent"))
}
private fun cookiesHeader(r18Toggle: Int, langCode: Int): String {
@ -150,36 +154,36 @@ open class Mangadex(override val lang: String, private val internalLang: String,
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
return clientBuilder().newCall(popularMangaRequest(page))
.asObservableSuccess()
.map { response ->
popularMangaParse(response)
}
.asObservableSuccess()
.map { response ->
popularMangaParse(response)
}
}
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
return clientBuilder().newCall(latestUpdatesRequest(page))
.asObservableSuccess()
.map { response ->
latestUpdatesParse(response)
}
.asObservableSuccess()
.map { response ->
latestUpdatesParse(response)
}
}
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return if (query.startsWith(PREFIX_ID_SEARCH)) {
val realQuery = query.removePrefix(PREFIX_ID_SEARCH)
client.newCall(searchMangaByIdRequest(realQuery))
.asObservableSuccess()
.map { response ->
val details = mangaDetailsParse(response)
details.url = "/manga/$realQuery/"
MangasPage(listOf(details), false)
}
.asObservableSuccess()
.map { response ->
val details = mangaDetailsParse(response)
details.url = "/manga/$realQuery/"
MangasPage(listOf(details), false)
}
} else {
getSearchClient(filters).newCall(searchMangaRequest(page, query, filters))
.asObservableSuccess()
.map { response ->
searchMangaParse(response)
}
.asObservableSuccess()
.map { response ->
searchMangaParse(response)
}
}
}
@ -205,8 +209,8 @@ open class Mangadex(override val lang: String, private val internalLang: String,
// Do traditional search
val url = HttpUrl.parse("$baseUrl/?page=search")!!.newBuilder()
.addQueryParameter("p", page.toString())
.addQueryParameter("title", query.replace(WHITESPACE_REGEX, " "))
.addQueryParameter("p", page.toString())
.addQueryParameter("title", query.replace(WHITESPACE_REGEX, " "))
filters.forEach { filter ->
when (filter) {
@ -323,10 +327,10 @@ open class Mangadex(override val lang: String, private val internalLang: String,
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return clientBuilder().newCall(apiRequest(manga))
.asObservableSuccess()
.map { response ->
mangaDetailsParse(response).apply { initialized = true }
}
.asObservableSuccess()
.map { response ->
mangaDetailsParse(response).apply { initialized = true }
}
}
private fun apiRequest(manga: SManga): Request {
@ -362,14 +366,14 @@ open class Mangadex(override val lang: String, private val internalLang: String,
val finalChapterNumber = getFinalChapter(mangaJson)
if ((status == 2 || status == 3) && chapterJson != null && isMangaCompleted(chapterJson, finalChapterNumber)) {
manga.status = SManga.COMPLETED
} else if (status == 2 && chapterJson != null && isOneshot(chapterJson, finalChapterNumber)){
} else if (status == 2 && chapterJson != null && isOneshot(chapterJson, finalChapterNumber)) {
manga.status = SManga.COMPLETED
} else {
manga.status = parseStatus(status)
}
val genres = (if (mangaJson.get("hentai").int == 1) listOf("Hentai") else listOf()) +
mangaJson.get("genres").asJsonArray.mapNotNull { GENRES[it.toString()] }
mangaJson.get("genres").asJsonArray.mapNotNull { GENRES[it.toString()] }
manga.genre = genres.joinToString(", ")
return manga
@ -395,10 +399,10 @@ open class Mangadex(override val lang: String, private val internalLang: String,
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return clientBuilder().newCall(apiRequest(manga))
.asObservableSuccess()
.map { response ->
chapterListParse(response)
}
.asObservableSuccess()
.map { response ->
chapterListParse(response)
}
}
private fun getFinalChapter(jsonObj: JsonObject): String = jsonObj.get("last_chapter").string.trim()
@ -414,8 +418,8 @@ open class Mangadex(override val lang: String, private val internalLang: String,
private fun isMangaCompleted(chapterJson: JsonObject, finalChapterNumber: String): Boolean {
val count = chapterJson.entrySet()
.filter { it.value.asJsonObject.get("lang_code").string == internalLang }
.filter { doesFinalChapterExist(finalChapterNumber, it.value) }.count()
.filter { it.value.asJsonObject.get("lang_code").string == internalLang }
.filter { doesFinalChapterExist(finalChapterNumber, it.value) }.count()
return count != 0
}
@ -460,7 +464,7 @@ open class Mangadex(override val lang: String, private val internalLang: String,
chapterName.add(chapterJson.get("title").string)
}
//if volume, chapter and title is empty its a oneshot
if(chapterName.isEmpty()){
if (chapterName.isEmpty()) {
chapterName.add("Oneshot")
}
if ((status == 2 || status == 3) && doesFinalChapterExist(finalChapterNumber, chapterJson)) {
@ -611,17 +615,17 @@ open class Mangadex(override val lang: String, private val internalLang: String,
private class R18 : Filter.Select<String>("R18+", arrayOf("Default", "Show all", "Show only", "Show none"))
private fun getDemographic() = listOf(
Tag("1", "Shounen"),
Tag("2", "Shoujo"),
Tag("3", "Seinen"),
Tag("4", "Josei")
Tag("1", "Shounen"),
Tag("2", "Shoujo"),
Tag("3", "Seinen"),
Tag("4", "Josei")
).sortedWith(compareBy { it.name })
private fun getPublicationStatus() = listOf(
Tag("1", "Ongoing"),
Tag("2", "Completed"),
Tag("3", "Cancelled"),
Tag("4", "Hiatus")
Tag("1", "Ongoing"),
Tag("2", "Completed"),
Tag("3", "Cancelled"),
Tag("4", "Hiatus")
).sortedWith(compareBy { it.name })
private class ThemeList(themes: List<Tag>) : Filter.Group<Tag>("Themes", themes)
@ -630,115 +634,115 @@ open class Mangadex(override val lang: String, private val internalLang: String,
// default selection (Rating Descending) matches popularMangaRequest url
class SortFilter : Filter.Sort("Sort",
sortables.map { it.first }.toTypedArray(),
Selection(3, false))
sortables.map { it.first }.toTypedArray(),
Selection(3, false))
private class OriginalLanguage : Filter.Select<String>("Original Language", SOURCE_LANG_LIST.map { it.first }.toTypedArray())
override fun getFilterList() = FilterList(
TextField("Author", "author"),
TextField("Artist", "artist"),
R18(),
SortFilter(),
Demographic(getDemographic()),
PublicationStatus(getPublicationStatus()),
OriginalLanguage(),
ContentList(getContentList()),
FormatList(getFormatList()),
GenreList(getGenreList()),
ThemeList(getThemeList()),
TagInclusionMode(),
TagExclusionMode()
TextField("Author", "author"),
TextField("Artist", "artist"),
R18(),
SortFilter(),
Demographic(getDemographic()),
PublicationStatus(getPublicationStatus()),
OriginalLanguage(),
ContentList(getContentList()),
FormatList(getFormatList()),
GenreList(getGenreList()),
ThemeList(getThemeList()),
TagInclusionMode(),
TagExclusionMode()
)
private fun getContentList() = listOf(
Tag("9", "Ecchi"),
Tag("32", "Smut"),
Tag("49", "Gore"),
Tag("50", "Sexual Violence")
Tag("9", "Ecchi"),
Tag("32", "Smut"),
Tag("49", "Gore"),
Tag("50", "Sexual Violence")
).sortedWith(compareBy { it.name })
private fun getFormatList() = listOf(
Tag("1", "4-koma"),
Tag("4", "Award Winning"),
Tag("7", "Doujinshi"),
Tag("21", "Oneshot"),
Tag("36", "Long Strip"),
Tag("42", "Adaptation"),
Tag("43", "Anthology"),
Tag("44", "Web Comic"),
Tag("45", "Full Color"),
Tag("46", "User Created"),
Tag("47", "Official Colored"),
Tag("48", "Fan Colored")
Tag("1", "4-koma"),
Tag("4", "Award Winning"),
Tag("7", "Doujinshi"),
Tag("21", "Oneshot"),
Tag("36", "Long Strip"),
Tag("42", "Adaptation"),
Tag("43", "Anthology"),
Tag("44", "Web Comic"),
Tag("45", "Full Color"),
Tag("46", "User Created"),
Tag("47", "Official Colored"),
Tag("48", "Fan Colored")
).sortedWith(compareBy { it.name })
private fun getGenreList() = listOf(
Tag("2", "Action"),
Tag("3", "Adventure"),
Tag("5", "Comedy"),
Tag("8", "Drama"),
Tag("10", "Fantasy"),
Tag("13", "Historical"),
Tag("14", "Horror"),
Tag("17", "Mecha"),
Tag("18", "Medical"),
Tag("20", "Mystery"),
Tag("22", "Psychological"),
Tag("23", "Romance"),
Tag("25", "Sci-Fi"),
Tag("28", "Shoujo Ai"),
Tag("30", "Shounen Ai"),
Tag("31", "Slice of Life"),
Tag("33", "Sports"),
Tag("35", "Tragedy"),
Tag("37", "Yaoi"),
Tag("38", "Yuri"),
Tag("41", "Isekai"),
Tag("51", "Crime"),
Tag("52", "Magical Girls"),
Tag("53", "Philosophical"),
Tag("54", "Superhero"),
Tag("55", "Thriller"),
Tag("56", "Wuxia")
Tag("2", "Action"),
Tag("3", "Adventure"),
Tag("5", "Comedy"),
Tag("8", "Drama"),
Tag("10", "Fantasy"),
Tag("13", "Historical"),
Tag("14", "Horror"),
Tag("17", "Mecha"),
Tag("18", "Medical"),
Tag("20", "Mystery"),
Tag("22", "Psychological"),
Tag("23", "Romance"),
Tag("25", "Sci-Fi"),
Tag("28", "Shoujo Ai"),
Tag("30", "Shounen Ai"),
Tag("31", "Slice of Life"),
Tag("33", "Sports"),
Tag("35", "Tragedy"),
Tag("37", "Yaoi"),
Tag("38", "Yuri"),
Tag("41", "Isekai"),
Tag("51", "Crime"),
Tag("52", "Magical Girls"),
Tag("53", "Philosophical"),
Tag("54", "Superhero"),
Tag("55", "Thriller"),
Tag("56", "Wuxia")
).sortedWith(compareBy { it.name })
private fun getThemeList() = listOf(
Tag("6", "Cooking"),
Tag("11", "Gyaru"),
Tag("12", "Harem"),
Tag("16", "Martial Arts"),
Tag("19", "Music"),
Tag("24", "School Life"),
Tag("34", "Supernatural"),
Tag("40", "Video Games"),
Tag("57", "Aliens"),
Tag("58", "Animals"),
Tag("59", "Crossdressing"),
Tag("60", "Demons"),
Tag("61", "Delinquents"),
Tag("62", "Genderswap"),
Tag("63", "Ghosts"),
Tag("64", "Monster Girls"),
Tag("65", "Loli"),
Tag("66", "Magic"),
Tag("67", "Military"),
Tag("68", "Monsters"),
Tag("69", "Ninja"),
Tag("70", "Office Workers"),
Tag("71", "Police"),
Tag("72", "Post-Apocalyptic"),
Tag("73", "Reincarnation"),
Tag("74", "Reverse Harem"),
Tag("75", "Samurai"),
Tag("76", "Shota"),
Tag("77", "Survival"),
Tag("78", "Time Travel"),
Tag("79", "Vampires"),
Tag("80", "Traditional Games"),
Tag("81", "Virtual Reality"),
Tag("82", "Zombies"),
Tag("83", "Incest")
Tag("6", "Cooking"),
Tag("11", "Gyaru"),
Tag("12", "Harem"),
Tag("16", "Martial Arts"),
Tag("19", "Music"),
Tag("24", "School Life"),
Tag("34", "Supernatural"),
Tag("40", "Video Games"),
Tag("57", "Aliens"),
Tag("58", "Animals"),
Tag("59", "Crossdressing"),
Tag("60", "Demons"),
Tag("61", "Delinquents"),
Tag("62", "Genderswap"),
Tag("63", "Ghosts"),
Tag("64", "Monster Girls"),
Tag("65", "Loli"),
Tag("66", "Magic"),
Tag("67", "Military"),
Tag("68", "Monsters"),
Tag("69", "Ninja"),
Tag("70", "Office Workers"),
Tag("71", "Police"),
Tag("72", "Post-Apocalyptic"),
Tag("73", "Reincarnation"),
Tag("74", "Reverse Harem"),
Tag("75", "Samurai"),
Tag("76", "Shota"),
Tag("77", "Survival"),
Tag("78", "Time Travel"),
Tag("79", "Vampires"),
Tag("80", "Traditional Games"),
Tag("81", "Virtual Reality"),
Tag("82", "Zombies"),
Tag("83", "Incest")
).sortedWith(compareBy { it.name })
private val GENRES = (getContentList() + getFormatList() + getGenreList() + getThemeList()).map { it.id to it.name }.toMap()
@ -768,26 +772,26 @@ open class Mangadex(override val lang: String, private val internalLang: String,
const val PREFIX_ID_SEARCH = "id:"
private val sortables = listOf(
Triple("Update date", 0, 1),
Triple("Alphabetically", 2, 3),
Triple("Number of comments", 4, 5),
Triple("Rating", 6, 7),
Triple("Views", 8, 9),
Triple("Follows", 10, 11))
Triple("Update date", 0, 1),
Triple("Alphabetically", 2, 3),
Triple("Number of comments", 4, 5),
Triple("Rating", 6, 7),
Triple("Views", 8, 9),
Triple("Follows", 10, 11))
private val SOURCE_LANG_LIST = listOf(
Pair("All", "0"),
Pair("Japanese", "2"),
Pair("English", "1"),
Pair("Polish", "3"),
Pair("German", "8"),
Pair("French", "10"),
Pair("Vietnamese", "12"),
Pair("Chinese", "21"),
Pair("Indonesian", "27"),
Pair("Korean", "28"),
Pair("Spanish (LATAM)", "29"),
Pair("Thai", "32"),
Pair("Filipino", "34"))
Pair("All", "0"),
Pair("Japanese", "2"),
Pair("English", "1"),
Pair("Polish", "3"),
Pair("German", "8"),
Pair("French", "10"),
Pair("Vietnamese", "12"),
Pair("Chinese", "21"),
Pair("Indonesian", "27"),
Pair("Korean", "28"),
Pair("Spanish (LATAM)", "29"),
Pair("Thai", "32"),
Pair("Filipino", "34"))
}
}

View File

@ -3,7 +3,83 @@ package eu.kanade.tachiyomi.extension.all.mangadex
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
class MangadexFactory : SourceFactory {
override fun createSources(): List<Source> = getAllMangaDexLanguages()
override fun createSources(): List<Source> = listOf(
MangaDexEnglish(),
MangaDexPolish(),
MangaDexItalian(),
MangaDexRussian(),
MangaDexGerman(),
MangaDexFrench(),
MangaDexVietnamese(),
MangaDexSpanishSpain(),
MangaDexSpanishLTAM(),
MangaDexCatalan(),
MangaDexPortuguesePortugal(),
MangaDexPortugueseBrazil(),
MangaDexSwedish(),
MangaDexTurkish(),
MangaDexIndonesian(),
MangaDexHungarian(),
MangaDexBulgarian(),
MangaDexFilipino(),
MangaDexDutch(),
MangaDexArabic(),
MangaDexChineseSimp(),
MangaDexChineseTrad(),
MangaDexThai(),
MangaDexBengali(),
MangaDexBurmese(),
MangaDexCzech(),
MangaDexDanish(),
MangaDexFinnish(),
MangaDexGreek(),
MangaDexJapanese(),
MangaDexKorean(),
MangaDexLithuanian(),
MangaDexMalay(),
MangaDexMongolian(),
MangaDexPersian(),
MangaDexRomanian(),
MangaDexSerboCroatian(),
MangaDexUkrainian()
)
}
class MangaDexPolish : Mangadex("pl", "pl", 3)
class MangaDexItalian : Mangadex("it", "it", 6)
class MangaDexRussian : Mangadex("ru", "ru", 7)
class MangaDexGerman : Mangadex("de", "de", 8)
class MangaDexFrench : Mangadex("fr", "fr", 10)
class MangaDexVietnamese : Mangadex("vi", "vn", 12)
class MangaDexSpanishSpain : Mangadex("es", "es", 15)
class MangaDexSpanishLTAM : Mangadex("es-419", "mx", 29)
class MangaDexCatalan : Mangadex("ca", "ct", 33)
class MangaDexPortuguesePortugal : Mangadex("pt", "pt", 17)
class MangaDexPortugueseBrazil : Mangadex("pt-BR", "br", 16)
class MangaDexSwedish : Mangadex("sv", "se", 18)
class MangaDexTurkish : Mangadex("tr", "tr", 26)
class MangaDexIndonesian : Mangadex("id", "id", 27)
class MangaDexHungarian : Mangadex("hu", "hu", 9)
class MangaDexBulgarian : Mangadex("bg", "bg", 14)
class MangaDexFilipino : Mangadex("fil", "ph", 34)
class MangaDexDutch : Mangadex("nl", "nl", 5)
class MangaDexArabic : Mangadex("ar", "sa", 19)
class MangaDexChineseSimp : Mangadex("zh-Hans", "cn", 21)
class MangaDexChineseTrad : Mangadex("zh-Hant", "hk", 35)
class MangaDexThai : Mangadex("th", "th", 32)
class MangaDexBengali : Mangadex("bn", "bd", 22)
class MangaDexBurmese : Mangadex("my", "mm", 37)
class MangaDexCzech : Mangadex("cs", "cz", 24)
class MangaDexDanish : Mangadex("da", "dk", 20)
class MangaDexFinnish : Mangadex("fi", "fi", 11)
class MangaDexGreek : Mangadex("el", "gr", 13)
class MangaDexJapanese : Mangadex("ja", "jp", 2)
class MangaDexKorean : Mangadex("ko", "kr", 28)
class MangaDexLithuanian : Mangadex("lt", "lt", 38)
class MangaDexMalay : Mangadex("ms", "my", 31)
class MangaDexMongolian : Mangadex("mn", "mn", 25)
class MangaDexPersian : Mangadex("fa", "ir", 30)
class MangaDexRomanian : Mangadex("ro", "ro", 23)
class MangaDexSerboCroatian : Mangadex("sh", "rs", 4)
class MangaDexUkrainian : Mangadex("uk", "ua", 36)

View File

@ -1,84 +0,0 @@
package eu.kanade.tachiyomi.extension.all.mangadex
/**
* Mangadex languages
*/
class MangaDexPolish : Mangadex("pl", "pl", 3)
class MangaDexItalian : Mangadex("it", "it", 6)
class MangaDexRussian : Mangadex("ru", "ru", 7)
class MangaDexGerman : Mangadex("de", "de", 8)
class MangaDexFrench : Mangadex("fr", "fr", 10)
class MangaDexVietnamese : Mangadex("vi", "vn", 12)
class MangaDexSpanishSpain : Mangadex("es", "es", 15)
class MangaDexSpanishLTAM : Mangadex("es-419", "mx", 29)
class MangaDexCatalan : Mangadex("ca", "ct", 33)
class MangaDexPortuguesePortugal : Mangadex("pt", "pt", 17)
class MangaDexPortugueseBrazil : Mangadex("pt-BR", "br", 16)
class MangaDexSwedish : Mangadex("sv", "se", 18)
class MangaDexTurkish : Mangadex("tr", "tr", 26)
class MangaDexIndonesian : Mangadex("id", "id", 27)
class MangaDexHungarian : Mangadex("hu", "hu", 9)
class MangaDexBulgarian : Mangadex("bg", "bg", 14)
class MangaDexFilipino : Mangadex("fil", "ph", 34)
class MangaDexDutch : Mangadex("nl", "nl", 5)
class MangaDexArabic : Mangadex("ar", "sa", 19)
class MangaDexChineseSimp : Mangadex("zh-Hans", "cn", 21)
class MangaDexChineseTrad : Mangadex("zh-Hant", "hk", 35)
class MangaDexThai : Mangadex("th", "th", 32)
class MangaDexBengali : Mangadex("bn", "bd", 22)
class MangaDexBurmese : Mangadex("my", "mm", 37)
class MangaDexCzech : Mangadex("cs", "cz", 24)
class MangaDexDanish : Mangadex("da", "dk", 20)
class MangaDexFinnish : Mangadex("fi", "fi", 11)
class MangaDexGreek : Mangadex("el", "gr", 13)
class MangaDexJapanese : Mangadex("ja", "jp", 2)
class MangaDexKorean : Mangadex("ko", "kr", 28)
class MangaDexLithuanian : Mangadex("lt", "lt", 38)
class MangaDexMalay : Mangadex("ms", "my", 31)
class MangaDexMongolian : Mangadex("mn", "mn", 25)
class MangaDexPersian : Mangadex("fa", "ir", 30)
class MangaDexRomanian : Mangadex("ro", "ro", 23)
class MangaDexSerboCroatian : Mangadex("sh", "rs", 4)
class MangaDexUkrainian : Mangadex("uk", "ua", 36)
fun getAllMangaDexLanguages() = listOf(
MangaDexEnglish(),
MangaDexPolish(),
MangaDexItalian(),
MangaDexRussian(),
MangaDexGerman(),
MangaDexFrench(),
MangaDexVietnamese(),
MangaDexSpanishSpain(),
MangaDexSpanishLTAM(),
MangaDexCatalan(),
MangaDexPortuguesePortugal(),
MangaDexPortugueseBrazil(),
MangaDexSwedish(),
MangaDexTurkish(),
MangaDexIndonesian(),
MangaDexHungarian(),
MangaDexBulgarian(),
MangaDexFilipino(),
MangaDexDutch(),
MangaDexArabic(),
MangaDexChineseSimp(),
MangaDexChineseTrad(),
MangaDexThai(),
MangaDexBengali(),
MangaDexBurmese(),
MangaDexCzech(),
MangaDexDanish(),
MangaDexFinnish(),
MangaDexGreek(),
MangaDexJapanese(),
MangaDexKorean(),
MangaDexLithuanian(),
MangaDexMalay(),
MangaDexMongolian(),
MangaDexPersian(),
MangaDexRomanian(),
MangaDexSerboCroatian(),
MangaDexUkrainian()
)

View File

@ -5,6 +5,7 @@ import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.util.Log
import kotlin.system.exitProcess
/**
* Springboard that accepts https://mangadex.com/title/xxx intents and redirects them to
@ -38,7 +39,7 @@ class MangadexUrlActivity : Activity() {
}
finish()
System.exit(0)
exitProcess(0)
}
}

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: MangAdventure'
pkgNameSuffix = 'all.mangadventure'
extClass = '.MangAdventureFactory'
extVersionCode = 3
extVersionCode = 4
libVersion = '1.2'
}

View File

@ -26,7 +26,7 @@ import java.util.Locale
*
* @property categories the available manga categories of the site.
*/
open class MangAdventure(
abstract class MangAdventure(
override val name: String,
override val baseUrl: String,
val categories: Array<String> = DEFAULT_CATEGORIES

View File

@ -31,7 +31,7 @@ class MangAdventureActivity : Activity() {
}
private fun logInvalidIntent(intent: Intent) {
val msg = "Failed to parse URI from intent"
Log.e("MangAdventureActivity", "$msg $intent")
Log.e("MangAdventureActivity", "Failed to parse URI from intent: $intent")
}
}

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.all.mangadventure
import eu.kanade.tachiyomi.source.SourceFactory
/** [MangAdventure] source factory. */
class MangAdventureFactory : SourceFactory {
override fun createSources() = listOf(
ArcRelight()
@ -11,22 +10,22 @@ class MangAdventureFactory : SourceFactory {
/** Arc-Relight source. */
class ArcRelight : MangAdventure(
"Arc-Relight", "https://arc-relight.com", arrayOf(
"4-Koma",
"Chaos;Head",
"Collection",
"Comedy",
"Drama",
"Jubilee",
"Mystery",
"Psychological",
"Robotics;Notes",
"Romance",
"Sci-Fi",
"Seinen",
"Shounen",
"Steins;Gate",
"Supernatural",
"Tragedy"
)
"4-Koma",
"Chaos;Head",
"Collection",
"Comedy",
"Drama",
"Jubilee",
"Mystery",
"Psychological",
"Robotics;Notes",
"Romance",
"Sci-Fi",
"Seinen",
"Shounen",
"Steins;Gate",
"Supernatural",
"Tragedy"
)
)
}

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: Mangatensei'
pkgNameSuffix = 'all.mangatensei'
extClass = '.MangatenseiFactory'
extVersionCode = 2
extVersionCode = 3
libVersion = '1.2'
}

View File

@ -1,7 +1,11 @@
package eu.kanade.tachiyomi.extension.all.mangatensei
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
@ -9,7 +13,7 @@ import okhttp3.Request
import org.json.JSONObject
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.util.*
import java.util.Calendar
import java.util.concurrent.TimeUnit
open class Mangatensei(override val lang: String, private val Mtlang: String) : ParsedHttpSource() {
@ -25,7 +29,7 @@ open class Mangatensei(override val lang: String, private val Mtlang: String) :
.readTimeout(30, TimeUnit.SECONDS)
.build()
override fun latestUpdatesRequest(page: Int): Request {
override fun latestUpdatesRequest(page: Int): Request {
// The site redirects page 1 -> url-without-page so we do this redirect early for optimization
val builtUrl = "$baseUrl/browse?langs=$Mtlang&sort=update&page=$page"
return GET(builtUrl)
@ -45,7 +49,7 @@ open class Mangatensei(override val lang: String, private val Mtlang: String) :
override fun latestUpdatesNextPageSelector() = "div.browse-pager:contains(order) a.page-link:contains(»)"
override fun popularMangaRequest(page: Int): Request {
override fun popularMangaRequest(page: Int): Request {
val builtUrl = "$baseUrl/browse?langs=$Mtlang&sort=views_w&page=$page"
return GET(builtUrl)
}
@ -64,7 +68,7 @@ open class Mangatensei(override val lang: String, private val Mtlang: String) :
filters.forEach { filter ->
when (filter) {
is AuthorFilter -> {
author = filter.state
author = filter.state
}
is StyleFilter -> {
val styleToInclude = mutableListOf<String>()
@ -96,7 +100,7 @@ open class Mangatensei(override val lang: String, private val Mtlang: String) :
Filter.TriState.STATE_EXCLUDE -> "0"
else -> ""
}
if(status.isNotEmpty()) {
if (status.isNotEmpty()) {
url.addQueryParameter("status", status)
}
}
@ -113,23 +117,23 @@ open class Mangatensei(override val lang: String, private val Mtlang: String) :
}
}
is StarFilter -> {
if(filter.state != 0) {
if (filter.state != 0) {
url.addQueryParameter("stars", filter.toUriPart())
}
}
is ChapterFilter -> {
if(filter.state != 0) {
if (filter.state != 0) {
url.addQueryParameter("chapters", filter.toUriPart())
}
}
is SortBy -> {
if(filter.state != 0) {
if (filter.state != 0) {
url.addQueryParameter("sort", filter.toUriPart())
}
}
}
}
return if(query.isNotBlank() || author!!.isNotBlank()) {
return if (query.isNotBlank() || author!!.isNotBlank()) {
GET("$baseUrl/search?q=$query&a=$author")
} else GET(url.build().toString(), headers)
}
@ -257,7 +261,7 @@ open class Mangatensei(override val lang: String, private val Mtlang: String) :
val imgJson = JSONObject(script)
val imgNames = imgJson.names()
for( i in 0 until imgNames.length()) {
for (i in 0 until imgNames.length()) {
val imgKey = imgNames.getString(i)
val imgUrl = imgJson.getString(imgKey)
pages.add(Page(i, "", imgUrl))
@ -275,111 +279,111 @@ open class Mangatensei(override val lang: String, private val Mtlang: String) :
private class StatusFilter : Filter.TriState("Completed")
private class StarFilter : UriPartFilter("Stars", arrayOf(
Pair("<select>", ""),
Pair("5 Stars", "5"),
Pair("4 Stars", "4"),
Pair("3 Stars", "3"),
Pair("2 Stars", "2"),
Pair("1 Stars", "1")
Pair("<select>", ""),
Pair("5 Stars", "5"),
Pair("4 Stars", "4"),
Pair("3 Stars", "3"),
Pair("2 Stars", "2"),
Pair("1 Stars", "1")
))
private class ChapterFilter : UriPartFilter("Chapters", arrayOf(
Pair("<select>", ""),
Pair("1 ~ 9", "1-9"),
Pair("10 ~ 29", "10-29"),
Pair("30 ~ 99", "30-99"),
Pair("100 ~ 199", "100-199"),
Pair("200+", "200"),
Pair("100+", "100"),
Pair("50+", "50"),
Pair("10+", "10"),
Pair("1+", "1")
Pair("<select>", ""),
Pair("1 ~ 9", "1-9"),
Pair("10 ~ 29", "10-29"),
Pair("30 ~ 99", "30-99"),
Pair("100 ~ 199", "100-199"),
Pair("200+", "200"),
Pair("100+", "100"),
Pair("50+", "50"),
Pair("10+", "10"),
Pair("1+", "1")
))
private class SortBy : UriPartFilter("Sorts By", arrayOf(
Pair("<select>", ""),
Pair("Totally", "views_t"),
Pair("365 days", "views_y"),
Pair("30 days", "views_m"),
Pair("7 days", "views_w"),
Pair("24 hours", "views_d"),
Pair("60 minutes", "views_h"),
Pair("A-Z", "title"),
Pair("Update time", "update"),
Pair("Add time", "create")
Pair("<select>", ""),
Pair("Totally", "views_t"),
Pair("365 days", "views_y"),
Pair("30 days", "views_m"),
Pair("7 days", "views_w"),
Pair("24 hours", "views_d"),
Pair("60 minutes", "views_h"),
Pair("A-Z", "title"),
Pair("Update time", "update"),
Pair("Add time", "create")
))
override fun getFilterList() = FilterList(
Filter.Header("NOTE: Ignored if using text search!"),
AuthorFilter(),
Filter.Separator(),
StatusFilter(),
StarFilter(),
ChapterFilter(),
SortBy(),
StyleFilter(getStyleList()),
DemographicFilter(getDemographicList()),
GenreFilter(getGenreList())
Filter.Header("NOTE: Ignored if using text search!"),
AuthorFilter(),
Filter.Separator(),
StatusFilter(),
StarFilter(),
ChapterFilter(),
SortBy(),
StyleFilter(getStyleList()),
DemographicFilter(getDemographicList()),
GenreFilter(getGenreList())
)
private fun getStyleList() = listOf(
Tag("manga"),
Tag("manhwa"),
Tag("manhua"),
Tag("webtoon")
Tag("manga"),
Tag("manhwa"),
Tag("manhua"),
Tag("webtoon")
)
private fun getDemographicList() = listOf(
Tag("josei"),
Tag("seinen"),
Tag("shoujo"),
Tag("shoujo ai"),
Tag("shounen"),
Tag("shounen ai"),
Tag("yaoi"),
Tag("yuri")
Tag("josei"),
Tag("seinen"),
Tag("shoujo"),
Tag("shoujo ai"),
Tag("shounen"),
Tag("shounen ai"),
Tag("yaoi"),
Tag("yuri")
)
private fun getGenreList() = listOf(
Tag("action"),
Tag("adventure"),
Tag("award winning"),
Tag("comedy"),
Tag("cooking"),
Tag("demons"),
Tag("doujinshi"),
Tag("drama"),
Tag("ecchi"),
Tag("fantasy"),
Tag("gender bender"),
Tag("harem"),
Tag("historical"),
Tag("horror"),
Tag("isekai"),
Tag("magic"),
Tag("martial arts"),
Tag("mature"),
Tag("mecha"),
Tag("medical"),
Tag("military"),
Tag("music"),
Tag("mystery"),
Tag("one shot"),
Tag("psychological"),
Tag("reverse harem"),
Tag("romance"),
Tag("school life"),
Tag("sci fi"),
Tag("shotacon"),
Tag("slice of life"),
Tag("smut"),
Tag("sports"),
Tag("super power"),
Tag("supernatural"),
Tag("tragedy"),
Tag("uncategorized"),
Tag("vampire"),
Tag("youkai")
Tag("action"),
Tag("adventure"),
Tag("award winning"),
Tag("comedy"),
Tag("cooking"),
Tag("demons"),
Tag("doujinshi"),
Tag("drama"),
Tag("ecchi"),
Tag("fantasy"),
Tag("gender bender"),
Tag("harem"),
Tag("historical"),
Tag("horror"),
Tag("isekai"),
Tag("magic"),
Tag("martial arts"),
Tag("mature"),
Tag("mecha"),
Tag("medical"),
Tag("military"),
Tag("music"),
Tag("mystery"),
Tag("one shot"),
Tag("psychological"),
Tag("reverse harem"),
Tag("romance"),
Tag("school life"),
Tag("sci fi"),
Tag("shotacon"),
Tag("slice of life"),
Tag("smut"),
Tag("sports"),
Tag("super power"),
Tag("supernatural"),
Tag("tragedy"),
Tag("uncategorized"),
Tag("vampire"),
Tag("youkai")
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :

View File

@ -3,7 +3,54 @@ package eu.kanade.tachiyomi.extension.all.mangatensei
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
class MangatenseiFactory : SourceFactory {
override fun createSources(): List<Source> = getAllMangatenseiLanguages()
}
override fun createSources(): List<Source> = listOf(
MangatenseiArabic(),
MangatenseiBrazilian(),
MangatenseiCzech(),
MangatenseiDanish(),
MangatenseiDutch(),
MangatenseiEnglish(),
MangatenseiFilipino(),
MangatenseiFrench(),
MangatenseiGerman(),
MangatenseiGreek(),
MangatenseiHebrew(),
MangatenseiHungarian(),
MangatenseiIndonesian(),
MangatenseiItalian(),
MangatenseiMalay(),
MangatenseiPolish(),
MangatenseiPortuguese(),
MangatenseiRomanian(),
MangatenseiRussian(),
MangatenseiSpanish(),
MangatenseiThai(),
MangatenseiTurkish(),
MangatenseiVietnamese()
)
}
class MangatenseiArabic : Mangatensei("ar", "arabic")
class MangatenseiBrazilian : Mangatensei("pt-BR", "brazilian")
class MangatenseiCzech : Mangatensei("cs", "czech")
class MangatenseiDanish : Mangatensei("da", "danish")
class MangatenseiDutch : Mangatensei("nl", "dutch")
class MangatenseiEnglish : Mangatensei("en", "english")
class MangatenseiFilipino : Mangatensei("fil", "filipino")
class MangatenseiFrench : Mangatensei("fr", "french")
class MangatenseiGerman : Mangatensei("de", "german")
class MangatenseiGreek : Mangatensei("el", "greek")
class MangatenseiHebrew : Mangatensei("iw", "hebrew")
class MangatenseiHungarian : Mangatensei("hu", "hungarian")
class MangatenseiIndonesian : Mangatensei("id", "indonesian")
class MangatenseiItalian : Mangatensei("it", "italian")
class MangatenseiMalay : Mangatensei("ms", "malay")
class MangatenseiPolish : Mangatensei("pl", "polish")
class MangatenseiPortuguese : Mangatensei("pt", "portuguese")
class MangatenseiRomanian : Mangatensei("ro", "romanian")
class MangatenseiRussian : Mangatensei("ru", "russian")
class MangatenseiSpanish : Mangatensei("es", "spanish")
class MangatenseiThai : Mangatensei("th", "thai")
class MangatenseiTurkish : Mangatensei("tr", "turkish")
class MangatenseiVietnamese : Mangatensei("vi", "vietnamese")

View File

@ -1,55 +0,0 @@
package eu.kanade.tachiyomi.extension.all.mangatensei
/**
* Mangatensei languages
*/
class MangatenseiArabic : Mangatensei("ar", "arabic")
class MangatenseiBrazilian : Mangatensei("pt-BR", "brazilian")
class MangatenseiCzech : Mangatensei("cs", "czech")
class MangatenseiDanish : Mangatensei("da", "danish")
class MangatenseiDutch : Mangatensei("nl", "dutch")
class MangatenseiEnglish : Mangatensei("en", "english")
class MangatenseiFilipino : Mangatensei("fil", "filipino")
class MangatenseiFrench : Mangatensei("fr", "french")
class MangatenseiGerman : Mangatensei("de", "german")
class MangatenseiGreek : Mangatensei("el", "greek")
class MangatenseiHebrew : Mangatensei("iw", "hebrew")
class MangatenseiHungarian : Mangatensei("hu", "hungarian")
class MangatenseiIndonesian : Mangatensei("id", "indonesian")
class MangatenseiItalian : Mangatensei("it", "italian")
class MangatenseiMalay : Mangatensei("ms", "malay")
class MangatenseiPolish : Mangatensei("pl", "polish")
class MangatenseiPortuguese : Mangatensei("pt", "portuguese")
class MangatenseiRomanian : Mangatensei("ro", "romanian")
class MangatenseiRussian : Mangatensei("ru", "russian")
class MangatenseiSpanish : Mangatensei("es", "spanish")
class MangatenseiThai : Mangatensei("th", "thai")
class MangatenseiTurkish : Mangatensei("tr", "turkish")
class MangatenseiVietnamese : Mangatensei("vi", "vietnamese")
fun getAllMangatenseiLanguages() = listOf(
MangatenseiArabic(),
MangatenseiBrazilian(),
MangatenseiCzech(),
MangatenseiDanish(),
MangatenseiDutch(),
MangatenseiEnglish(),
MangatenseiFilipino(),
MangatenseiFrench(),
MangatenseiGerman(),
MangatenseiGreek(),
MangatenseiHebrew(),
MangatenseiHungarian(),
MangatenseiIndonesian(),
MangatenseiItalian(),
MangatenseiMalay(),
MangatenseiPolish(),
MangatenseiPortuguese(),
MangatenseiRomanian(),
MangatenseiRussian(),
MangatenseiSpanish(),
MangatenseiThai(),
MangatenseiTurkish(),
MangatenseiVietnamese()
)