New [RU] MangaPoisk (#6414)
* start MangaPoisk * Search and Details * Chapter List * Page List * Fix chapter dates * Update before. Fix numbers * Fix before dates * add icon Co-authored-by: Pavel Mosein <p.mosein@edadeal.ru>
This commit is contained in:
parent
f98d35bea5
commit
326d27e062
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="eu.kanade.tachiyomi.extension" />
|
|
@ -0,0 +1,12 @@
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
|
ext {
|
||||||
|
extName = 'MangaPoisk'
|
||||||
|
pkgNameSuffix = 'ru.mangapoisk'
|
||||||
|
extClass = '.MangaPoisk'
|
||||||
|
extVersionCode = 1
|
||||||
|
libVersion = '1.2'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 101 KiB |
|
@ -0,0 +1,248 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.ru.mangapoisk
|
||||||
|
|
||||||
|
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.ParsedHttpSource
|
||||||
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import okhttp3.Headers
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
import rx.Observable
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class MangaPoisk : ParsedHttpSource() {
|
||||||
|
override val name = "MangaPoisk"
|
||||||
|
|
||||||
|
override val baseUrl = "https://mangapoisk.ru"
|
||||||
|
|
||||||
|
override val lang = "ru"
|
||||||
|
|
||||||
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
private val userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.3987.163 Safari/537.36"
|
||||||
|
|
||||||
|
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
||||||
|
.add("User-Agent", userAgent)
|
||||||
|
.add("Referer", baseUrl)
|
||||||
|
|
||||||
|
override fun popularMangaRequest(page: Int): Request =
|
||||||
|
GET("$baseUrl/manga?sortBy=popular&page=$page", headers)
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request =
|
||||||
|
GET("$baseUrl/manga?sortBy=-last_chapter_at&page=$page", headers)
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
val url = if (query.isNotBlank()) {
|
||||||
|
"$baseUrl/search?q=$query"
|
||||||
|
} else {
|
||||||
|
val url = HttpUrl.parse("$baseUrl/manga")!!.newBuilder()
|
||||||
|
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||||
|
when (filter) {
|
||||||
|
is OrderBy -> {
|
||||||
|
val ord = arrayOf("-year", "popular", "name", "-published_at", "-last_chapter_at")[filter.state!!.index]
|
||||||
|
val ordRev = arrayOf("year", "-popular", "-name", "published_at", "last_chapter_at")[filter.state!!.index]
|
||||||
|
url.addQueryParameter("sortBy", if (filter.state!!.ascending) "$ordRev" else "$ord")
|
||||||
|
}
|
||||||
|
is StatusList -> filter.state.forEach { status ->
|
||||||
|
if (status.state) {
|
||||||
|
url.addQueryParameter("translated[]", status.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is GenreList -> filter.state.forEach { genre ->
|
||||||
|
if (genre.state) {
|
||||||
|
url.addQueryParameter("genres[]", genre.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return GET(url.toString(), headers)
|
||||||
|
}
|
||||||
|
return GET(url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaSelector() = "article.card"
|
||||||
|
|
||||||
|
override fun latestUpdatesSelector() = popularMangaSelector()
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
|
return popularMangaParse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getImage(first: Element): String? {
|
||||||
|
val image = first.attr("data-src")
|
||||||
|
if (image.isNotEmpty()) {
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
return first.attr("src")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaFromElement(element: Element): SManga {
|
||||||
|
return SManga.create().apply {
|
||||||
|
thumbnail_url = getImage(element.select("a > img").first())
|
||||||
|
|
||||||
|
element.select("a.card-about").first().let {
|
||||||
|
setUrlWithoutDomain(it.attr("href"))
|
||||||
|
}
|
||||||
|
|
||||||
|
element.select("a > h2.entry-title").first().let {
|
||||||
|
title = it.text().split("/").first()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesFromElement(element: Element): SManga =
|
||||||
|
popularMangaFromElement(element)
|
||||||
|
|
||||||
|
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||||
|
|
||||||
|
override fun popularMangaNextPageSelector() = "a.page-link"
|
||||||
|
|
||||||
|
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
||||||
|
|
||||||
|
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
||||||
|
|
||||||
|
override fun mangaDetailsParse(document: Document): SManga {
|
||||||
|
val infoElement = document.select("article div.card-body").first()
|
||||||
|
val manga = SManga.create()
|
||||||
|
manga.genre = infoElement.select(".post-info > span:eq(10) > a").joinToString { it.text() }
|
||||||
|
manga.description = infoElement.select(".post-info > div .manga-description.entry").text()
|
||||||
|
manga.status = parseStatus(infoElement.select(".post-info > span:eq(7)").text())
|
||||||
|
manga.thumbnail_url = infoElement.select("img.img-fluid").first().attr("src")
|
||||||
|
return manga
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseStatus(element: String): Int = when {
|
||||||
|
element.contains("Статус: Завершена") -> SManga.COMPLETED
|
||||||
|
element.contains("Статус: Выпускается") -> SManga.ONGOING
|
||||||
|
else -> SManga.UNKNOWN
|
||||||
|
}
|
||||||
|
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||||
|
return client.newCall(chapterListRequest(manga))
|
||||||
|
.asObservableSuccess()
|
||||||
|
.map { response ->
|
||||||
|
chapterListParse(response, manga)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun chapterListParse(response: Response, manga: SManga): List<SChapter> {
|
||||||
|
val document = response.asJsoup()
|
||||||
|
return document.select(chapterListSelector()).map { chapterFromElement(it, manga) }
|
||||||
|
}
|
||||||
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
|
return GET("$baseUrl${manga.url}/chaptersList", headers)
|
||||||
|
}
|
||||||
|
override fun chapterListSelector() = ".chapter-item"
|
||||||
|
|
||||||
|
private fun chapterFromElement(element: Element, manga: SManga): SChapter {
|
||||||
|
val title = element.select("span.chapter-title").first().text()
|
||||||
|
val urlElement = element.select("a").first()
|
||||||
|
val urlText = urlElement.text()
|
||||||
|
|
||||||
|
val chapter = SChapter.create()
|
||||||
|
chapter.setUrlWithoutDomain(urlElement.attr("href"))
|
||||||
|
|
||||||
|
chapter.name = urlText.trim()
|
||||||
|
chapter.chapter_number = "Глава\\s(\\d+)".toRegex(RegexOption.IGNORE_CASE).find(title)?.groupValues?.get(1)?.toFloat() ?: -1F
|
||||||
|
chapter.date_upload = element.select("span.chapter-date").first()?.text()?.let {
|
||||||
|
try {
|
||||||
|
when {
|
||||||
|
it.contains("минут") -> Date(System.currentTimeMillis() - it.split("\\s".toRegex())[0].toLong() * 60 * 1000).time
|
||||||
|
it.contains("час") -> Date(System.currentTimeMillis() - it.split("\\s".toRegex())[0].toLong() * 60 * 60 * 1000).time
|
||||||
|
it.contains("дня") || it.contains("дней") -> Date(System.currentTimeMillis() - it.split("\\s".toRegex())[0].toLong() * 24 * 60 * 60 * 1000).time
|
||||||
|
else -> SimpleDateFormat("dd MMMM yyyy", Locale("ru")).parse(it)?.time ?: 0L
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Date(System.currentTimeMillis()).time
|
||||||
|
}
|
||||||
|
} ?: 0
|
||||||
|
return chapter
|
||||||
|
}
|
||||||
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
|
return document.select(".img-fluid.page-image").mapIndexed { index, element ->
|
||||||
|
Page(index, "", getImage(element))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CheckFilter(name: String, val id: String) : Filter.CheckBox(name)
|
||||||
|
|
||||||
|
private class StatusList(statuses: List<CheckFilter>) : Filter.Group<CheckFilter>("Статус", statuses)
|
||||||
|
private class GenreList(genres: List<CheckFilter>) : Filter.Group<CheckFilter>("Жанры", genres)
|
||||||
|
override fun getFilterList() = FilterList(
|
||||||
|
OrderBy(),
|
||||||
|
StatusList(getStatusList()),
|
||||||
|
GenreList(getGenreList())
|
||||||
|
)
|
||||||
|
|
||||||
|
private class OrderBy : Filter.Sort(
|
||||||
|
"Сортировка",
|
||||||
|
arrayOf("Год", "Популярности", "Алфавиту", "Дате добавления", "Дате обновления"),
|
||||||
|
Selection(1, false)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getStatusList() = listOf(
|
||||||
|
CheckFilter("Выпускается", "0"),
|
||||||
|
CheckFilter("Завершена", "1"),
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getGenreList() = listOf(
|
||||||
|
CheckFilter("приключения", "1"),
|
||||||
|
CheckFilter("романтика", "2"),
|
||||||
|
CheckFilter("боевик", "3"),
|
||||||
|
CheckFilter("комедия", "4"),
|
||||||
|
CheckFilter("сверхъестественное", "5"),
|
||||||
|
CheckFilter("драма", "6"),
|
||||||
|
CheckFilter("фэнтези", "7"),
|
||||||
|
CheckFilter("сёнэн", "8"),
|
||||||
|
CheckFilter("этти", "7"),
|
||||||
|
CheckFilter("вампиры", "10"),
|
||||||
|
CheckFilter("школа", "11"),
|
||||||
|
CheckFilter("сэйнэн", "12"),
|
||||||
|
CheckFilter("повседневность", "18"),
|
||||||
|
CheckFilter("сёнэн-ай", "19"),
|
||||||
|
CheckFilter("гарем", "29"),
|
||||||
|
CheckFilter("героическое фэнтези", "30"),
|
||||||
|
CheckFilter("боевые искусства", "31"),
|
||||||
|
CheckFilter("психология", "38"),
|
||||||
|
CheckFilter("сёдзё", "57"),
|
||||||
|
CheckFilter("игра", "105"),
|
||||||
|
CheckFilter("триллер", "120"),
|
||||||
|
CheckFilter("детектив", "121"),
|
||||||
|
CheckFilter("трагедия", "122"),
|
||||||
|
CheckFilter("история", "123"),
|
||||||
|
CheckFilter("сёдзё-ай", "147"),
|
||||||
|
CheckFilter("спорт", "160"),
|
||||||
|
CheckFilter("научная фантастика", "171"),
|
||||||
|
CheckFilter("гендерная интрига", "172"),
|
||||||
|
CheckFilter("дзёсэй", "230"),
|
||||||
|
CheckFilter("ужасы", "260"),
|
||||||
|
CheckFilter("постапокалиптика", "310"),
|
||||||
|
CheckFilter("киберпанк", "355"),
|
||||||
|
CheckFilter("меха", "356"),
|
||||||
|
CheckFilter("эротика", "380"),
|
||||||
|
CheckFilter("яой", "612"),
|
||||||
|
CheckFilter("самурайский боевик", "916"),
|
||||||
|
CheckFilter("махо-сёдзё", "1472"),
|
||||||
|
CheckFilter("додзинси", "1785"),
|
||||||
|
CheckFilter("кодомо", "1789"),
|
||||||
|
CheckFilter("юри", "3197"),
|
||||||
|
CheckFilter("арт", "7332"),
|
||||||
|
CheckFilter("омегаверс", "7514"),
|
||||||
|
CheckFilter("бара", "8119")
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun imageUrlParse(document: Document) = throw Exception("Not Used")
|
||||||
|
|
||||||
|
override fun searchMangaSelector(): String = throw Exception("Not Used")
|
||||||
|
|
||||||
|
override fun chapterFromElement(element: Element): SChapter = throw Exception("Not Used")
|
||||||
|
}
|
Loading…
Reference in New Issue