Initial commit

This commit is contained in:
almightyhak 2024-06-20 11:54:12 +07:00
commit 98ed7e8839
2263 changed files with 108711 additions and 0 deletions

View file

@ -0,0 +1,14 @@
ext {
extName = 'JetAnime'
extClass = '.JetAnime'
themePkg = 'dooplay'
baseUrl = 'https://ssl.jetanimes.com'
overrideVersionCode = 7
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
implementation(project(':lib:playlist-utils'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View file

@ -0,0 +1,171 @@
package eu.kanade.tachiyomi.animeextension.fr.jetanime
import eu.kanade.tachiyomi.animeextension.fr.jetanime.extractors.HdsplayExtractor
import eu.kanade.tachiyomi.animeextension.fr.jetanime.extractors.SentinelExtractor
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Response
import org.jsoup.nodes.Element
class JetAnime : DooPlay(
"fr",
"JetAnime",
"https://ssl.jetanimes.com",
) {
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int) = GET(baseUrl, headers)
override fun popularAnimeSelector(): String = "aside#dtw_content_views-2 div.dtw_content > article"
override fun popularAnimeNextPageSelector() = null
// =============================== Latest ===============================
override fun latestUpdatesFromElement(element: Element): SAnime {
return SAnime.create().apply {
val img = element.selectFirst("img")!!
val url = element.selectFirst("a")?.attr("href") ?: element.attr("href")
val slug = url.substringAfter("/episodes/")
setUrlWithoutDomain("/serie/${slug.substringBeforeLast("-episode").substringBeforeLast("-saison")}")
title = img.attr("alt")
thumbnail_url = img.getImageUrl()
}
}
override fun latestUpdatesNextPageSelector(): String = "div.pagination > span.current + a"
// =============================== Search ===============================
override fun searchAnimeParse(response: Response): AnimesPage {
val document = response.asJsoup()
val url = response.request.url.toString()
val animeList = when {
"/?s=" in url -> { // Search by name.
document.select(searchSelector())
.map(::searchAnimeFromElement)
}
"/annee/" in url -> { // Search by year
document.select(searchYearSelector())
.map(::popularAnimeFromElement)
}
else -> { // Search by some kind of filter, like genres or popularity.
document.select(searchAnimeSelector())
.map(::popularAnimeFromElement)
}
}
val hasNextPage = document.selectFirst(searchAnimeNextPageSelector()) != null
return AnimesPage(animeList, hasNextPage)
}
private fun searchSelector() = "div.search-page > div.result-item div.image a"
private fun searchYearSelector() = "div.content > div.items > article div.poster"
override fun searchAnimeSelector() = "div#archive-content > article > div.poster"
// ============================== Filters ===============================
override val fetchGenres = false
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
AnimeFilter.Header("Text search ignores filters"),
AnimeFilter.Header("Only one filter at a time works"),
SubPageFilter(),
YearFilter(),
)
private class SubPageFilter : UriPartFilter(
"Sub-page",
arrayOf(
Pair("<select>", ""),
Pair("FILMS Animes", "/films"),
Pair("SERIES Animes", "/serie"),
),
)
private class YearFilter : UriPartFilter(
"Year",
arrayOf(
Pair("<select>", ""),
Pair("2024", "/annee/2024"),
Pair("2023", "/annee/2023"),
Pair("2022", "/annee/2022"),
Pair("2021", "/annee/2021"),
Pair("2020", "/annee/2020"),
Pair("2019", "/annee/2019"),
Pair("2018", "/annee/2018"),
Pair("2017", "/annee/2017"),
Pair("2016", "/annee/2016"),
Pair("2015", "/annee/2015"),
Pair("2014", "/annee/2014"),
Pair("2013", "/annee/2013"),
Pair("2012", "/annee/2012"),
Pair("2011", "/annee/2011"),
Pair("2010", "/annee/2010"),
Pair("2009", "/annee/2009"),
),
)
// ============================ Video Links =============================
private val noRedirects = client.newBuilder()
.followRedirects(false)
.followSslRedirects(false)
.build()
override fun videoListParse(response: Response): List<Video> {
val players = response.asJsoup().select("ul#playeroptionsul li")
val videoList = players.mapNotNull { player ->
runCatching {
val url = getPlayerUrl(player).ifEmpty { return@mapNotNull null }
val redirected = noRedirects.newCall(
GET(url),
).execute().headers["location"] ?: url
val name = player.text().trim()
getPlayerVideos(redirected, name)
}.getOrNull()
}.flatten()
require(videoList.isNotEmpty()) { "Failed to fetch videos" }
return emptyList()
}
private fun getPlayerUrl(player: Element): String {
val type = player.attr("data-type")
val id = player.attr("data-post")
val num = player.attr("data-nume")
if (num == "trailer") return ""
return client.newCall(GET("$baseUrl/wp-json/dooplayer/v1/post/$id?type=$type&source=$num"))
.execute()
.body.string()
.substringAfter("\"embed_url\":\"")
.substringBefore("\",")
.replace("\\", "")
}
private fun getPlayerVideos(url: String, name: String): List<Video> {
return when {
url.contains("https://sentinel") -> SentinelExtractor(client).videoFromUrl(url, name)
url.contains("https://hdsplay") -> HdsplayExtractor(client).videoFromUrl(url, name)
else -> emptyList()
}
}
// ============================== Settings ==============================
override val prefQualityValues = arrayOf("1080p", "720p", "480p", "360p", "240p")
override val prefQualityEntries = prefQualityValues
}

View file

@ -0,0 +1,44 @@
package eu.kanade.tachiyomi.animeextension.fr.jetanime.extractors
import dev.datlag.jsunpacker.JsUnpacker
import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
class HdsplayExtractor(private val client: OkHttpClient) {
private val playListUtils: PlaylistUtils by lazy {
PlaylistUtils(client)
}
fun videoFromUrl(url: String, name: String): List<Video> {
val document = client.newCall(GET(url)).execute().asJsoup()
val script = document.selectFirst("script:containsData(m3u8),script:containsData(mp4)")
?.data()
?.let { t -> JsUnpacker.unpackAndCombine(t) ?: t }
?: return emptyList()
val videoUrl = Regex("""file: ?\"(.*?(?:m3u8|mp4).*?)\"""").find(script)!!.groupValues[1]
val subtitleList = Regex("""file: ?\"(.*?(?:vtt|ass|srt).*?)\".*?label: ?\"(.*?)\"""").find(script)?.let {
listOf(Track(it.groupValues[1], it.groupValues[2]))
} ?: emptyList()
if (videoUrl.toHttpUrlOrNull() == null) {
return emptyList()
}
return when {
videoUrl.contains(".m3u8") -> playListUtils.extractFromHls(videoUrl, url, videoNameGen = { quality -> "Hdsplay: $quality ($name)" }, subtitleList = subtitleList)
else -> {
listOf(
Video(videoUrl, "Sentinel: Video ($name)", videoUrl, subtitleTracks = subtitleList),
)
}
}
}
}

View file

@ -0,0 +1,44 @@
package eu.kanade.tachiyomi.animeextension.fr.jetanime.extractors
import dev.datlag.jsunpacker.JsUnpacker
import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
class SentinelExtractor(private val client: OkHttpClient) {
private val playListUtils: PlaylistUtils by lazy {
PlaylistUtils(client)
}
fun videoFromUrl(url: String, name: String): List<Video> {
val document = client.newCall(GET(url)).execute().asJsoup()
val script = document.selectFirst("script:containsData(m3u8),script:containsData(mp4)")
?.data()
?.let { t -> JsUnpacker.unpackAndCombine(t) ?: t }
?: return emptyList()
val videoUrl = Regex("""file: ?\"(.*?(?:m3u8|mp4).*?)\"""").find(script)!!.groupValues[1]
val subtitleList = Regex("""file: ?\"(.*?(?:vtt|ass|srt).*?)\".*?label: ?\"(.*?)\"""").find(script)?.let {
listOf(Track(it.groupValues[1], it.groupValues[2]))
} ?: emptyList()
if (videoUrl.toHttpUrlOrNull() == null) {
return emptyList()
}
return when {
videoUrl.contains(".m3u8") -> playListUtils.extractFromHls(videoUrl, url, videoNameGen = { quality -> "Sentinel: $quality ($name)" }, subtitleList = subtitleList)
else -> {
listOf(
Video(videoUrl, "Sentinel: Video ($name)", videoUrl, subtitleTracks = subtitleList),
)
}
}
}
}