feat(en/fmovies): Add status and next episode air date using title (#2834)

This commit is contained in:
Samfun75
2024-01-29 15:49:21 +03:00
committed by GitHub
parent 4470991ee4
commit e54612a8a7
3 changed files with 114 additions and 10 deletions

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'FMovies' extName = 'FMovies'
extClass = '.FMovies' extClass = '.FMovies'
extVersionCode = 17 extVersionCode = 18
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -49,7 +49,7 @@ class FMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
private val utils by lazy { FmoviesUtils() } private val utils by lazy { FmoviesUtils(client, headers) }
// ============================== Popular =============================== // ============================== Popular ===============================
@ -107,15 +107,36 @@ class FMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val desc = descElement?.selectFirst("div[data-name=full]")?.ownText() ?: descElement?.ownText() ?: "" val desc = descElement?.selectFirst("div[data-name=full]")?.ownText() ?: descElement?.ownText() ?: ""
val extraInfo = detail?.select("> div")?.joinToString("\n") { it.text() } ?: "" val extraInfo = detail?.select("> div")?.joinToString("\n") { it.text() } ?: ""
val mediaTitle = info.selectFirst("h1.name")!!.text()
val mediaDetail = utils.getDetail(mediaTitle)
return SAnime.create().apply { return SAnime.create().apply {
title = info.selectFirst("h1.name")!!.text() title = mediaTitle
thumbnail_url = document.selectFirst("section#w-info > div.poster img")!!.attr("src") status = when (mediaDetail?.status) {
description = if (desc.isBlank()) extraInfo else "$desc\n\n$extraInfo" "Ended", "Released" -> SAnime.COMPLETED
genre = detail?.let { "In Production" -> SAnime.LICENSED
it.select("> div:has(> div:contains(Genre:)) span").joinToString(", ") { it.text() } "Canceled" -> SAnime.CANCELLED
"Returning Series" -> {
mediaDetail.nextEpisode?.let { SAnime.ONGOING } ?: SAnime.ON_HIATUS
}
else -> SAnime.UNKNOWN
} }
author = detail?.let { thumbnail_url = document.selectFirst("section#w-info > div.poster img")!!.attr("src")
it.select("> div:has(> div:contains(Production:)) span").joinToString(", ") { it.text() } description = buildString {
appendLine(desc.ifBlank { mediaDetail?.overview })
appendLine()
mediaDetail?.nextEpisode?.let {
appendLine("Next: Ep ${it.epNumber} - ${it.name}")
appendLine("Air Date: ${it.airDate}")
appendLine()
}
appendLine(extraInfo)
}
genre = detail?.let { dtl ->
dtl.select("> div:has(> div:contains(Genre:)) span").joinToString { it.text() }
}
author = detail?.let { dtl ->
dtl.select("> div:has(> div:contains(Production:)) span").joinToString { it.text() }
} }
} }
} }

View File

@ -1,12 +1,66 @@
package eu.kanade.tachiyomi.animeextension.en.fmovies package eu.kanade.tachiyomi.animeextension.en.fmovies
import android.util.Base64 import android.util.Base64
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import java.net.URLDecoder import java.net.URLDecoder
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
class FmoviesUtils { class FmoviesUtils(private val client: OkHttpClient, private val headers: Headers) {
// ===================== Media Detail ================================
private val tmdbURL = "https://api.themoviedb.org/3".toHttpUrl()
private val seez = "https://seez.su"
private val apiKey by lazy {
val jsUrl = client.newCall(GET(seez, headers)).execute().asJsoup()
.select("script[defer][src]")[1].attr("abs:src")
val jsBody = client.newCall(GET(jsUrl, headers)).execute().use { it.body.string() }
Regex("""f="(\w{20,})"""").find(jsBody)!!.groupValues[1]
}
private val apiHeaders = headers.newBuilder().apply {
add("Accept", "application/json, text/javascript, */*; q=0.01")
add("Host", "api.themoviedb.org")
add("Origin", seez)
add("Referer", "$seez/")
}.build()
fun getDetail(mediaTitle: String): TmdbDetailsResponse? =
runCatching {
val searchUrl = tmdbURL.newBuilder().apply {
addPathSegment("search")
addPathSegment("multi")
addQueryParameter("query", mediaTitle)
addQueryParameter("api_key", apiKey)
}.build().toString()
val searchResp = client.newCall(GET(searchUrl, headers = apiHeaders))
.execute()
.parseAs<TmdbResponse>()
val media = searchResp.results.first()
val detailUrl = tmdbURL.newBuilder().apply {
addPathSegment(media.mediaType)
addPathSegment(media.id.toString())
addQueryParameter("api_key", apiKey)
}.build().toString()
client.newCall(GET(detailUrl, headers = apiHeaders))
.execute()
.parseAs<TmdbDetailsResponse>()
}.getOrNull()
// ===================== Encryption ================================
fun vrfEncrypt(input: String): String { fun vrfEncrypt(input: String): String {
val rc4Key = SecretKeySpec("FWsfu0KQd9vxYGNB".toByteArray(), "RC4") val rc4Key = SecretKeySpec("FWsfu0KQd9vxYGNB".toByteArray(), "RC4")
val cipher = Cipher.getInstance("RC4") val cipher = Cipher.getInstance("RC4")
@ -53,3 +107,32 @@ class FmoviesUtils {
return vrf return vrf
} }
} }
@Serializable
data class TmdbResponse(
val results: List<TmdbResult>,
) {
@Serializable
data class TmdbResult(
val id: Int,
@SerialName("media_type")
val mediaType: String = "tv",
)
}
@Serializable
data class TmdbDetailsResponse(
val status: String,
val overview: String? = null,
@SerialName("next_episode_to_air")
val nextEpisode: NextEpisode? = null,
) {
@Serializable
data class NextEpisode(
val name: String? = "",
@SerialName("episode_number")
val epNumber: Int,
@SerialName("air_date")
val airDate: String,
)
}