Merged with dark25 (#636)

* merge

merged lib, lib-multisrc, all, ar, de, en, es, fr, hi, id, it, pt, tr src from dark25

* patch
This commit is contained in:
Hak 2025-02-10 15:41:59 +07:00 committed by GitHub
parent 9f385108fc
commit 1384df62f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
350 changed files with 12176 additions and 1064 deletions

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -0,0 +1,410 @@
package eu.kanade.tachiyomi.animeextension.fr.animevostfr
import android.app.Application
import android.content.SharedPreferences
import android.util.Log
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.fr.animevostfr.extractors.CdopeExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class AnimeVostFr : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "AnimeVostFr"
override val baseUrl = "https://animevostfr.tv"
override val lang = "fr"
override val supportsLatest = true
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/filter-advance/page/$page/")
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/filter-advance/page/$page/?status=ongoing")
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val filterList = if (filters.isEmpty()) {
return GET("$baseUrl/?s=$query")
} else {
filters
}
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
val typeFilter = filterList.find { it is TypeFilter } as TypeFilter
val yearFilter = filterList.find { it is YearFilter } as YearFilter
val statusFilter = filterList.find { it is StatusFilter } as StatusFilter
val langFilter = filterList.find { it is LangFilter } as LangFilter
val filterPath = if (query.isEmpty()) "/filter-advance" else ""
var urlBuilder = "$baseUrl$filterPath/page/$page/".toHttpUrl().newBuilder()
when {
query.isNotEmpty() ->
urlBuilder =
urlBuilder.addQueryParameter("s", query)
typeFilter.state != 0 ->
urlBuilder =
urlBuilder.addQueryParameter("topic", typeFilter.toUriPart())
genreFilter.state != 0 ->
urlBuilder =
urlBuilder.addQueryParameter("genre", genreFilter.toUriPart())
yearFilter.state != 0 ->
urlBuilder =
urlBuilder.addQueryParameter("years", yearFilter.toUriPart())
statusFilter.state != 0 ->
urlBuilder =
urlBuilder.addQueryParameter("status", statusFilter.toUriPart())
langFilter.state != 0 ->
urlBuilder =
urlBuilder.addQueryParameter("typesub", langFilter.toUriPart())
}
return GET(urlBuilder.build().toString())
}
override fun searchAnimeSelector() = "div.ml-item"
override fun searchAnimeNextPageSelector() = "ul.pagination li:not(.active):last-child"
override fun searchAnimeFromElement(element: Element): SAnime {
val a = element.select("a:has(img)")
val img = a.select("img")
val h2 = a.select("span.mli-info > h2")
return SAnime.create().apply {
title = h2.text()
setUrlWithoutDomain(a.attr("href"))
thumbnail_url = img.attr("data-original")
}
}
override fun popularAnimeSelector() = searchAnimeSelector()
override fun latestUpdatesSelector() = searchAnimeSelector()
override fun popularAnimeNextPageSelector() = searchAnimeNextPageSelector()
override fun latestUpdatesNextPageSelector() = searchAnimeNextPageSelector()
override fun popularAnimeFromElement(element: Element) = searchAnimeFromElement(element)
override fun latestUpdatesFromElement(element: Element) = searchAnimeFromElement(element)
override fun animeDetailsParse(response: Response): SAnime {
val document = response.asJsoup()
return SAnime.create().apply {
title = document.select("h1[itemprop=name]").text()
status = parseStatus(
document.select(
"div.mvici-right > p:contains(Statut) > a:last-child",
).text(),
)
genre = document.select("div.mvici-left > p:contains(Genres)")
.text().substringAfter("Genres: ")
thumbnail_url = document.select("div.thumb > img")
.firstOrNull()?.attr("data-lazy-src")
description = document.select("div[itemprop=description]")
.firstOrNull()?.wholeText()?.trim()
?.substringAfter("\n")
}
}
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val type = document
.select("div.mvici-right > p:contains(Type) > a:last-child")
.text()
return if (type == "MOVIE") {
return listOf(
SEpisode.create().apply {
url = response.request.url.toString()
name = "Movie"
},
)
} else {
document.select(episodeListSelector()).map { episodeFromElement(it) }.reversed()
}
}
override fun episodeListSelector() = "div#seasonss > div.les-title > a"
override fun episodeFromElement(element: Element): SEpisode {
val number = element.text()
.substringAfterLast("-episode-")
.substringBefore("-")
return SEpisode.create().apply {
setUrlWithoutDomain(element.attr("href"))
name = "Épisode $number"
episode_number = number.toFloat()
}
}
override suspend fun getVideoList(episode: SEpisode): List<Video> {
val videoList = mutableListOf<Video>()
val url = if (episode.url.startsWith("https:")) {
episode.url
} else {
baseUrl + episode.url
}
val response = client.newCall(GET(url)).execute()
val parsedResponse = response.asJsoup()
if (parsedResponse.select("title").text().contains("Warning")) {
throw Exception(parsedResponse.select("body").text())
}
val epId = parsedResponse.select("link[rel=shortlink]").attr("href")
.substringAfter("?p=")
parsedResponse.select("div.list-server > select > option").forEach { server ->
videoList.addAll(
extractVideos(
server.attr("value"),
server.text(),
epId,
),
)
}
return videoList
}
private fun extractVideos(serverValue: String, serverName: String, epId: String): List<Video> {
Log.i("bruh", "ID: $epId \nLink: $")
val xhr = Headers.headersOf("x-requested-with", "XMLHttpRequest")
val epLink = client.newCall(GET("$baseUrl/ajax-get-link-stream/?server=$serverValue&filmId=$epId", xhr))
.execute().body.string()
val playlist = mutableListOf<Video>()
when {
epLink.contains("comedyshow.to") -> {
val playlistInterceptor = CloudFlareInterceptor()
val cfClient = client.newBuilder().addInterceptor(playlistInterceptor).build()
val headers = Headers.headersOf(
"referer",
"$baseUrl/",
"user-agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
)
val playlistResponse = cfClient.newCall(GET(epLink, headers)).execute().body.string()
val headersVideo = Headers.headersOf(
"referer",
epLink,
"user-agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
)
playlistResponse.substringAfter("#EXT-X-STREAM-INF:")
.split("#EXT-X-STREAM-INF:").map {
val quality = it.substringAfter("RESOLUTION=").split(",")[0].split("\n")[0].substringAfter("x") + "p ($serverName)"
val videoUrl = it.substringAfter("\n").substringBefore("\n")
playlist.add(Video(videoUrl, quality, videoUrl, headers = headersVideo))
}
}
epLink.contains("cdopetimes.xyz") -> {
val extractor = CdopeExtractor(client)
playlist.addAll(
extractor.videosFromUrl(epLink),
)
}
}
return playlist.sort()
}
override fun videoListSelector() = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", "720")
if (quality != null) {
val newList = mutableListOf<Video>()
var preferred = 0
for (video in this) {
if (video.quality.contains(quality)) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
}
return this
}
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.title = document.select("div.slide-middle h1").text()
anime.description = document.selectFirst("div.slide-desc")!!.ownText()
anime.genre = document.select("div.image-bg-content div.slide-block div.slide-middle ul.slide-top li.right a").joinToString { it.text() }
return anime
}
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
TypeFilter(),
GenreFilter(),
YearFilter(),
StatusFilter(),
LangFilter(),
)
private class TypeFilter : UriPartFilter(
"Type",
arrayOf(
Pair("-----", ""),
Pair("Anime", "anime"),
Pair("Cartoon", "cartoon"),
Pair("MOVIE", "movie"),
Pair("SERIES", "series"),
),
)
private class GenreFilter : UriPartFilterReverse(
"Genre",
arrayOf(
Pair("", "-----"),
Pair("action", "Action"),
Pair("adventure", "Adventure"),
Pair("animation", "Animation"),
Pair("martial-arts", "Arts martiaux"),
Pair("biography", "Biographie"),
Pair("comedy", "Comédie"),
Pair("crime", "Crime"),
Pair("demence", "Démence"),
Pair("demon", "Demons"),
Pair("documentaire", "Documentaire"),
Pair("drame", "Drama"),
Pair("ecchi", "Ecchi"),
Pair("enfants", "Enfants"),
Pair("espace", "Espace"),
Pair("famille", "Famille"),
Pair("fantasy", "Fantastique"),
Pair("game", "Game"),
Pair("harem", "Harem"),
Pair("historical", "Historique"),
Pair("horror", "Horreur"),
Pair("jeux", "Jeux"),
Pair("josei", "Josei"),
Pair("kids", "Kids"),
Pair("magic", "Magie"),
Pair("mecha", "Mecha"),
Pair("military", "Militaire"),
Pair("monster", "Monster"),
Pair("music", "Musique"),
Pair("mystere", "Mystère"),
Pair("parody", "Parodie"),
Pair("police", "Policier"),
Pair("psychological", "Psychologique"),
Pair("romance", "Romance"),
Pair("samurai", "Samurai"),
Pair("sci-fi", "Sci-Fi"),
Pair("school", "Scolaire"),
Pair("seinen", "Seinen"),
Pair("short", "Short"),
Pair("shoujo", "Shoujo"),
Pair("shoujo-ai", "Shoujo Ai"),
Pair("shounen", "Shounen"),
Pair("shounen-ai", "Shounen Ai"),
Pair("sport", "Sport"),
Pair("super-power", "Super Pouvoir"),
Pair("supernatural", "Surnaturel"),
Pair("suspense", "Suspense"),
Pair("thriller", "Thriller"),
Pair("silce-of-life", "Tranche de vie"),
Pair("vampire", "Vampire"),
Pair("cars", "Voitures"),
Pair("war", "War"),
Pair("western", "Western"),
),
)
private class YearFilter : UriPartFilterYears(
"Year",
Array(62) {
if (it == 0) {
"-----"
} else {
(2022 - (it - 1)).toString()
}
},
)
private class StatusFilter : UriPartFilter(
"Status",
arrayOf(
Pair("-----", ""),
Pair("Fin", "completed"),
Pair("En cours", "ongoing"),
),
)
private class LangFilter : UriPartFilter(
"La langue",
arrayOf(
Pair("-----", ""),
Pair("VO", "vo"),
Pair("Animé Vostfr", "vostfr"),
Pair("Animé VF", "vf"),
),
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
AnimeFilter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
private open class UriPartFilterReverse(displayName: String, val vals: Array<Pair<String, String>>) :
AnimeFilter.Select<String>(displayName, vals.map { it.second }.toTypedArray()) {
fun toUriPart() = vals[state].first
}
private open class UriPartFilterYears(displayName: String, val years: Array<String>) :
AnimeFilter.Select<String>(displayName, years) {
fun toUriPart() = years[state]
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Qualité préférée"
entries = arrayOf("720p", "360p")
entryValues = arrayOf("720", "360")
setDefaultValue("720")
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}
screen.addPreference(videoQualityPref)
}
private fun parseStatus(statusString: String): Int {
return when (statusString) {
"Fin" -> SAnime.COMPLETED
"En cours" -> SAnime.ONGOING
else -> SAnime.UNKNOWN
}
}
}

