YugenMangas: Update domains (#4743)

* Update domains

* Bump versionId
This commit is contained in:
Chopper 2024-08-25 03:51:33 -03:00 committed by GitHub
parent 7fe27af40d
commit 76f6adfc8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 123 additions and 90 deletions

View File

@ -1,7 +1,7 @@
ext {
extName = 'Yugen Mangás'
extClass = '.YugenMangas'
extVersionCode = 40
extVersionCode = 41
}
apply from: "$rootDir/common.gradle"

View File

@ -22,13 +22,15 @@ import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okio.Buffer
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
class YugenMangas : HttpSource() {
override val name = "Yugen Mangás"
override val baseUrl = "https://yugenapp.lat"
override val baseUrl = "https://yugenweb.com"
override val lang = "pt-BR"
@ -38,6 +40,8 @@ class YugenMangas : HttpSource() {
.rateLimit(1, 2, TimeUnit.SECONDS)
.build()
override val versionId = 2
private val json: Json by injectLazy()
override fun headersBuilder(): Headers.Builder = Headers.Builder()
@ -49,80 +53,79 @@ class YugenMangas : HttpSource() {
.add("Accept", "application/json, text/plain, */*")
.add("Origin", baseUrl)
.add("Sec-Fetch-Dest", "empty")
.add("Sec-Fetch-Mode", "cors")
.add("Sec-Fetch-Mode", "no-cors")
.add("Sec-Fetch-Site", "same-site")
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 {
val result = response.parseAs<List<YugenMangaDto>>()
val mangaList = result.map { it.toSManga(API_HOST) }
return MangasPage(mangaList, hasNextPage = false)
val dto = response.parseAs<PageDto<MangaDto>>()
val mangaList = dto.results.map { it.toSManga() }
return MangasPage(mangaList, hasNextPage = dto.hasNext())
}
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 {
val apiUrl = "$API_BASE_URL/series".toHttpUrl().newBuilder()
.addQueryParameter("search", query)
.build()
return GET(apiUrl, apiHeaders)
val payload = json.encodeToString(SearchDto(query)).toRequestBody(JSON_MEDIA_TYPE)
return POST("$BASE_API/widgets/search/", apiHeaders, payload)
}
override fun searchMangaParse(response: Response) = popularMangaParse(response)
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
override fun mangaDetailsRequest(manga: SManga): Request {
val slug = manga.url.removePrefix("/series/")
return POST("$API_BASE_URL/serie/serie_details/$slug", apiHeaders)
val code = manga.url.substringAfterLast("/")
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 mangaDetailsParse(response: Response): SManga {
return response.parseAs<YugenMangaDto>().toSManga(API_BASE_URL)
return response.parseAs<MangaDetailsDto>().toSManga()
}
override fun chapterListRequest(manga: SManga): Request {
val slug = manga.url.removePrefix("/series/")
val body = YugenGetChaptersBySeriesDto(slug)
val payload = json.encodeToString(body).toRequestBody(JSON_MEDIA_TYPE)
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)
val code = manga.url.substringAfterLast("/")
val payload = json.encodeToString(SeriesDto(code)).toRequestBody(JSON_MEDIA_TYPE)
return POST("$BASE_API/series/chapters/get-series-chapters/", apiHeaders, payload)
}
override fun pageListRequest(chapter: SChapter): Request {
val slug = chapter.url.removePrefix("/series/").substringBefore("/")
val chapterSlug = chapter.url.substringAfterLast("/")
return POST("$API_BASE_URL/serie/$slug/chapter/$chapterSlug/images/imgs/get/", apiHeaders)
val code = chapter.url.substringAfterLast("/")
val payload = json.encodeToString(SeriesDto(code)).toRequestBody(JSON_MEDIA_TYPE)
return POST("$BASE_API/chapters/chapter-info/", apiHeaders, payload)
}
override fun chapterListParse(response: Response): List<SChapter> {
val (seriesSlug) = response.request.body!!.parseAs<YugenGetChaptersBySeriesDto>()
return response.parseAs<YugenChapterListDto>().chapters
.map { it.toSChapter(seriesSlug) }
.sortedByDescending(SChapter::chapter_number)
val series = response.request.body!!.parseAs<SeriesDto>()
return response.parseAs<List<ChapterDto>>()
.map { it.toSChapter(series.code) }
.reversed()
}
override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url
override fun pageListParse(response: Response): List<Page> {
val result = response.parseAs<YugenPageList>()
return result.chapterImages.mapIndexed { index, url -> Page(index, baseUrl, "$API_HOST/$url") }
return response.parseAs<PageListDto>().images.mapIndexed { index, imageUrl ->
Page(index, baseUrl, "$BASE_MEDIA/$imageUrl")
}
}
override fun imageUrlParse(response: Response) = ""
@ -145,8 +148,9 @@ class YugenMangas : HttpSource() {
}
companion object {
private const val API_HOST = "https://api.yugenapp.lat"
private const val API_BASE_URL = "$API_HOST/api"
private const val BASE_API = "https://api.yugenweb.com/api"
private const val BASE_MEDIA = "https://media.yugenweb.com"
private val JSON_MEDIA_TYPE = "application/json".toMediaType()
val DATE_FORMAT = SimpleDateFormat("dd/MM/yyyy", Locale.ROOT)
}
}

View File

@ -1,83 +1,112 @@
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.SManga
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.Calendar
@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,
@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 artist: String? = null,
val genres: List<String> = emptyList(),
val synopsis: String? = null,
val status: String? = null,
) {
fun toSManga(baseUrl: String): SManga = SManga.create().apply {
title = name
author = this@YugenMangaDto.author
artist = this@YugenMangaDto.artist
fun toSManga(): SManga = SManga.create().apply {
title = this@MangaDetailsDto.title
author = this@MangaDetailsDto.author
artist = this@MangaDetailsDto.artist
description = synopsis
status = when (this@YugenMangaDto.status) {
"ongoing" -> SManga.ONGOING
"completed", "finished" -> SManga.COMPLETED
status = when (this@MangaDetailsDto.status) {
"Em Lançamento" -> SManga.ONGOING
"Hiato" -> SManga.ON_HIATUS
"Cancelado" -> SManga.CANCELLED
"Finalizado" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
thumbnail_url = when {
cover.startsWith(listOf("/", "cover")) -> "$baseUrl/media/${cover.removePrefix("/")}"
else -> cover
}
url = "/series/$slug"
genre = genres.joinToString()
thumbnail_url = cover
url = "/series/$code"
}
private fun String.startsWith(group: List<String>): Boolean = group.any(::startsWith)
}
@Serializable
class YugenChapterListDto(val chapters: List<YugenChapterDto>)
@Serializable
class YugenChapterDto(
class ChapterDto(
val code: String,
val name: String,
val season: Int,
@SerialName("upload_date") val uploadDate: String,
val slug: String,
val group: String,
@SerialName("upload_date")
val date: String,
) {
fun toSChapter(mangaSlug: String): SChapter = SChapter.create().apply {
name = this@YugenChapterDto.name
date_upload = runCatching { DATE_FORMATTER.parse(uploadDate)?.time }
.getOrNull() ?: 0L
chapter_number = this@YugenChapterDto.name
.removePrefix("Capítulo ")
.substringBefore(" - ")
.toFloatOrNull() ?: -1f
scanlator = group.ifEmpty { null }
url = "/series/$mangaSlug/$slug"
fun toSChapter(mangaCode: String): SChapter = SChapter.create().apply {
name = this@ChapterDto.name
date_upload = try { parseDate() } catch (_: Exception) { 0L }
url = "/series/$mangaCode/$code"
}
companion object {
private val DATE_FORMATTER by lazy {
SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR"))
}
private fun parseDate(): Long {
return try {
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
data class YugenGetChaptersBySeriesDto(
@SerialName("serie_slug") val seriesSlug: String,
class SeriesDto(
val code: String,
)
@Serializable
class YugenPageList(
@SerialName("chapter_images") val chapterImages: List<String>,
class SearchDto(
val query: String,
)
@Serializable
class PageListDto(
val images: List<String>,
)