diff --git a/src/en/mangaowl/AndroidManifest.xml b/src/en/mangaowl/AndroidManifest.xml deleted file mode 100644 index 30deb7f79..000000000 --- a/src/en/mangaowl/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/en/mangaowl/build.gradle b/src/en/mangaowl/build.gradle deleted file mode 100644 index b3fc8f4fa..000000000 --- a/src/en/mangaowl/build.gradle +++ /dev/null @@ -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" diff --git a/src/en/mangaowl/res/mipmap-hdpi/ic_launcher.png b/src/en/mangaowl/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 2d63f0ab2..000000000 Binary files a/src/en/mangaowl/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/mangaowl/res/mipmap-mdpi/ic_launcher.png b/src/en/mangaowl/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 13b650f39..000000000 Binary files a/src/en/mangaowl/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/mangaowl/res/mipmap-xhdpi/ic_launcher.png b/src/en/mangaowl/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index e412e079f..000000000 Binary files a/src/en/mangaowl/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/mangaowl/res/mipmap-xxhdpi/ic_launcher.png b/src/en/mangaowl/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 644ee754b..000000000 Binary files a/src/en/mangaowl/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/mangaowl/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/mangaowl/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 3ad68eaa0..000000000 Binary files a/src/en/mangaowl/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/mangaowl/res/web_hi_res_512.png b/src/en/mangaowl/res/web_hi_res_512.png deleted file mode 100644 index 1e8a6282b..000000000 Binary files a/src/en/mangaowl/res/web_hi_res_512.png and /dev/null differ diff --git a/src/en/mangaowl/src/eu/kanade/tachiyomi/extension/en/mangaowl/MangaOwl.kt b/src/en/mangaowl/src/eu/kanade/tachiyomi/extension/en/mangaowl/MangaOwl.kt deleted file mode 100644 index e14bbe15b..000000000 --- a/src/en/mangaowl/src/eu/kanade/tachiyomi/extension/en/mangaowl/MangaOwl.kt +++ /dev/null @@ -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 { - 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 { - 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>) : - Filter.Select(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) : Filter.Group("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) : Filter.Group("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") -}