View file

@ -0,0 +1,88 @@
package eu.kanade.tachiyomi.animeextension.fr.animevostfr
import android.annotation.SuppressLint
import android.app.Application
import android.os.Handler
import android.os.Looper
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers.Companion.toHeaders
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
class CloudFlareInterceptor : Interceptor {
private val context = Injekt.get<Application>()
private val handler by lazy { Handler(Looper.getMainLooper()) }
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val newRequest = resolveWithWebView(originalRequest) ?: throw Exception("bruh")
return chain.proceed(newRequest)
}
@SuppressLint("SetJavaScriptEnabled")
private fun resolveWithWebView(request: Request): Request? {
// We need to lock this thread until the WebView finds the challenge solution url, because
// OkHttp doesn't support asynchronous interceptors.
val latch = CountDownLatch(1)
var webView: WebView? = null
val origRequestUrl = request.url.toString()
val headers = request.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }.toMutableMap()
var newRequest: Request? = null
handler.post {
val webview = WebView(context)
webView = webview
with(webview.settings) {
javaScriptEnabled = true
domStorageEnabled = true
databaseEnabled = true
useWideViewPort = false
loadWithOverviewMode = false
userAgentString = request.header("User-Agent")
?: "\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 Edg/88.0.705.63\""
}
webview.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest,
): WebResourceResponse? {
if (request.url.toString().contains("master.txt")) {
newRequest = GET(request.url.toString(), request.requestHeaders.toHeaders())
latch.countDown()
}
return super.shouldInterceptRequest(view, request)
}
}
webView?.loadUrl(origRequestUrl, headers)
}
// Wait a reasonable amount of time to retrieve the solution. The minimum should be
// around 4 seconds but it can take more due to slow networks or server issues.
latch.await(12, TimeUnit.SECONDS)
handler.post {
webView?.stopLoading()
webView?.destroy()
webView = null
}
return newRequest
}
}

