diff --git a/src/zh/zerobyw/AndroidManifest.xml b/src/zh/zerobyw/AndroidManifest.xml new file mode 100644 index 000000000..30deb7f79 --- /dev/null +++ b/src/zh/zerobyw/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/src/zh/zerobyw/build.gradle b/src/zh/zerobyw/build.gradle new file mode 100644 index 000000000..9c001f03d --- /dev/null +++ b/src/zh/zerobyw/build.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + extName = 'Zerobyw' + pkgNameSuffix = 'zh.zerobyw' + extClass = '.Zerobyw' + extVersionCode = 1 + libVersion = '1.2' +} + +apply from: "$rootDir/common.gradle" + diff --git a/src/zh/zerobyw/res/mipmap-hdpi/ic_launcher.png b/src/zh/zerobyw/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..45dfa22df Binary files /dev/null and b/src/zh/zerobyw/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/zh/zerobyw/res/mipmap-mdpi/ic_launcher.png b/src/zh/zerobyw/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..b55b8982f Binary files /dev/null and b/src/zh/zerobyw/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/zh/zerobyw/res/mipmap-xhdpi/ic_launcher.png b/src/zh/zerobyw/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..2366796cc Binary files /dev/null and b/src/zh/zerobyw/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/zh/zerobyw/res/mipmap-xxhdpi/ic_launcher.png b/src/zh/zerobyw/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..c05684064 Binary files /dev/null and b/src/zh/zerobyw/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/zh/zerobyw/res/mipmap-xxxhdpi/ic_launcher.png b/src/zh/zerobyw/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..6fc862d06 Binary files /dev/null and b/src/zh/zerobyw/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/zh/zerobyw/res/web_hi_res_512.png b/src/zh/zerobyw/res/web_hi_res_512.png new file mode 100644 index 000000000..969c296c5 Binary files /dev/null and b/src/zh/zerobyw/res/web_hi_res_512.png differ diff --git a/src/zh/zerobyw/src/eu/kanade/tachiyomi/extension/zh/zerobyw/Zerobyw.kt b/src/zh/zerobyw/src/eu/kanade/tachiyomi/extension/zh/zerobyw/Zerobyw.kt new file mode 100644 index 000000000..029b508d7 --- /dev/null +++ b/src/zh/zerobyw/src/eu/kanade/tachiyomi/extension/zh/zerobyw/Zerobyw.kt @@ -0,0 +1,195 @@ +package eu.kanade.tachiyomi.extension.zh.zerobyw + +import android.net.Uri +import eu.kanade.tachiyomi.network.GET +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.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element + +class Zerobyw : ParsedHttpSource() { + override val name: String = "zero搬运网" + override val lang: String = "zh" + override val supportsLatest: Boolean = false + override val baseUrl: String = "https://www.zerobywzio.com" + + // Popular + // Website does not provide popular manga, this is actually latest manga + + override fun popularMangaRequest(page: Int) = GET("$baseUrl/plugin.php?id=jameson_manhua&c=index&a=ku&&page=$page", headers) + override fun popularMangaNextPageSelector(): String? = "div.pg > a.nxt" + override fun popularMangaSelector(): String = "div.uk-card" + override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { + title = getTitle(element.select("p.mt5 > a").text()) + setUrlWithoutDomain(element.select("p.mt5 > a").attr("abs:href")) + thumbnail_url = element.select("img").attr("src") + } + + // Latest + + override fun latestUpdatesRequest(page: Int) = throw Exception("Not used") + override fun latestUpdatesNextPageSelector() = throw Exception("Not used") + override fun latestUpdatesSelector() = throw Exception("Not used") + override fun latestUpdatesFromElement(element: Element) = throw Exception("Not used") + + // Search + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val uri = Uri.parse(baseUrl).buildUpon() + if (query.isNotBlank()) { + uri.appendPath("plugin.php") + .appendQueryParameter("id", "jameson_manhua") + .appendQueryParameter("a", "search") + .appendQueryParameter("c", "index") + .appendQueryParameter("keyword", query) + .appendQueryParameter("page", page.toString()) + } else { + uri.appendPath("plugin.php") + .appendQueryParameter("id", "jameson_manhua") + .appendQueryParameter("c", "index") + .appendQueryParameter("a", "ku") + filters.forEach { + if (it is UriSelectFilterPath && it.toUri().second.isNotEmpty()) + uri.appendQueryParameter(it.toUri().first, it.toUri().second) + } + uri.appendQueryParameter("page", page.toString()) + } + return GET(uri.toString(), headers) + } + + override fun searchMangaNextPageSelector(): String? = "div.pg > a.nxt" + override fun searchMangaSelector(): String = "a.uk-card, div.uk-card" + override fun searchMangaFromElement(element: Element): SManga = SManga.create().apply { + title = getTitle(element.select("p.mt5").text()) + setUrlWithoutDomain(element.select("a").attr("abs:href")) + thumbnail_url = element.select("img").attr("src") + } + + // Details + + override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { + title = getTitle(document.select("li.uk-active > h3.uk-heading-line").text()) + thumbnail_url = document.select("div.uk-width-medium > img").attr("abs:src") + author = document.selectFirst("div.cl > a.uk-label").text().substring(3) + artist = author + genre = document.select("div.cl > a.uk-label, div.cl > span.uk-label").eachText().joinToString(", ") + description = document.select("li > div.uk-alert").html().replace("
", "") + status = when (document.select("div.cl > span.uk-label").last().text()) { + "连载中" -> SManga.ONGOING + "已完结" -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + } + + // Chapters + + override fun chapterListSelector(): String = "div.uk-grid-collapse > div.muludiv" + override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { + setUrlWithoutDomain(element.select("a.uk-button-default").attr("abs:href")) + name = element.select("a.uk-button-default").text() + } + override fun chapterListParse(response: Response): List { + return super.chapterListParse(response).reversed() + } + + // Pages + + override fun pageListParse(document: Document): List = mutableListOf().apply { + val images = document.select("div.uk-text-center > img") + if (images.size == 0) { + var message = document.select("div#messagetext > p") + if (message.size == 0) { + message = document.select("div.uk-alert > p") + } + if (message.size != 0) { + throw Exception(message.text()) + } + } + images.forEach { + add(Page(size, "", it.attr("src"))) + } + } + + override fun imageUrlParse(document: Document): String = throw Exception("Not Used") + + // Filters + + override fun getFilterList() = FilterList( + Filter.Header("如果使用文本搜索"), + Filter.Header("过滤器将被忽略"), + CategoryFilter(), + StatusFilter(), + AttributeFilter() + ) + + private class CategoryFilter : UriSelectFilterPath( + "category_id", + "分类", + arrayOf( + Pair("", "全部"), + Pair("1", "卖肉"), + Pair("15", "战斗"), + Pair("32", "日常"), + Pair("6", "后宫"), + Pair("13", "搞笑"), + Pair("28", "日常"), + Pair("31", "爱情"), + Pair("22", "冒险"), + Pair("23", "奇幻"), + Pair("26", "战斗"), + Pair("29", "体育"), + Pair("34", "机战"), + Pair("35", "职业"), + Pair("36", "汉化组跟上,不再更新") + ) + ) + + private class StatusFilter : UriSelectFilterPath( + "jindu", + "进度", + arrayOf( + Pair("", "全部"), + Pair("0", "连载中"), + Pair("1", "已完结") + ) + ) + + private class AttributeFilter : UriSelectFilterPath( + "shuxing", + "性质", + arrayOf( + Pair("", "全部"), + Pair("一半中文一半生肉", "一半中文一半生肉"), + Pair("全生肉", "全生肉"), + Pair("全中文", "全中文") + ) + ) + + /** + * Class that creates a select filter. Each entry in the dropdown has a name and a display name. + * If an entry is selected it is appended as a query parameter onto the end of the URI. + */ + // vals: + private open class UriSelectFilterPath( + val key: String, + displayName: String, + val vals: Array> + ) : Filter.Select(displayName, vals.map { it.second }.toTypedArray()) { + fun toUri() = Pair(key, vals[state].first) + } + + private fun getTitle(title: String): String { + val result = Regex("【\\d+").find(title) + return if (result != null) { + title.substringBefore(result.value) + } else { + title.substringBefore("【") + } + } +}