remove mangalife & mangasee
|
@ -1,5 +0,0 @@
|
||||||
plugins {
|
|
||||||
id("lib-multisrc")
|
|
||||||
}
|
|
||||||
|
|
||||||
baseVersionCode = 12
|
|
|
@ -1,428 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.multisrc.nepnep
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
|
||||||
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.HttpSource
|
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.json.JsonArray
|
|
||||||
import kotlinx.serialization.json.JsonElement
|
|
||||||
import kotlinx.serialization.json.contentOrNull
|
|
||||||
import kotlinx.serialization.json.jsonArray
|
|
||||||
import kotlinx.serialization.json.jsonObject
|
|
||||||
import kotlinx.serialization.json.jsonPrimitive
|
|
||||||
import okhttp3.Headers
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import org.jsoup.nodes.Document
|
|
||||||
import rx.Observable
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Source responds to requests with their full database as a JsonArray, then sorts/filters it client-side
|
|
||||||
* We'll take the database on first requests, then do what we want with it
|
|
||||||
*/
|
|
||||||
abstract class NepNep(
|
|
||||||
override val name: String,
|
|
||||||
override val baseUrl: String,
|
|
||||||
override val lang: String,
|
|
||||||
) : HttpSource() {
|
|
||||||
|
|
||||||
override val supportsLatest = true
|
|
||||||
|
|
||||||
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
|
||||||
.add("Referer", "$baseUrl/")
|
|
||||||
.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/77.0")
|
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
|
||||||
|
|
||||||
private lateinit var directory: List<JsonElement>
|
|
||||||
|
|
||||||
// Convenience functions to shorten later code
|
|
||||||
/** Returns value corresponding to given key as a string, or null */
|
|
||||||
private fun JsonElement.getString(key: String): String? {
|
|
||||||
return this.jsonObject[key]!!.jsonPrimitive.contentOrNull
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns value corresponding to given key as a JsonArray */
|
|
||||||
private fun JsonElement.getArray(key: String): JsonArray {
|
|
||||||
return this.jsonObject[key]!!.jsonArray
|
|
||||||
}
|
|
||||||
|
|
||||||
// Popular
|
|
||||||
|
|
||||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
|
||||||
return if (page == 1) {
|
|
||||||
client.newCall(popularMangaRequest(page))
|
|
||||||
.asObservableSuccess()
|
|
||||||
.map { response ->
|
|
||||||
popularMangaParse(response)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Observable.just(parseDirectory(page))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun popularMangaRequest(page: Int): Request {
|
|
||||||
return GET("$baseUrl/search/", headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't use ";" for substringBefore() !
|
|
||||||
private fun directoryFromDocument(document: Document): JsonArray {
|
|
||||||
val str = document.select("script:containsData(MainFunction)").first()!!.data()
|
|
||||||
.substringAfter("vm.Directory = ").substringBefore("vm.GetIntValue").trim()
|
|
||||||
.replace(";", " ")
|
|
||||||
return json.parseToJsonElement(str).jsonArray
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun popularMangaParse(response: Response): MangasPage {
|
|
||||||
val document = response.asJsoup()
|
|
||||||
thumbnailUrl = document.select(".SearchResult > .SearchResultCover img").first()!!.attr("ng-src")
|
|
||||||
directory = directoryFromDocument(document).sortedByDescending { it.getString("v") }
|
|
||||||
return parseDirectory(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseDirectory(page: Int): MangasPage {
|
|
||||||
val mangas = mutableListOf<SManga>()
|
|
||||||
val endRange = ((page * 24) - 1).let { if (it <= directory.lastIndex) it else directory.lastIndex }
|
|
||||||
|
|
||||||
for (i in (((page - 1) * 24)..endRange)) {
|
|
||||||
mangas.add(
|
|
||||||
SManga.create().apply {
|
|
||||||
title = directory[i].getString("s")!!
|
|
||||||
url = "/manga/${directory[i].getString("i")}"
|
|
||||||
thumbnail_url = getThumbnailUrl(directory[i].getString("i")!!)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return MangasPage(mangas, endRange < directory.lastIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var thumbnailUrl: String? = null
|
|
||||||
|
|
||||||
private fun getThumbnailUrl(id: String): String {
|
|
||||||
if (thumbnailUrl.isNullOrEmpty()) {
|
|
||||||
val response = client.newCall(popularMangaRequest(1)).execute()
|
|
||||||
thumbnailUrl = response.asJsoup().select(".SearchResult > .SearchResultCover img").first()!!.attr("ng-src")
|
|
||||||
}
|
|
||||||
|
|
||||||
return thumbnailUrl!!.replace("{{Result.i}}", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Latest
|
|
||||||
|
|
||||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
|
||||||
return if (page == 1) {
|
|
||||||
client.newCall(latestUpdatesRequest(page))
|
|
||||||
.asObservableSuccess()
|
|
||||||
.map { response ->
|
|
||||||
latestUpdatesParse(response)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Observable.just(parseDirectory(page))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int): Request = popularMangaRequest(1)
|
|
||||||
|
|
||||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
|
||||||
directory = directoryFromDocument(response.asJsoup()).sortedByDescending { it.getString("lt") }
|
|
||||||
return parseDirectory(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search
|
|
||||||
|
|
||||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
|
||||||
return if (page == 1) {
|
|
||||||
client.newCall(searchMangaRequest(page, query, filters))
|
|
||||||
.asObservableSuccess()
|
|
||||||
.map { response ->
|
|
||||||
searchMangaParse(response, query, filters)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Observable.just(parseDirectory(page))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = popularMangaRequest(1)
|
|
||||||
|
|
||||||
private fun searchMangaParse(response: Response, query: String, filters: FilterList): MangasPage {
|
|
||||||
val trimmedQuery = query.trim()
|
|
||||||
directory = directoryFromDocument(response.asJsoup())
|
|
||||||
.filter {
|
|
||||||
// Comparing query with display name
|
|
||||||
it.getString("s")!!.contains(trimmedQuery, ignoreCase = true) or
|
|
||||||
// Comparing query with list of alternate names
|
|
||||||
it.getArray("al").any { altName ->
|
|
||||||
altName.jsonPrimitive.content.contains(trimmedQuery, ignoreCase = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val genres = mutableListOf<String>()
|
|
||||||
val genresNo = mutableListOf<String>()
|
|
||||||
var sortBy: String
|
|
||||||
for (filter in if (filters.isEmpty()) getFilterList() else filters) {
|
|
||||||
when (filter) {
|
|
||||||
is Sort -> {
|
|
||||||
sortBy = when (filter.state?.index) {
|
|
||||||
1 -> "ls"
|
|
||||||
2 -> "v"
|
|
||||||
else -> "s"
|
|
||||||
}
|
|
||||||
directory = if (filter.state?.ascending != true) {
|
|
||||||
directory.sortedByDescending { it.getString(sortBy) }
|
|
||||||
} else {
|
|
||||||
directory.sortedByDescending { it.getString(sortBy) }.reversed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is SelectField -> if (filter.state != 0) {
|
|
||||||
directory = when (filter.name) {
|
|
||||||
"Scan Status" -> directory.filter { it.getString("ss")!!.contains(filter.values[filter.state], ignoreCase = true) }
|
|
||||||
"Publish Status" -> directory.filter { it.getString("ps")!!.contains(filter.values[filter.state], ignoreCase = true) }
|
|
||||||
"Type" -> directory.filter { it.getString("t")!!.contains(filter.values[filter.state], ignoreCase = true) }
|
|
||||||
"Translation" -> directory.filter { it.getString("o")!!.contains("yes", ignoreCase = true) }
|
|
||||||
else -> directory
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is YearField -> if (filter.state.isNotEmpty()) directory = directory.filter { it.getString("y")!!.contains(filter.state) }
|
|
||||||
is AuthorField -> if (filter.state.isNotEmpty()) {
|
|
||||||
directory = directory.filter { e ->
|
|
||||||
e.getArray("a").any {
|
|
||||||
it.jsonPrimitive.content.contains(filter.state, ignoreCase = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is GenreList -> filter.state.forEach { genre ->
|
|
||||||
when (genre.state) {
|
|
||||||
Filter.TriState.STATE_INCLUDE -> genres.add(genre.name)
|
|
||||||
Filter.TriState.STATE_EXCLUDE -> genresNo.add(genre.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (genres.isNotEmpty()) {
|
|
||||||
genres.map { genre ->
|
|
||||||
directory = directory.filter { e ->
|
|
||||||
e.getArray("g").any { it.jsonPrimitive.content.contains(genre, ignoreCase = true) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (genresNo.isNotEmpty()) {
|
|
||||||
genresNo.map { genre ->
|
|
||||||
directory = directory.filterNot { e ->
|
|
||||||
e.getArray("g").any { it.jsonPrimitive.content.contains(genre, ignoreCase = true) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseDirectory(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response): MangasPage = throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
// Details
|
|
||||||
|
|
||||||
override fun mangaDetailsParse(response: Response): SManga {
|
|
||||||
return response.asJsoup().select("div.BoxBody > div.row").let { info ->
|
|
||||||
SManga.create().apply {
|
|
||||||
title = info.select("h1").text()
|
|
||||||
author = info.select("li.list-group-item:has(span:contains(Author)) a").first()?.text()
|
|
||||||
status = info.select("li.list-group-item:has(span:contains(Status)) a:contains(scan)").text().toStatus()
|
|
||||||
description = info.select("div.Content").text()
|
|
||||||
thumbnail_url = info.select("img").attr("abs:src")
|
|
||||||
|
|
||||||
val genres = info.select("li.list-group-item:has(span:contains(Genre)) a")
|
|
||||||
.map { element -> element.text() }
|
|
||||||
.toMutableSet()
|
|
||||||
|
|
||||||
// add series type(manga/manhwa/manhua/other) thinggy to genre
|
|
||||||
info.select("li.list-group-item:has(span:contains(Type)) a, a[href*=type\\=]").firstOrNull()?.ownText()?.let {
|
|
||||||
if (it.isEmpty().not()) {
|
|
||||||
genres.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
genre = genres.toList().joinToString(", ")
|
|
||||||
|
|
||||||
// add alternative name to manga description
|
|
||||||
val altName = "Alternative Name: "
|
|
||||||
info.select("li.list-group-item:has(span:contains(Alter))").firstOrNull()?.ownText()?.let {
|
|
||||||
if (it.isBlank().not() && it != "N/A") {
|
|
||||||
description = when {
|
|
||||||
description.isNullOrBlank() -> altName + it
|
|
||||||
else -> description + "\n\n$altName" + it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun String.toStatus() = when {
|
|
||||||
this.contains("Ongoing", ignoreCase = true) -> SManga.ONGOING
|
|
||||||
this.contains("Complete", ignoreCase = true) -> SManga.COMPLETED
|
|
||||||
this.contains("Cancelled", ignoreCase = true) -> SManga.CANCELLED
|
|
||||||
this.contains("Hiatus", ignoreCase = true) -> SManga.ON_HIATUS
|
|
||||||
else -> SManga.UNKNOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chapters - Mind special cases like decimal chapters (e.g. One Punch Man) and manga with seasons (e.g. The Gamer)
|
|
||||||
|
|
||||||
private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:SS Z", Locale.getDefault())
|
|
||||||
|
|
||||||
private fun chapterURLEncode(e: String): String {
|
|
||||||
var index = ""
|
|
||||||
val t = e.substring(0, 1).toInt()
|
|
||||||
if (1 != t) { index = "-index-$t" }
|
|
||||||
val dgt = if (e.toInt() < 100100) { 4 } else if (e.toInt() < 101000) { 3 } else if (e.toInt() < 110000) { 2 } else { 1 }
|
|
||||||
val n = e.substring(dgt, e.length - 1)
|
|
||||||
var suffix = ""
|
|
||||||
val path = e.substring(e.length - 1).toInt()
|
|
||||||
if (0 != path) { suffix = ".$path" }
|
|
||||||
return "-chapter-$n$suffix$index.html"
|
|
||||||
}
|
|
||||||
|
|
||||||
private val chapterImageRegex = Regex("""^0+""")
|
|
||||||
|
|
||||||
private fun chapterImage(e: String, cleanString: Boolean = false): String {
|
|
||||||
// cleanString will result in an empty string if chapter number is 0, hence the else if below
|
|
||||||
val a = e.substring(1, e.length - 1).let { if (cleanString) it.replace(chapterImageRegex, "") else it }
|
|
||||||
// If b is not zero, indicates chapter has decimal numbering
|
|
||||||
val b = e.substring(e.length - 1).toInt()
|
|
||||||
return if (b == 0 && a.isNotEmpty()) {
|
|
||||||
a
|
|
||||||
} else if (b == 0 && a.isEmpty()) {
|
|
||||||
"0"
|
|
||||||
} else {
|
|
||||||
"$a.$b"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
|
||||||
val vmChapters = response.asJsoup().select("script:containsData(MainFunction)").first()!!.data()
|
|
||||||
.substringAfter("vm.Chapters = ").substringBefore(";")
|
|
||||||
val array = json.parseToJsonElement(vmChapters).jsonArray
|
|
||||||
val hasDistinctTypes = array.map { it.getString("Type") }.distinct().count() > 1
|
|
||||||
return array.map { json ->
|
|
||||||
val indexChapter = json.getString("Chapter")!!
|
|
||||||
val type = json.getString("Type")
|
|
||||||
SChapter.create().apply {
|
|
||||||
name = json.getString("ChapterName").let { if (it.isNullOrEmpty()) "$type ${chapterImage(indexChapter, true)}" else it }
|
|
||||||
url = "/read-online/" + response.request.url.toString().substringAfter("/manga/") + chapterURLEncode(indexChapter)
|
|
||||||
// only add type info as scanlator if there are differing types among chapter array
|
|
||||||
scanlator = if (hasDistinctTypes) type else null
|
|
||||||
date_upload = try {
|
|
||||||
json.getString("Date").let { dateFormat.parse("$it +0600")?.time } ?: 0
|
|
||||||
} catch (_: Exception) {
|
|
||||||
0L
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pages
|
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
|
||||||
val document = response.asJsoup()
|
|
||||||
val script = document.selectFirst("script:containsData(MainFunction)")?.data()
|
|
||||||
?: client.newCall(GET(document.location().removeSuffix(".html"), headers))
|
|
||||||
.execute().asJsoup().selectFirst("script:containsData(MainFunction)")!!.data()
|
|
||||||
val curChapter = json.parseToJsonElement(script!!.substringAfter("vm.CurChapter = ").substringBefore(";")).jsonObject
|
|
||||||
|
|
||||||
val pageTotal = curChapter.getString("Page")!!.toInt()
|
|
||||||
|
|
||||||
val host = "https://" +
|
|
||||||
script
|
|
||||||
.substringAfter("vm.CurPathName = \"", "")
|
|
||||||
.substringBefore("\"")
|
|
||||||
.also {
|
|
||||||
if (it.isEmpty()) {
|
|
||||||
throw Exception("$name is overloaded and blocking Tachiyomi right now. Wait for unblock.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val titleURI = script.substringAfter("vm.IndexName = \"").substringBefore("\"")
|
|
||||||
val seasonURI = curChapter.getString("Directory")!!
|
|
||||||
.let { if (it.isEmpty()) "" else "$it/" }
|
|
||||||
val path = "$host/manga/$titleURI/$seasonURI"
|
|
||||||
|
|
||||||
val chNum = chapterImage(curChapter.getString("Chapter")!!)
|
|
||||||
|
|
||||||
return IntRange(1, pageTotal).mapIndexed { i, _ ->
|
|
||||||
val imageNum = (i + 1).toString().let { "000$it" }.let { it.substring(it.length - 3) }
|
|
||||||
Page(i, "", "$path$chNum-$imageNum.png")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
|
|
||||||
private class Sort : Filter.Sort("Sort", arrayOf("Alphabetically", "Date updated", "Popularity"), Selection(2, false))
|
|
||||||
private class Genre(name: String) : Filter.TriState(name)
|
|
||||||
private class YearField : Filter.Text("Years")
|
|
||||||
private class AuthorField : Filter.Text("Author")
|
|
||||||
private class SelectField(name: String, values: Array<String>, state: Int = 0) : Filter.Select<String>(name, values, state)
|
|
||||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
|
|
||||||
|
|
||||||
override fun getFilterList() = FilterList(
|
|
||||||
YearField(),
|
|
||||||
AuthorField(),
|
|
||||||
SelectField("Scan Status", arrayOf("Any", "Complete", "Discontinued", "Hiatus", "Incomplete", "Ongoing")),
|
|
||||||
SelectField("Publish Status", arrayOf("Any", "Cancelled", "Complete", "Discontinued", "Hiatus", "Incomplete", "Ongoing", "Unfinished")),
|
|
||||||
SelectField("Type", arrayOf("Any", "Doujinshi", "Manga", "Manhua", "Manhwa", "OEL", "One-shot")),
|
|
||||||
SelectField("Translation", arrayOf("Any", "Official Only")),
|
|
||||||
Sort(),
|
|
||||||
GenreList(getGenreList()),
|
|
||||||
)
|
|
||||||
|
|
||||||
// [...document.querySelectorAll("label.triStateCheckBox input")].map(el => `Filter("${el.getAttribute('name')}", "${el.nextSibling.textContent.trim()}")`).join(',\n')
|
|
||||||
// https://manga4life.com/advanced-search/
|
|
||||||
private fun getGenreList() = listOf(
|
|
||||||
Genre("Action"),
|
|
||||||
Genre("Adult"),
|
|
||||||
Genre("Adventure"),
|
|
||||||
Genre("Comedy"),
|
|
||||||
Genre("Doujinshi"),
|
|
||||||
Genre("Drama"),
|
|
||||||
Genre("Ecchi"),
|
|
||||||
Genre("Fantasy"),
|
|
||||||
Genre("Gender Bender"),
|
|
||||||
Genre("Harem"),
|
|
||||||
Genre("Hentai"),
|
|
||||||
Genre("Historical"),
|
|
||||||
Genre("Horror"),
|
|
||||||
Genre("Isekai"),
|
|
||||||
Genre("Josei"),
|
|
||||||
Genre("Lolicon"),
|
|
||||||
Genre("Martial Arts"),
|
|
||||||
Genre("Mature"),
|
|
||||||
Genre("Mecha"),
|
|
||||||
Genre("Mystery"),
|
|
||||||
Genre("Psychological"),
|
|
||||||
Genre("Romance"),
|
|
||||||
Genre("School Life"),
|
|
||||||
Genre("Sci-fi"),
|
|
||||||
Genre("Seinen"),
|
|
||||||
Genre("Shotacon"),
|
|
||||||
Genre("Shoujo"),
|
|
||||||
Genre("Shoujo Ai"),
|
|
||||||
Genre("Shounen"),
|
|
||||||
Genre("Shounen Ai"),
|
|
||||||
Genre("Slice of Life"),
|
|
||||||
Genre("Smut"),
|
|
||||||
Genre("Sports"),
|
|
||||||
Genre("Supernatural"),
|
|
||||||
Genre("Tragedy"),
|
|
||||||
Genre("Yaoi"),
|
|
||||||
Genre("Yuri"),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
ext {
|
|
||||||
extName = 'MangaLife'
|
|
||||||
extClass = '.MangaLife'
|
|
||||||
themePkg = 'nepnep'
|
|
||||||
baseUrl = 'https://manga4life.com'
|
|
||||||
overrideVersionCode = 16
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 9.2 KiB |
|
@ -1,16 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.mangalife
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.nepnep.NepNep
|
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class MangaLife : NepNep("MangaLife", "https://manga4life.com", "en") {
|
|
||||||
|
|
||||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
|
||||||
.rateLimit(1, 2)
|
|
||||||
.connectTimeout(1, TimeUnit.MINUTES)
|
|
||||||
.readTimeout(1, TimeUnit.MINUTES)
|
|
||||||
.writeTimeout(1, TimeUnit.MINUTES)
|
|
||||||
.build()
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<application>
|
|
||||||
<activity
|
|
||||||
android:name="eu.kanade.tachiyomi.multisrc.mangasee.MangaseeUrlActivity"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:exported="true"
|
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
|
|
||||||
<data
|
|
||||||
android:host="mangasee123.com"
|
|
||||||
android:pathPattern="/manga/..*"
|
|
||||||
android:scheme="https" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
</application>
|
|
||||||
</manifest>
|
|
|
@ -1,9 +0,0 @@
|
||||||
ext {
|
|
||||||
extName = 'MangaSee'
|
|
||||||
extClass = '.MangaSee'
|
|
||||||
themePkg = 'nepnep'
|
|
||||||
baseUrl = 'https://mangasee123.com'
|
|
||||||
overrideVersionCode = 24
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 10 KiB |
|
@ -1,38 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.mangasee
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.nepnep.NepNep
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import rx.Observable
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class MangaSee : NepNep("MangaSee", "https://mangasee123.com", "en") {
|
|
||||||
|
|
||||||
override val id: Long = 9
|
|
||||||
|
|
||||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
|
||||||
.rateLimit(1, 2)
|
|
||||||
.connectTimeout(1, TimeUnit.MINUTES)
|
|
||||||
.readTimeout(1, TimeUnit.MINUTES)
|
|
||||||
.writeTimeout(1, TimeUnit.MINUTES)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
|
||||||
if (query.startsWith("id:")) {
|
|
||||||
val id = query.substringAfter("id:")
|
|
||||||
return client.newCall(GET("$baseUrl/manga/$id"))
|
|
||||||
.asObservableSuccess()
|
|
||||||
.map { response ->
|
|
||||||
val manga = mangaDetailsParse(response)
|
|
||||||
manga.url = "/manga/$id"
|
|
||||||
MangasPage(listOf(manga), false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.fetchSearchManga(page, query, filters)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.multisrc.mangasee
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.util.Log
|
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Springboard that accepts https://mangasee123.com/manga/xxxxxx intents and redirects them to
|
|
||||||
* the main Tachiyomi process.
|
|
||||||
*/
|
|
||||||
class MangaseeUrlActivity : Activity() {
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
val pathSegments = intent?.data?.pathSegments
|
|
||||||
if (pathSegments != null && pathSegments.size > 1) {
|
|
||||||
val id = pathSegments[1]
|
|
||||||
val mainIntent = Intent().apply {
|
|
||||||
action = "eu.kanade.tachiyomi.SEARCH"
|
|
||||||
putExtra("query", "id:$id")
|
|
||||||
putExtra("filter", packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
startActivity(mainIntent)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Log.e("MangaseeUrlActivity", e.toString())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.e("MangaseeUrlActivity", "could not parse uri from intent $intent")
|
|
||||||
}
|
|
||||||
|
|
||||||
finish()
|
|
||||||
exitProcess(0)
|
|
||||||
}
|
|
||||||
}
|
|