View file

@ -0,0 +1,65 @@
package eu.kanade.tachiyomi.animeextension.fr.animevostfr.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.POST
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody
@Serializable
data class CdopeResponse(
val data: List<FileObject>,
) {
@Serializable
data class FileObject(
val file: String,
val label: String,
val type: String,
)
}
class CdopeExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val videoList = mutableListOf<Video>()
val id = url.substringAfter("/v/")
val body = "r=&d=cdopetimes.xyz".toRequestBody("application/x-www-form-urlencoded".toMediaType())
val headers = Headers.headersOf(
"Accept", "*/*",
"Content-Type", "application/x-www-form-urlencoded; charset=UTF-8",
"Host", "cdopetimes.xyz",
"Origin", "https://cdopetimes.xyz",
"Referer", url,
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0",
"X-Requested-With", "XMLHttpRequest",
)
val response = client.newCall(
POST("https://cdopetimes.xyz/api/source/$id", body = body, headers = headers),
).execute()
Json { ignoreUnknownKeys = true }.decodeFromString<CdopeResponse>(response.body.string()).data.forEach { file ->
val videoHeaders = Headers.headersOf(
"Accept",
"video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
"Referer",
"https://cdopetimes.xyz/",
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0",
)
videoList.add(
Video(
file.file,
"${file.label} (Cdope - ${file.type})",
file.file,
headers = videoHeaders,
),
)
}
return videoList
}
}

