diff --git a/src/en/wutopia/build.gradle b/src/en/wutopia/build.gradle new file mode 100644 index 000000000..a156308d4 --- /dev/null +++ b/src/en/wutopia/build.gradle @@ -0,0 +1,17 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + appName = 'Tachiyomi: Wutopia' + pkgNameSuffix = 'en.wutopia' + extClass = '.Wutopia' + extVersionCode = 1 + libVersion = '1.2' +} + +dependencies { + compileOnly 'com.google.code.gson:gson:2.8.2' + compileOnly 'com.github.salomonbrys.kotson:kotson:2.5.0' +} + +apply from: "$rootDir/common.gradle" diff --git a/src/en/wutopia/res/mipmap-hdpi/ic_launcher.png b/src/en/wutopia/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..80f19c03b Binary files /dev/null and b/src/en/wutopia/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/en/wutopia/res/mipmap-mdpi/ic_launcher.png b/src/en/wutopia/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..ed01adf6d Binary files /dev/null and b/src/en/wutopia/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/en/wutopia/res/mipmap-xhdpi/ic_launcher.png b/src/en/wutopia/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..bf2852b7f Binary files /dev/null and b/src/en/wutopia/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/en/wutopia/res/mipmap-xxhdpi/ic_launcher.png b/src/en/wutopia/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..3bea8f853 Binary files /dev/null and b/src/en/wutopia/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/en/wutopia/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/wutopia/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..75db5a774 Binary files /dev/null and b/src/en/wutopia/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/en/wutopia/res/web_hi_res_512.png b/src/en/wutopia/res/web_hi_res_512.png new file mode 100644 index 000000000..68df5c5d0 Binary files /dev/null and b/src/en/wutopia/res/web_hi_res_512.png differ diff --git a/src/en/wutopia/src/eu/kanade/tachiyomi/extension/en/wutopia/Wutopia.kt b/src/en/wutopia/src/eu/kanade/tachiyomi/extension/en/wutopia/Wutopia.kt new file mode 100644 index 000000000..30b64a339 --- /dev/null +++ b/src/en/wutopia/src/eu/kanade/tachiyomi/extension/en/wutopia/Wutopia.kt @@ -0,0 +1,133 @@ +package eu.kanade.tachiyomi.extension.en.wutopia + +import com.github.salomonbrys.kotson.fromJson +import com.github.salomonbrys.kotson.get +import com.google.gson.Gson +import com.google.gson.JsonObject +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.source.model.* +import eu.kanade.tachiyomi.source.online.HttpSource +import okhttp3.OkHttpClient +import okhttp3.Headers +import okhttp3.Request +import okhttp3.Response +import okhttp3.RequestBody + +class Wutopia : HttpSource() { + + override val name = "Wutopia" + + override val baseUrl = "https://www.wutopiacomics.com" + + override val lang = "en" + + override val supportsLatest = true + + override val client: OkHttpClient = network.cloudflareClient + + private val gson = Gson() + + override fun headersBuilder(): Headers.Builder = super.headersBuilder() + .add("Content-Type", "application/x-www-form-urlencoded") + .add("platform", "10") + + // Popular + + override fun popularMangaRequest(page: Int): Request { + val body = RequestBody.create(null, "pageNo=$page&pageSize=15&cartoonTypeId=&isFinish=&payState=&order=0") + return POST("$baseUrl/mobile/cartoon-collection/search-fuzzy", headers, body) + } + + override fun popularMangaParse(response: Response): MangasPage { + val json = gson.fromJson<JsonObject>(response.body()!!.string()) + + val mangas = json["list"].asJsonArray.map { + SManga.create().apply { + title = it["name"].asString + url = it["id"].asString + thumbnail_url = it["picUrlWebp"].asString + } + } + + return MangasPage(mangas, json["hasNext"].asBoolean) + } + + // Latest + + override fun latestUpdatesRequest(page: Int): Request { + val body = RequestBody.create(null, "type=8&pageNo=$page&pageSize=15") + return POST("$baseUrl/mobile/home-page/query", headers, body) + } + + override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response) + + // Search + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val body = RequestBody.create(null, "pageNo=$page&pageSize=15&keyword=$query") + return POST("$baseUrl/mobile/cartoon-collection/search-fuzzy", headers, body) + } + + override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response) + + // Details + + override fun mangaDetailsRequest(manga: SManga): Request { + val body = RequestBody.create(null, "id=${manga.url}&linkId=0") + return POST("$baseUrl/mobile/cartoon-collection/get", headers, body) + } + + override fun mangaDetailsParse(response: Response): SManga { + return gson.fromJson<JsonObject>(response.body()!!.string())["cartoon"].let { json -> + SManga.create().apply { + thumbnail_url = json["acrossPicUrlWebp"].asString + author = json["author"].asString + genre = json["cartoonTypes"].asJsonArray.joinToString { it["name"].asString } + description = json["content"].asString + title = json["name"].asString + status = json["isFinishStr"].asString.toStatus() + } + } + } + + private fun String.toStatus() = when (this) { + "完结" -> SManga.COMPLETED + "连载" -> SManga.ONGOING + else -> SManga.UNKNOWN + } + + // Chapters + + override fun chapterListRequest(manga: SManga): Request { + val body = RequestBody.create(null, "id=${manga.url}&pageSize=99999&pageNo=1&sort=0&linkId=0") + return POST("$baseUrl/mobile/cartoon-collection/list-chapter", headers, body) + } + + override fun chapterListParse(response: Response): List<SChapter> { + return gson.fromJson<JsonObject>(response.body()!!.string())["list"].asJsonArray.map { json -> + SChapter.create().apply { + url = json["id"].asString + name = json["name"].asString.let { if (it.isNotEmpty()) it else "Chapter " + json["chapterIndex"].asString } + date_upload = json["modifyTime"].asLong + } + }.reversed() + } + + // Pages + + override fun pageListRequest(chapter: SChapter): Request { + val body = RequestBody.create(null, "id=${chapter.url}&linkId=0") + return POST("$baseUrl/mobile/chapter/get", headers, body) + } + + override fun pageListParse(response: Response): List<Page> { + return gson.fromJson<JsonObject>(response.body()!!.string())["chapter"]["picList"].asJsonArray.mapIndexed { i, json -> + Page(i, "", json["picUrl"].asString) + } + } + + override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used") + + override fun getFilterList() = FilterList() + +}