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 { 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"

View File

@ -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)
} }
} }

View File

@ -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>,
) )