View file

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

View file

@ -183,7 +183,7 @@ class AniSama : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
private val voeExtractor by lazy { VoeExtractor(client) }
private val vidCdnExtractor by lazy { VidCdnExtractor(client) }
private val doodExtractor by lazy { DoodExtractor(client) }
private val streamHideVidExtractor by lazy { StreamHideVidExtractor(client) }
private val streamHideVidExtractor by lazy { StreamHideVidExtractor(client, headers) }
override fun videoListRequest(episode: SEpisode) = GET(
"$baseUrl${episode.url}",
@ -210,7 +210,7 @@ class AniSama : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
contains("sendvid.com") -> sendvidExtractor.videosFromUrl(this, prefix)
contains("voe.sx") -> voeExtractor.videosFromUrl(this, prefix)
contains(Regex("(d000d|dood)")) -> doodExtractor.videosFromUrl(this, "${prefix}DoodStream")
contains("vidhide") -> streamHideVidExtractor.videosFromUrl(this, prefix)
contains("vidhide") -> streamHideVidExtractor.videosFromUrl(this, videoNameGen = { "$prefix$it StreamHideVid" })
else -> emptyList()
}
}

View file

@ -2,21 +2,21 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".fr.franime.FrAnimeUrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay"
>
android:name=".fr.franime.FrAnimeUrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay"
>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="franime.fr"
android:pathPattern="/anime/..*"
/>
android:scheme="https"
android:host="franime.fr"
android:pathPattern="/anime/..*"
/>
</intent-filter>
</activity>
</application>
</manifest>
</manifest>

View file

@ -1,7 +1,7 @@
ext {
extName = 'FrAnime'
extClass = '.FrAnime'
extVersionCode = 13
extVersionCode = 16
isNsfw = true
}
apply from: "$rootDir/common.gradle"
@ -11,5 +11,5 @@ dependencies {
implementation(project(':lib:vk-extractor'))
implementation(project(':lib:sendvid-extractor'))
implementation(project(':lib:sibnet-extractor'))
implementation project(':lib:vidmoly-extractor')
implementation(project(':lib:vidmoly-extractor'))
}

View file

