Remove MangaOwl extension (#12281)

MangaOwl has permanently shutdown.
This commit is contained in:
resoue 2022-06-22 21:07:29 +08:00 committed by GitHub
parent 84f3daa01d
commit c5291f58d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 0 additions and 396 deletions

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />

View File

@ -1,11 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'MangaOwl'
pkgNameSuffix = 'en.mangaowl'
extClass = '.MangaOwl'
extVersionCode = 25
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

View File

@ -1,383 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangaowl
import android.util.Base64
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.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.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.net.URLDecoder
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class MangaOwl : ParsedHttpSource() {
override val name = "MangaOwl"
override val baseUrl = "https://mangaowls.com"
override val lang = "en"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.writeTimeout(1, TimeUnit.MINUTES)
.build()
private val trPattern = "window\\['tr'] = '([^']*)';".toRegex(RegexOption.IGNORE_CASE)
private fun getTr(document: Document): String {
val trElement = document.getElementsByTag("script").find { trPattern.find(it.data()) != null } ?: error("tr not found")
val tr = trPattern.find(trElement.data())!!.groups[1]!!.value
return URLDecoder.decode(tr, "utf-8")
}
// Popular
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/popular/$page", headers)
}
override fun popularMangaSelector() = "div.col-md-2"
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
element.select("h6 a").let {
manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.text()
}
manga.thumbnail_url = element.select("div.img-responsive").attr("abs:data-background-image")
return manga
}
override fun popularMangaNextPageSelector() = "div.blog-pagenat-wthree li a:contains(>>)"
// Latest
override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/lastest/$page", headers)
}
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
// Search
// This is necessary because the HTML response does not contain pagination links
override fun searchMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
val mangas = document.select(searchMangaSelector()).map { element ->
searchMangaFromElement(element)
}
// Max manga in 1 page is 36
val hasNextPage = document.select(searchMangaSelector()).size == 36
return MangasPage(mangas, hasNextPage)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/search/$page".toHttpUrl().newBuilder()
url.addQueryParameter("search", query)
filters.forEach { filter ->
when (filter) {
is SearchFieldFilter -> {
val fields = filter.state
.filter { it.state }
.joinToString("") { it.uriPart }
url.addQueryParameter("search_field", fields)
}
is SortFilter -> url.addQueryParameter("sort", filter.toUriPart())
is StatusFilter -> url.addQueryParameter("completed", filter.toUriPart())
is GenreFilter -> {
val genres = filter.state
.filter { it.state }
.joinToString(",") { it.uriPart }
url.addQueryParameter("genres", genres)
}
is MinChapterFilter -> url.addQueryParameter("chapter_from", filter.state)
is MaxChapterFilter -> url.addQueryParameter("chapter_to", filter.state)
}
}
return GET(url.toString(), headers)
}
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = throw UnsupportedOperationException("Not used")
// Manga summary page
override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select("div.single_detail").first()
return SManga.create().apply {
title = infoElement.select("h2").first().ownText()
author = infoElement.select("p.fexi_header_para a.author_link").text()
artist = author
status = parseStatus(infoElement.select("p.fexi_header_para:contains(status)").first().ownText())
genre = infoElement.select("div.col-xs-12.col-md-8.single-right-grid-right > p > a[href*=genres]").joinToString { it.text() }
description = infoElement.select(".description").first().ownText()
thumbnail_url = infoElement.select("img").first()?.let { img ->
if (img.hasAttr("data-src")) img.attr("abs:data-src") else img.attr("abs:src")
}
}
}
private fun parseStatus(status: String?) = when {
status == null -> SManga.UNKNOWN
status.contains("Ongoing") -> SManga.ONGOING
status.contains("Completed") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
// Chapters
// Only selects chapter elements with links, since sometimes chapter lists have unlinked chapters
override fun chapterListSelector() = "div.table-chapter-list ul li:has(a)"
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
TR = getTr(document)
val s = Base64.encodeToString(baseUrl.toByteArray(), Base64.NO_PADDING)
return document.select(chapterListSelector()).map { element ->
SChapter.create().apply {
element.select("a").let {
url = it.attr("data-href")
.toHttpUrl().newBuilder()
.addQueryParameter("tr", TR)
.addQueryParameter("s", s)
.toString()
name = it.select("label").first().text()
}
date_upload = parseChapterDate(element.select("small:last-of-type").text())
}
}
}
override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException("Not used")
companion object {
val dateFormat by lazy {
SimpleDateFormat("MM/dd/yyyy", Locale.US)
}
var TR: String? = null
}
private fun parseChapterDate(string: String): Long {
return try {
dateFormat.parse(string)?.time ?: 0
} catch (_: ParseException) {
0
}
}
// Pages
override fun pageListRequest(chapter: SChapter): Request {
if (TR == null) {
val document = client.newCall(GET(baseUrl)).execute().asJsoup()
TR = getTr(document)
}
val url = chapter.url.toHttpUrl().newBuilder()
.removeAllQueryParameters("tr")
.addQueryParameter("tr", TR)
return GET(url.toString(), headers)
}
override fun pageListParse(document: Document): List<Page> {
return document.select("div.item img.owl-lazy").mapIndexed { i, img ->
Page(i, "", img.attr("abs:data-src"))
}.ifEmpty {
TR = null
listOf()
}
}
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
// Filters
override fun getFilterList() = FilterList(
SearchFieldFilter(getSearchFields()),
SortFilter(),
StatusFilter(),
GenreFilter(getGenreList()),
Filter.Separator(),
Filter.Header("Only works with text search"),
MinChapterFilter(),
MaxChapterFilter()
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
private class SortFilter : UriPartFilter(
"Sort by",
arrayOf(
Pair("Matched", "4"),
Pair("Viewed", "0"),
Pair("Popularity", "1"),
Pair("Create Date", "2"),
Pair("Upload Date", "3")
)
)
private class StatusFilter : UriPartFilter(
"Status",
arrayOf(
Pair("Any", "2"),
Pair("Completed", "1"),
Pair("Ongoing", "0")
)
)
private class Genre(name: String, val uriPart: String) : Filter.CheckBox(name)
private class GenreFilter(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
private fun getGenreList() = listOf(
Genre("4-koma", "89"),
Genre("Action", "1"),
Genre("Adaptation", "72"),
Genre("Adventure", "2"),
Genre("Aliens", "112"),
Genre("All Ages", "122"),
Genre("Animals", "90"),
Genre("Anthology", "101"),
Genre("Award winning", "91"),
Genre("Bara", "116"),
Genre("Cars", "49"),
Genre("Comedy", "15"),
Genre("Comic", "130"),
Genre("Cooking", "63"),
Genre("Crime", "81"),
Genre("Crossdressing", "105"),
Genre("Delinquents", "73"),
Genre("Dementia", "48"),
Genre("Demons", "3"),
Genre("Doujinshi", "55"),
Genre("Drama", "4"),
Genre("Ecchi", "27"),
Genre("Fan colored", "92"),
Genre("Fantasy", "7"),
Genre("Full Color", "82"),
Genre("Game", "33"),
Genre("Gender Bender", "39"),
Genre("Ghosts", "97"),
Genre("Gore", "107"),
Genre("Gossip", "123"),
Genre("Gyaru", "104"),
Genre("Harem", "38"),
Genre("Historical", "12"),
Genre("Horror", "5"),
Genre("Incest", "98"),
Genre("Isekai", "69"),
Genre("Japanese", "129"),
Genre("Josei", "35"),
Genre("Kids", "42"),
Genre("Korean", "128"),
Genre("Long Strip", "76"),
Genre("Mafia", "82"),
Genre("Magic", "34"),
Genre("Magical Girls", "88"),
Genre("Manga", "127"),
Genre("Manhua", "62"),
Genre("Manhwa", "61"),
Genre("Martial Arts", "37"),
Genre("Mature", "60"),
Genre("Mecha", "36"),
Genre("Medical", "66"),
Genre("Military", "8"),
Genre("Monster girls", "95"),
Genre("Monsters", "84"),
Genre("Music", "32"),
Genre("Mystery", "11"),
Genre("Ninja", "93"),
Genre("Novel", "56"),
Genre("NTR", "121"),
Genre("Office", "126"),
Genre("Office Workers", "99"),
Genre("Official colored", "78"),
Genre("One shot", "67"),
Genre("Parody", "30"),
Genre("Philosophical", "100"),
Genre("Police", "46"),
Genre("Post apocalyptic", "94"),
Genre("Psychological", "9"),
Genre("Reincarnation", "74"),
Genre("Reverse harem", "79"),
Genre("Romance", "25"),
Genre("Samurai", "18"),
Genre("School life", "59"),
Genre("Sci-fi", "70"),
Genre("Seinen", "10"),
Genre("Sexual violence", "117"),
Genre("Shoujo", "28"),
Genre("Shoujo Ai", "40"),
Genre("Shounen", "13"),
Genre("Shounen Ai", "44"),
Genre("Slice of Life", "19"),
Genre("Smut", "65"),
Genre("Space", "29"),
Genre("Sports", "22"),
Genre("Super Power", "17"),
Genre("Superhero", "109"),
Genre("Supernatural", "6"),
Genre("Survival", "85"),
Genre("Thriller", "31"),
Genre("Time travel", "80"),
Genre("Toomics", "120"),
Genre("Traditional games", "113"),
Genre("Tragedy", "68"),
Genre("Uncategorized", "50"),
Genre("Uncensored", "124"),
Genre("User created", "102"),
Genre("Vampires", "103"),
Genre("Vanilla", "125"),
Genre("Video games", "75"),
Genre("Villainess", "119"),
Genre("Virtual reality", "110"),
Genre("Web comic", "77"),
Genre("Webtoon", "71"),
Genre("Wuxia", "106"),
Genre("Yaoi", "51"),
Genre("Yuri", "54"),
Genre("Zombies", "108")
)
private class SearchField(name: String, state: Boolean, val uriPart: String) : Filter.CheckBox(name, state)
private class SearchFieldFilter(fields: List<SearchField>) : Filter.Group<SearchField>("Search in", fields)
private fun getSearchFields() = listOf(
SearchField("Manga title", true, "1"),
SearchField("Authors", true, "2"),
SearchField("Description", false, "3")
)
private class MinChapterFilter : Filter.Text("Minimum Chapters")
private class MaxChapterFilter : Filter.Text("Maximum Chapters")
}