mirror of
https://github.com/keiyoushi/extensions-source.git
synced 2024-11-26 12:12:30 +01:00
parent
7fe27af40d
commit
76f6adfc8b
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'Yugen Mangás'
|
extName = 'Yugen Mangás'
|
||||||
extClass = '.YugenMangas'
|
extClass = '.YugenMangas'
|
||||||
extVersionCode = 40
|
extVersionCode = 41
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -22,13 +22,15 @@ import okhttp3.RequestBody.Companion.toRequestBody
|
|||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okio.Buffer
|
import okio.Buffer
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class YugenMangas : HttpSource() {
|
class YugenMangas : HttpSource() {
|
||||||
|
|
||||||
override val name = "Yugen Mangás"
|
override val name = "Yugen Mangás"
|
||||||
|
|
||||||
override val baseUrl = "https://yugenapp.lat"
|
override val baseUrl = "https://yugenweb.com"
|
||||||
|
|
||||||
override val lang = "pt-BR"
|
override val lang = "pt-BR"
|
||||||
|
|
||||||
@ -38,6 +40,8 @@ class YugenMangas : HttpSource() {
|
|||||||
.rateLimit(1, 2, TimeUnit.SECONDS)
|
.rateLimit(1, 2, TimeUnit.SECONDS)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
override val versionId = 2
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
||||||
@ -49,80 +53,79 @@ class YugenMangas : HttpSource() {
|
|||||||
.add("Accept", "application/json, text/plain, */*")
|
.add("Accept", "application/json, text/plain, */*")
|
||||||
.add("Origin", baseUrl)
|
.add("Origin", baseUrl)
|
||||||
.add("Sec-Fetch-Dest", "empty")
|
.add("Sec-Fetch-Dest", "empty")
|
||||||
.add("Sec-Fetch-Mode", "cors")
|
.add("Sec-Fetch-Mode", "no-cors")
|
||||||
.add("Sec-Fetch-Site", "same-site")
|
.add("Sec-Fetch-Site", "same-site")
|
||||||
|
|
||||||
override fun popularMangaRequest(page: Int): Request {
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
return GET("$API_BASE_URL/top_series_all/", apiHeaders)
|
val url = "$BASE_API/widgets/sort_and_filter/".toHttpUrl().newBuilder()
|
||||||
|
.addQueryParameter("page", "$page")
|
||||||
|
.addQueryParameter("sort", "views")
|
||||||
|
.addQueryParameter("order", "desc")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return GET(url, apiHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularMangaParse(response: Response): MangasPage {
|
override fun popularMangaParse(response: Response): MangasPage {
|
||||||
val result = response.parseAs<List<YugenMangaDto>>()
|
val dto = response.parseAs<PageDto<MangaDto>>()
|
||||||
val mangaList = result.map { it.toSManga(API_HOST) }
|
val mangaList = dto.results.map { it.toSManga() }
|
||||||
return MangasPage(mangaList, hasNextPage = false)
|
return MangasPage(mangaList, hasNextPage = dto.hasNext())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int): Request {
|
override fun latestUpdatesRequest(page: Int): Request {
|
||||||
return GET("$API_BASE_URL/latest_updates/", apiHeaders)
|
return GET("$BASE_API/widgets/home/updates/", apiHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
|
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||||
|
val dto = response.parseAs<LatestUpdatesDto>()
|
||||||
|
val mangaList = dto.series.map { it.toSManga() }
|
||||||
|
return MangasPage(mangaList, hasNextPage = false)
|
||||||
|
}
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
val apiUrl = "$API_BASE_URL/series".toHttpUrl().newBuilder()
|
val payload = json.encodeToString(SearchDto(query)).toRequestBody(JSON_MEDIA_TYPE)
|
||||||
.addQueryParameter("search", query)
|
return POST("$BASE_API/widgets/search/", apiHeaders, payload)
|
||||||
.build()
|
|
||||||
|
|
||||||
return GET(apiUrl, apiHeaders)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response) = popularMangaParse(response)
|
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
|
||||||
|
|
||||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
val slug = manga.url.removePrefix("/series/")
|
val code = manga.url.substringAfterLast("/")
|
||||||
return POST("$API_BASE_URL/serie/serie_details/$slug", apiHeaders)
|
val payload = json.encodeToString(SeriesDto(code)).toRequestBody(JSON_MEDIA_TYPE)
|
||||||
|
return POST("$BASE_API/series/detail/series/", apiHeaders, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMangaUrl(manga: SManga) = baseUrl + manga.url
|
override fun getMangaUrl(manga: SManga) = baseUrl + manga.url
|
||||||
|
|
||||||
override fun mangaDetailsParse(response: Response): SManga {
|
override fun mangaDetailsParse(response: Response): SManga {
|
||||||
return response.parseAs<YugenMangaDto>().toSManga(API_BASE_URL)
|
return response.parseAs<MangaDetailsDto>().toSManga()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga): Request {
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
val slug = manga.url.removePrefix("/series/")
|
val code = manga.url.substringAfterLast("/")
|
||||||
val body = YugenGetChaptersBySeriesDto(slug)
|
val payload = json.encodeToString(SeriesDto(code)).toRequestBody(JSON_MEDIA_TYPE)
|
||||||
val payload = json.encodeToString(body).toRequestBody(JSON_MEDIA_TYPE)
|
return POST("$BASE_API/series/chapters/get-series-chapters/", apiHeaders, payload)
|
||||||
|
|
||||||
val newHeaders = apiHeadersBuilder()
|
|
||||||
.set("Content-Length", payload.contentLength().toString())
|
|
||||||
.set("Content-Type", payload.contentType().toString())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
return POST("$API_BASE_URL/chapters/get_chapters_by_serie/", newHeaders, payload)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter): Request {
|
override fun pageListRequest(chapter: SChapter): Request {
|
||||||
val slug = chapter.url.removePrefix("/series/").substringBefore("/")
|
val code = chapter.url.substringAfterLast("/")
|
||||||
val chapterSlug = chapter.url.substringAfterLast("/")
|
val payload = json.encodeToString(SeriesDto(code)).toRequestBody(JSON_MEDIA_TYPE)
|
||||||
|
return POST("$BASE_API/chapters/chapter-info/", apiHeaders, payload)
|
||||||
return POST("$API_BASE_URL/serie/$slug/chapter/$chapterSlug/images/imgs/get/", apiHeaders)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
val (seriesSlug) = response.request.body!!.parseAs<YugenGetChaptersBySeriesDto>()
|
val series = response.request.body!!.parseAs<SeriesDto>()
|
||||||
|
return response.parseAs<List<ChapterDto>>()
|
||||||
return response.parseAs<YugenChapterListDto>().chapters
|
.map { it.toSChapter(series.code) }
|
||||||
.map { it.toSChapter(seriesSlug) }
|
.reversed()
|
||||||
.sortedByDescending(SChapter::chapter_number)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url
|
override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
val result = response.parseAs<YugenPageList>()
|
return response.parseAs<PageListDto>().images.mapIndexed { index, imageUrl ->
|
||||||
|
Page(index, baseUrl, "$BASE_MEDIA/$imageUrl")
|
||||||
return result.chapterImages.mapIndexed { index, url -> Page(index, baseUrl, "$API_HOST/$url") }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun imageUrlParse(response: Response) = ""
|
override fun imageUrlParse(response: Response) = ""
|
||||||
@ -145,8 +148,9 @@ class YugenMangas : HttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val API_HOST = "https://api.yugenapp.lat"
|
private const val BASE_API = "https://api.yugenweb.com/api"
|
||||||
private const val API_BASE_URL = "$API_HOST/api"
|
private const val BASE_MEDIA = "https://media.yugenweb.com"
|
||||||
private val JSON_MEDIA_TYPE = "application/json".toMediaType()
|
private val JSON_MEDIA_TYPE = "application/json".toMediaType()
|
||||||
|
val DATE_FORMAT = SimpleDateFormat("dd/MM/yyyy", Locale.ROOT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,83 +1,112 @@
|
|||||||
package eu.kanade.tachiyomi.extension.pt.yugenmangas
|
package eu.kanade.tachiyomi.extension.pt.yugenmangas
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.extension.pt.yugenmangas.YugenMangas.Companion.DATE_FORMAT
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.JsonNames
|
import kotlinx.serialization.json.JsonNames
|
||||||
import java.text.SimpleDateFormat
|
import java.util.Calendar
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class YugenMangaDto(
|
class PageDto<T>(
|
||||||
|
@SerialName("current_page")
|
||||||
|
val page: Int,
|
||||||
|
@SerialName("total_pages")
|
||||||
|
val totalPages: Int,
|
||||||
|
val results: List<T>,
|
||||||
|
) {
|
||||||
|
fun hasNext() = page < totalPages
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class MangaDto(
|
||||||
|
@JsonNames("series_code")
|
||||||
|
val code: String,
|
||||||
|
@JsonNames("path_cover")
|
||||||
|
val cover: String,
|
||||||
|
@JsonNames("title", "series_name")
|
||||||
val name: String,
|
val name: String,
|
||||||
@JsonNames("capa", "cover") val cover: String,
|
) {
|
||||||
val slug: String,
|
fun toSManga(): SManga = SManga.create().apply {
|
||||||
|
title = this@MangaDto.name
|
||||||
|
thumbnail_url = cover
|
||||||
|
url = "/series/$code"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class LatestUpdatesDto(
|
||||||
|
val series: List<MangaDto>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class MangaDetailsDto(
|
||||||
|
val title: String,
|
||||||
|
@SerialName("path_cover")
|
||||||
|
val cover: String,
|
||||||
|
val code: String,
|
||||||
val author: String? = null,
|
val author: String? = null,
|
||||||
val artist: String? = null,
|
val artist: String? = null,
|
||||||
val genres: List<String> = emptyList(),
|
val genres: List<String> = emptyList(),
|
||||||
val synopsis: String? = null,
|
val synopsis: String? = null,
|
||||||
val status: String? = null,
|
val status: String? = null,
|
||||||
) {
|
) {
|
||||||
|
fun toSManga(): SManga = SManga.create().apply {
|
||||||
fun toSManga(baseUrl: String): SManga = SManga.create().apply {
|
title = this@MangaDetailsDto.title
|
||||||
title = name
|
author = this@MangaDetailsDto.author
|
||||||
author = this@YugenMangaDto.author
|
artist = this@MangaDetailsDto.artist
|
||||||
artist = this@YugenMangaDto.artist
|
|
||||||
description = synopsis
|
description = synopsis
|
||||||
status = when (this@YugenMangaDto.status) {
|
status = when (this@MangaDetailsDto.status) {
|
||||||
"ongoing" -> SManga.ONGOING
|
"Em Lançamento" -> SManga.ONGOING
|
||||||
"completed", "finished" -> SManga.COMPLETED
|
"Hiato" -> SManga.ON_HIATUS
|
||||||
|
"Cancelado" -> SManga.CANCELLED
|
||||||
|
"Finalizado" -> SManga.COMPLETED
|
||||||
else -> SManga.UNKNOWN
|
else -> SManga.UNKNOWN
|
||||||
}
|
}
|
||||||
|
genre = genres.joinToString()
|
||||||
thumbnail_url = when {
|
thumbnail_url = cover
|
||||||
cover.startsWith(listOf("/", "cover")) -> "$baseUrl/media/${cover.removePrefix("/")}"
|
url = "/series/$code"
|
||||||
else -> cover
|
|
||||||
}
|
|
||||||
url = "/series/$slug"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.startsWith(group: List<String>): Boolean = group.any(::startsWith)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class YugenChapterListDto(val chapters: List<YugenChapterDto>)
|
class ChapterDto(
|
||||||
|
val code: String,
|
||||||
@Serializable
|
|
||||||
class YugenChapterDto(
|
|
||||||
val name: String,
|
val name: String,
|
||||||
val season: Int,
|
@SerialName("upload_date")
|
||||||
@SerialName("upload_date") val uploadDate: String,
|
val date: String,
|
||||||
val slug: String,
|
|
||||||
val group: String,
|
|
||||||
) {
|
) {
|
||||||
|
fun toSChapter(mangaCode: String): SChapter = SChapter.create().apply {
|
||||||
fun toSChapter(mangaSlug: String): SChapter = SChapter.create().apply {
|
name = this@ChapterDto.name
|
||||||
name = this@YugenChapterDto.name
|
date_upload = try { parseDate() } catch (_: Exception) { 0L }
|
||||||
date_upload = runCatching { DATE_FORMATTER.parse(uploadDate)?.time }
|
url = "/series/$mangaCode/$code"
|
||||||
.getOrNull() ?: 0L
|
|
||||||
chapter_number = this@YugenChapterDto.name
|
|
||||||
.removePrefix("Capítulo ")
|
|
||||||
.substringBefore(" - ")
|
|
||||||
.toFloatOrNull() ?: -1f
|
|
||||||
scanlator = group.ifEmpty { null }
|
|
||||||
url = "/series/$mangaSlug/$slug"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
private fun parseDate(): Long {
|
||||||
private val DATE_FORMATTER by lazy {
|
return try {
|
||||||
SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR"))
|
if ("dia" in date) {
|
||||||
}
|
val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0L
|
||||||
|
return Calendar.getInstance().let {
|
||||||
|
it.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DATE_FORMAT.parse(date)!!.time
|
||||||
|
} catch (_: Exception) { 0L }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class YugenGetChaptersBySeriesDto(
|
class SeriesDto(
|
||||||
@SerialName("serie_slug") val seriesSlug: String,
|
val code: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class YugenPageList(
|
class SearchDto(
|
||||||
@SerialName("chapter_images") val chapterImages: List<String>,
|
val query: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class PageListDto(
|
||||||
|
val images: List<String>,
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user