@ -20,6 +20,7 @@ typealias BigIntegerJson =
@OptIn(ExperimentalSerializationApi::class)
private object BigIntegerSerializer : KSerializer<BigInteger> {
override val descriptor = PrimitiveSerialDescriptor("java.math.BigInteger", PrimitiveKind.LONG)
override fun deserialize(decoder: Decoder): BigInteger =

View file

@ -3,7 +3,7 @@ ext {
extClass = '.FrenchAnime'
themePkg = 'datalifeengine'
baseUrl = 'https://french-anime.com'
overrideVersionCode = 7
overrideVersionCode = 16
}
apply from: "$rootDir/common.gradle"
@ -19,4 +19,6 @@ dependencies {
implementation(project(':lib:sibnet-extractor'))
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:streamhub-extractor'))
implementation(project(':lib:vidmoly-extractor'))
implementation(project(':lib:voe-extractor'))
}

View file

@ -10,7 +10,9 @@ import eu.kanade.tachiyomi.lib.streamhubextractor.StreamHubExtractor
import eu.kanade.tachiyomi.lib.streamvidextractor.StreamVidExtractor
import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.vidmolyextractor.VidMolyExtractor
import eu.kanade.tachiyomi.lib.vidoextractor.VidoExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.lib.vudeoextractor.VudeoExtractor
import eu.kanade.tachiyomi.multisrc.datalifeengine.DataLifeEngine
import eu.kanade.tachiyomi.network.GET
@ -86,21 +88,37 @@ class FrenchAnime : DataLifeEngine(
override fun episodeFromElement(element: Element): SEpisode = throw UnsupportedOperationException()
// ============================ Video Links =============================
private val doodExtractor by lazy { DoodExtractor(client) }
private val upstreamExtractor by lazy { UpstreamExtractor(client) }
private val vudeoExtractor by lazy { VudeoExtractor(client) }
private val uqloadExtractor by lazy { UqloadExtractor(client) }
private val streamHideVidExtractor by lazy { StreamHideVidExtractor(client, headers) }
private val streamVidExtractor by lazy { StreamVidExtractor(client) }
private val vidoExtractor by lazy { VidoExtractor(client) }
private val sibnetExtractor by lazy { SibnetExtractor(client) }
private val okruExtractor by lazy { OkruExtractor(client) }
private val streamHubExtractor by lazy { StreamHubExtractor(client) }
private val vidmolyExtractor by lazy { VidMolyExtractor(client) }
private val voeExtractor by lazy { VoeExtractor(client) }
override suspend fun getVideoList(episode: SEpisode): List<Video> {
val list = episode.url.split(",").filter { it.isNotBlank() }.parallelCatchingFlatMap {
with(it) {
when {
contains("dood") -> DoodExtractor(client).videosFromUrl(this)
contains("upstream") -> UpstreamExtractor(client).videosFromUrl(this)
contains("vudeo") -> VudeoExtractor(client).videosFromUrl(this)
contains("uqload") -> UqloadExtractor(client).videosFromUrl(this)
contains("dood") ||
contains("d0000d") -> doodExtractor.videosFromUrl(this)
contains("upstream") -> upstreamExtractor.videosFromUrl(this)
contains("vudeo") -> vudeoExtractor.videosFromUrl(this)
contains("uqload") -> uqloadExtractor.videosFromUrl(this)
contains("guccihide") ||
contains("streamhide") -> StreamHideVidExtractor(client).videosFromUrl(this)
contains("streamvid") -> StreamVidExtractor(client).videosFromUrl(this)
contains("vido") -> VidoExtractor(client).videosFromUrl(this)
contains("sibnet") -> SibnetExtractor(client).videosFromUrl(this)
contains("ok.ru") -> OkruExtractor(client).videosFromUrl(this)
contains("streamhub.gg") -> StreamHubExtractor(client).videosFromUrl(this)
contains("streamhide") -> streamHideVidExtractor.videosFromUrl(this)
contains("streamvid") -> streamVidExtractor.videosFromUrl(this)
contains("vido") -> vidoExtractor.videosFromUrl(this)
contains("sibnet") -> sibnetExtractor.videosFromUrl(this)
contains("ok.ru") -> okruExtractor.videosFromUrl(this)
contains("streamhub.gg") -> streamHubExtractor.videosFromUrl(this)
contains("vidmoly") -> vidmolyExtractor.videosFromUrl(this)
contains("voe.sx") -> voeExtractor.videosFromUrl(this)
else -> emptyList()
}
}

View file

@ -3,7 +3,7 @@ ext {
extClass = '.Hds'
themePkg = 'dooplay'
baseUrl = 'https://www.hds.quest'
overrideVersionCode = 2
overrideVersionCode = 3
}
apply from: "$rootDir/common.gradle"

View file

@ -51,7 +51,7 @@ class Hds : DooPlay(
data class VideoLinkDTO(@SerialName("embed_url") val url: String)
private val fileMoonExtractor by lazy { FilemoonExtractor(client) }
private val streamHideVidExtractor by lazy { StreamHideVidExtractor(client) }
private val streamHideVidExtractor by lazy { StreamHideVidExtractor(client, headers) }
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()

View file

@ -1,7 +1,7 @@
ext {
extName = 'OtakuFR'
extClass = '.OtakuFR'
extVersionCode = 15
extVersionCode = 23
}
apply from: "$rootDir/common.gradle"
@ -14,5 +14,6 @@ dependencies {
implementation(project(':lib:dood-extractor'))
implementation(project(':lib:okru-extractor'))
implementation(project(":lib:playlist-utils"))
implementation(project(":lib:upstream-extractor"))
implementation(libs.jsunpacker)
}

View file

@ -4,7 +4,6 @@ import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.fr.otakufr.extractors.UpstreamExtractor
import eu.kanade.tachiyomi.animeextension.fr.otakufr.extractors.VidbmExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
@ -18,6 +17,7 @@ import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.sendvidextractor.SendvidExtractor
import eu.kanade.tachiyomi.lib.sibnetextractor.SibnetExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
@ -37,7 +37,7 @@ class OtakuFR : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "OtakuFR"
override val baseUrl = "https://otakufr.co"
override val baseUrl = "https://otakufr.cc"
override val lang = "fr"
@ -145,7 +145,7 @@ class OtakuFR : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
private val vidbmExtractor by lazy { VidbmExtractor(client, headers) }
private val sendvidExtractor by lazy { SendvidExtractor(client, headers) }
private val upstreamExtractor by lazy { UpstreamExtractor(client, headers) }
private val upstreamExtractor by lazy { UpstreamExtractor(client) }
private val okruExtractor by lazy { OkruExtractor(client) }
private val doodExtractor by lazy { DoodExtractor(client) }
private val voeExtractor by lazy { VoeExtractor(client) }
@ -155,31 +155,28 @@ class OtakuFR : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val document = response.asJsoup()
val serversList = document.select("div.tab-content iframe[src]").mapNotNull {
val url = it.attr("abs:src")
val url = it.attr("abs:data-src")
if (url.contains("parisanime.com")) {
val docHeaders = headers.newBuilder().apply {
add("Accept", "*/*")
add("Host", url.toHttpUrl().host)
add("Referer", url)
add("X-Requested-With", "XMLHttpRequest")
set("X-Requested-With", "XMLHttpRequest")
}.build()
val newDoc = client.newCall(
GET(url, headers = docHeaders),
).execute().asJsoup()
newDoc.selectFirst("div[data-url]")?.attr("data-url")
val resUrl = newDoc.selectFirst("div[data-url]")?.attr("data-url")
if (resUrl!!.startsWith("//")) "https:$resUrl" else resUrl
} else {
url
}
}
return serversList.parallelCatchingFlatMapBlocking(::getHosterVideos)
}
private fun getHosterVideos(host: String): List<Video> {
return when {
host.startsWith("https://doo") -> doodExtractor.videosFromUrl(host, quality = "Doodstream")
host.startsWith("https://doo") || host.contains("d0000d")
-> doodExtractor.videosFromUrl(host, quality = "Doodstream")
host.contains("streamwish") -> streamwishExtractor.videosFromUrl(host)
host.contains("sibnet.ru") -> sibnetExtractor.videosFromUrl(host)
host.contains("vadbam") -> vidbmExtractor.videosFromUrl(host)

View file

@ -1,13 +1,17 @@
ext {
extName = 'Vostfree'
extClass = '.Vostfree'
extVersionCode = 19
extVersionCode = 25
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib:vudeo-extractor'))
implementation(project(':lib:dood-extractor'))
implementation(project(':lib:mixdrop-extractor'))
implementation(project(':lib:okru-extractor'))
}
implementation(project(':lib:sibnet-extractor'))
implementation(project(':lib:uqload-extractor'))
implementation(project(':lib:voe-extractor'))
implementation(project(':lib:vudeo-extractor'))
}

View file

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.animeextension.fr.vostfree
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
@ -13,11 +12,17 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.sibnetextractor.SibnetExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.lib.vudeoextractor.VudeoExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMap
import okhttp3.FormBody
import okhttp3.Request
import okhttp3.Response
@ -25,7 +30,6 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.lang.Exception
class Vostfree : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
@ -37,117 +41,35 @@ class Vostfree : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = false
private val preferences: SharedPreferences by lazy {
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun popularAnimeSelector(): String = "div#page-content div.page-left div#content div#dle-content div.movie-poster"
// ============================== Popular ===============================
override fun popularAnimeSelector() = "div#dle-content div.movie-poster"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/films-vf-vostfr/page/$page/")
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/films-vf-vostfr/page/$page/")
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(
element.select("div.movie-poster div.play a").attr("href"),
)
anime.title = element.select("div.movie-poster div.info.hidden div.title").text()
anime.thumbnail_url = baseUrl + element.select("div.movie-poster span.image img").attr("src")
return anime
}
override fun popularAnimeNextPageSelector(): String = "span.next-page"
override fun episodeListParse(response: Response): List<SEpisode> {
val episodes = mutableListOf<SEpisode>()
val jsoup = response.asJsoup()
jsoup.select("select.new_player_selector option").forEachIndexed { index, it ->
val epNum = it.text().replace("Episode", "").drop(2)
if (it.text() == "Film") {
val episode = SEpisode.create().apply {
episode_number = "1".toFloat()
name = "Film"
}
episode.url = "?episode:0/${response.request.url}"
episodes.add(episode)
} else {
val episode = SEpisode.create().apply {
episode_number = epNum.toFloat()
name = "Épisode $epNum"
}
episode.setUrlWithoutDomain("?episode:$index/${response.request.url}")
episodes.add(episode)
}
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
with(element.selectFirst("a")!!) {
setUrlWithoutDomain(attr("href"))
title = text()
}
return episodes
thumbnail_url = element.selectFirst("span.image img")?.absUrl("src")
}
override fun episodeListSelector() = throw UnsupportedOperationException()
override fun popularAnimeNextPageSelector() = "span.next-page"
override fun episodeFromElement(element: Element) = throw UnsupportedOperationException()
// =============================== Latest ===============================
override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException()
override fun videoListParse(response: Response): List<Video> {
val epNum = response.request.url.toString().substringAfter("$baseUrl/?episode:").substringBefore("/")
val realUrl = response.request.url.toString().replace("$baseUrl/?episode:$epNum/", "")
override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException()
val document = client.newCall(GET(realUrl)).execute().asJsoup()
val videoList = mutableListOf<Video>()
val allPlayerIds = document.select("div.tab-content div div.new_player_top div.new_player_bottom div.button_box")[epNum.toInt()]
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
allPlayerIds.select("div").forEach {
val server = it.text()
if (server.lowercase() == "vudeo") {
val playerId = it.attr("id")
val url = document.select("div#player-tabs div.tab-blocks div.tab-content div div#content_$playerId").text()
runCatching {
val video = VudeoExtractor(client).videosFromUrl(url)
videoList.addAll(video)
}
}
if (server.lowercase() == "ok") {
val playerId = it.attr("id")
val url = "https://ok.ru/videoembed/" + document.select("div#player-tabs div.tab-blocks div.tab-content div div#content_$playerId").text()
val video = OkruExtractor(client).videosFromUrl(url, "", false)
videoList.addAll(video)
}
if (server.lowercase() == "doodstream") {
val playerId = it.attr("id")
val url = document.select("div#player-tabs div.tab-blocks div.tab-content div div#content_$playerId").text()
val video = DoodExtractor(client).videoFromUrl(url, "DoodStream", false)
if (video != null) {
videoList.add(video)
}
}
}
return videoList
}
override fun videoListSelector() = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", "Vudeo")
if (quality != null) {
val newList = mutableListOf<Video>()
var preferred = 0
for (video in this) {
if (video.quality == quality) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
}
return this
}
override fun latestUpdatesSelector() = throw UnsupportedOperationException()
// =============================== Search ===============================
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val filterList = if (filters.isEmpty()) getFilterList() else filters
val genreFilter = filterList.find { it is GenreFilter } as GenreFilter
@ -163,62 +85,114 @@ class Vostfree : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
.build()
return when {
query.isNotBlank() -> try { POST("$baseUrl/index.php?do=search", headers, formData) } catch (e: Exception) { GET("$baseUrl/animes-vostfr/page/$page/") }
query.isNotBlank() -> POST("$baseUrl/index.php?do=search", headers, formData)
genreFilter.state != 0 -> GET("$baseUrl/genre/${genreFilter.toUriPart()}/page/$page/")
typeFilter.state != 0 -> GET("$baseUrl/${typeFilter.toUriPart()}/page/$page/")
else -> GET("$baseUrl/animes-vostfr/page/$page/")
}
}
override fun searchAnimeSelector() = "div#dle-content div.search-result, " + popularAnimeSelector()
override fun searchAnimeParse(response: Response): AnimesPage {
val document = response.asJsoup()
val animeList = document.select("div.search-result")
val animeList2 = document.select("div.movie-poster")
val animes = animeList.map {
searchAnimeFromElement(it)
} + animeList2.map {
searchAnimeFromElement(it)
}
val animes = document.select(searchAnimeSelector()).map(::searchAnimeFromElement)
return AnimesPage(animes, false)
}
override fun searchAnimeFromElement(element: Element): SAnime {
return when {
element.select("div.search-result").toString() != "" -> searchPopularAnimeFromElement(element)
else -> popularAnimeFromElement(element)
}
override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector()
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document) = SAnime.create().apply {
title = document.select("div.slide-middle h1").text()
description = document.selectFirst("div.slide-desc")?.ownText()
thumbnail_url = document.selectFirst("div.slide-poster img")?.absUrl("src")
genre = document.select("li.right b.fa-bookmark-o ~ a").joinToString { it.text() }
}
private fun searchPopularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(
element.select("div.search-result div.info div.title a").attr("href"),
)
anime.title = element.select("div.search-result div.info div.title a").text()
anime.thumbnail_url = baseUrl + element.select("div.search-result span.image img").attr("src")
return anime
// ============================== Episodes ==============================
override fun episodeListParse(response: Response): List<SEpisode> {
val doc = response.asJsoup()
val epUrl = response.request.url.toString()
return doc.select("select.new_player_selector option").map { it ->
if (it.text() == "Film") {
SEpisode.create().apply {
episode_number = 1F
name = "Film"
setUrlWithoutDomain("$epUrl?episode=1")
}
} else {
SEpisode.create().apply {
val epNum = it.text().substringAfter(" ").toInt()
episode_number = epNum.toFloat()
name = "Épisode $epNum"
setUrlWithoutDomain("$epUrl?episode=$epNum")
}
}
}.reversed()
}
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
override fun episodeListSelector() = throw UnsupportedOperationException()
override fun searchAnimeSelector(): String = "div#dle-content"
override fun episodeFromElement(element: Element) = throw UnsupportedOperationException()
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.title = document.select("div.slide-middle h1").text()
anime.description = document.selectFirst("div.slide-desc")!!.ownText()
anime.genre = document.select("div.image-bg-content div.slide-block div.slide-middle ul.slide-top li.right a").joinToString { it.text() }
return anime
// ============================ Video Links =============================
private val doodExtractor by lazy { DoodExtractor(client) }
private val mixdropExtractor by lazy { MixDropExtractor(client) }
private val okruExtractor by lazy { OkruExtractor(client) }
private val sibnetExtractor by lazy { SibnetExtractor(client) }
private val uqloadExtractor by lazy { UqloadExtractor(client) }
private val voeExtractor by lazy { VoeExtractor(client) }
private val vudeoExtractor by lazy { VudeoExtractor(client) }
override fun videoListParse(response: Response) = throw UnsupportedOperationException()
private val hostPrefixes = mapOf(
"ok" to "https://ok.ru/videoembed/",
"sibnet" to "https://video.sibnet.ru/shell.php?videoid=",
"uqload" to "https://uqload.io/embed-",
)
override suspend fun getVideoList(episode: SEpisode): List<Video> {
val epNum = episode.url.substringAfter("=")
val document = client.newCall(GET(baseUrl + episode.url, headers)).await().asJsoup()
val allPlayers = document.select("div#buttons_$epNum div")
return allPlayers.parallelCatchingFlatMap {
val server = it.text().lowercase()
val id = it.attr("id")
val content = document.selectFirst("div#content_$id")?.text() ?: return@parallelCatchingFlatMap emptyList()
when (server) {
"doodstream" -> doodExtractor.videosFromUrl(content, "DoodStream")
"mixdrop" -> mixdropExtractor.videosFromUrl(content)
"ok" -> okruExtractor.videosFromUrl(hostPrefixes[server] + content)
"sibnet" -> sibnetExtractor.videosFromUrl(hostPrefixes[server] + content)
"uqload" -> uqloadExtractor.videosFromUrl(hostPrefixes[server] + content + ".html")
"voe" -> voeExtractor.videosFromUrl(content)
"vudeo" -> vudeoExtractor.videosFromUrl(content)
else -> emptyList()
}
}.sort()
}
override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException()
override fun videoListSelector() = throw UnsupportedOperationException()
override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
override fun latestUpdatesSelector() = throw UnsupportedOperationException()
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
return sortedWith(
compareBy { it.quality.contains(quality) },
).reversed()
}
// ============================== Filters ===============================
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
TypeFilter(),
GenreFilter(),
@ -257,22 +231,32 @@ class Vostfree : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
fun toUriPart() = vals[state].second
}
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("Vudeo", "Doodstream")
entryValues = arrayOf("Vudeo", "Doodstream")
setDefaultValue("Vudeo")
ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY
title = PREF_QUALITY_TITLE
entries = PREF_QUALITY_ENTRIES
setDefaultValue(PREF_QUALITY_DEFAULT)
summary = "%s"
}.also(screen::addPreference)
}
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}
screen.addPreference(videoQualityPref)
companion object {
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Preferred quality"
private const val PREF_QUALITY_DEFAULT = "Vudeo"
private val PREF_QUALITY_ENTRIES = arrayOf(
"1080p",
"360p",
"480p",
"720p",
"Doodstream",
"MixDrop",
"Sibnet",
"Uqload",
"Voe",
"Vudeo",
)
}
}

View file

@ -3,7 +3,7 @@ ext {
extClass = '.Wiflix'
themePkg = 'datalifeengine'
baseUrl = 'https://wiflix.voto'
overrideVersionCode = 8
overrideVersionCode = 14
}
apply from: "$rootDir/common.gradle"

View file

@ -83,7 +83,7 @@ class Wiflix : DataLifeEngine(
contains("uqload.co") -> UqloadExtractor(client).videosFromUrl(this)
contains("waaw1.tv") -> emptyList()
contains("vudeo.co") -> VudeoExtractor(client).videosFromUrl(this)
contains("streamvid.net") -> StreamHideVidExtractor(client).videosFromUrl(this)
contains("streamvid.net") -> StreamHideVidExtractor(client, headers).videosFromUrl(this)
contains("upstream.to") -> UpstreamExtractor(client).videosFromUrl(this)
contains("streamdav.com") -> StreamDavExtractor(client).videosFromUrl(this)
contains("voe.sx") -> VoeExtractor(client).videosFromUrl(this)