Initial commit
13
src/hi/animesaga/build.gradle
Normal file
|
@ -0,0 +1,13 @@
|
|||
ext {
|
||||
extName = 'AnimeSAGA'
|
||||
extClass = '.AnimeSAGA'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://www.animesaga.in'
|
||||
overrideVersionCode = 9
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(":lib:chillx-extractor"))
|
||||
}
|
BIN
src/hi/animesaga/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/hi/animesaga/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
src/hi/animesaga/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
src/hi/animesaga/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
src/hi/animesaga/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,62 @@
|
|||
package eu.kanade.tachiyomi.animeextension.hi.animesaga
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.lib.chillxextractor.ChillxExtractor
|
||||
import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class AnimeSAGA : DooPlay(
|
||||
"hi",
|
||||
"AnimeSAGA",
|
||||
"https://www.animesaga.in",
|
||||
) {
|
||||
private val videoHost = "https://cdn.animesaga.in"
|
||||
|
||||
// ============================== Popular ===============================
|
||||
override fun popularAnimeSelector() = "div.top-imdb-list > div.top-imdb-item"
|
||||
|
||||
// ============================ Video Links =============================
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val playerUrls = response.asJsoup()
|
||||
.select("ul#playeroptionsul li:not([id=player-option-trailer])")
|
||||
.map(::getPlayerUrl)
|
||||
|
||||
return playerUrls.flatMap { url ->
|
||||
runCatching {
|
||||
getPlayerVideos(url)
|
||||
}.getOrElse { emptyList() }
|
||||
}
|
||||
}
|
||||
|
||||
private val chillxExtractor by lazy { ChillxExtractor(client, headers) }
|
||||
|
||||
private fun getPlayerVideos(url: String): List<Video> {
|
||||
return when {
|
||||
videoHost in url -> chillxExtractor.videoFromUrl(url, "$baseUrl/")
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPlayerUrl(player: Element): String {
|
||||
val body = FormBody.Builder()
|
||||
.add("action", "doo_player_ajax")
|
||||
.add("post", player.attr("data-post"))
|
||||
.add("nume", player.attr("data-nume"))
|
||||
.add("type", player.attr("data-type"))
|
||||
.build()
|
||||
|
||||
return client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", headers, body))
|
||||
.execute()
|
||||
.let { response ->
|
||||
response
|
||||
.body.string()
|
||||
.substringAfter("\"embed_url\":\"")
|
||||
.substringBefore("\",")
|
||||
.replace("\\", "")
|
||||
}
|
||||
}
|
||||
}
|
15
src/hi/yomovies/build.gradle
Normal file
|
@ -0,0 +1,15 @@
|
|||
ext {
|
||||
extName = 'YoMovies'
|
||||
extClass = '.YoMovies'
|
||||
extVersionCode = 13
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(':lib:dood-extractor'))
|
||||
implementation(project(':lib:mixdrop-extractor'))
|
||||
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
|
||||
implementation(project(':lib:playlist-utils'))
|
||||
}
|
BIN
src/hi/yomovies/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/hi/yomovies/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/hi/yomovies/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
src/hi/yomovies/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
src/hi/yomovies/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/hi/yomovies/res/web_hi_res_512.png
Normal file
After Width: | Height: | Size: 62 KiB |
|
@ -0,0 +1,421 @@
|
|||
package eu.kanade.tachiyomi.animeextension.hi.yomovies
|
||||
|
||||
import android.app.Application
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animeextension.hi.yomovies.extractors.MinoplresExtractor
|
||||
import eu.kanade.tachiyomi.animeextension.hi.yomovies.extractors.MovembedExtractor
|
||||
import eu.kanade.tachiyomi.animeextension.hi.yomovies.extractors.SpeedostreamExtractor
|
||||
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 eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
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
|
||||
import java.lang.Exception
|
||||
|
||||
class YoMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
override val name = "YoMovies"
|
||||
|
||||
// TODO: Check frequency of url changes to potentially
|
||||
// add back overridable baseurl preference
|
||||
override val baseUrl = "https://yomovies.town"
|
||||
|
||||
override val lang = "hi"
|
||||
|
||||
override val supportsLatest = false
|
||||
|
||||
private val preferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
// ============================== Popular ===============================
|
||||
|
||||
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/most-favorites/".addPage(page), headers)
|
||||
|
||||
override fun popularAnimeSelector(): String = "div.movies-list > div.ml-item"
|
||||
|
||||
override fun popularAnimeFromElement(element: Element): SAnime = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.selectFirst("a[href]")!!.attr("href"))
|
||||
thumbnail_url = element.selectFirst("img[data-original]")?.attr("abs:data-original") ?: ""
|
||||
title = element.selectFirst("div.qtip-title")!!.text()
|
||||
}
|
||||
|
||||
override fun popularAnimeNextPageSelector(): String = "ul.pagination > li.active + li"
|
||||
|
||||
// =============================== Latest ===============================
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
|
||||
|
||||
override fun latestUpdatesSelector(): String = throw UnsupportedOperationException()
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element): SAnime = throw UnsupportedOperationException()
|
||||
|
||||
override fun latestUpdatesNextPageSelector(): String = throw UnsupportedOperationException()
|
||||
|
||||
// =============================== Search ===============================
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
||||
val firstSelected = filterList.firstOrNull {
|
||||
it.state != 0
|
||||
}?.let { it as UriPartFilter }
|
||||
|
||||
return when {
|
||||
query.isNotBlank() -> GET("$baseUrl/?s=$query".addPage(page), headers)
|
||||
firstSelected != null -> GET("$baseUrl${firstSelected.toUriPart()}".addPage(page), headers)
|
||||
else -> throw Exception("Either search or")
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchAnimeSelector(): String = popularAnimeSelector()
|
||||
|
||||
override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element)
|
||||
|
||||
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
|
||||
|
||||
// =========================== Anime Details ============================
|
||||
|
||||
override fun animeDetailsParse(document: Document): SAnime {
|
||||
val infoDiv = document.selectFirst("div.mvi-content")!!
|
||||
|
||||
return SAnime.create().apply {
|
||||
description = infoDiv.selectFirst("p.f-desc")?.text()
|
||||
genre = infoDiv.select("div.mvici-left > p:contains(Genre:) a").joinToString(", ") { it.text() }
|
||||
author = infoDiv.select("div.mvici-left > p:contains(Studio:) a").joinToString(", ") { it.text() }
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== Episodes ==============================
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
val document = response.asJsoup()
|
||||
val episodeList = mutableListOf<SEpisode>()
|
||||
|
||||
val seasonList = document.select("div#seasons > div.tvseason")
|
||||
// For movies
|
||||
if (seasonList.size == 0) {
|
||||
episodeList.add(
|
||||
SEpisode.create().apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
name = "Movie"
|
||||
episode_number = 1F
|
||||
},
|
||||
)
|
||||
} else {
|
||||
seasonList.forEach { season ->
|
||||
val seasonText = season.selectFirst("div.les-title")!!.text().trim()
|
||||
|
||||
season.select(episodeListSelector()).forEachIndexed { index, ep ->
|
||||
val epNumber = ep.text().trim().substringAfter("pisode ")
|
||||
|
||||
episodeList.add(
|
||||
SEpisode.create().apply {
|
||||
setUrlWithoutDomain(ep.attr("abs:href"))
|
||||
name = "$seasonText Ep. $epNumber"
|
||||
episode_number = epNumber.toFloatOrNull() ?: (index + 1).toFloat()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return episodeList.reversed()
|
||||
}
|
||||
|
||||
override fun episodeListSelector() = "div.les-content > a"
|
||||
|
||||
override fun episodeFromElement(element: Element): SEpisode = throw UnsupportedOperationException()
|
||||
|
||||
// ============================ Video Links =============================
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
|
||||
val videoList = document.select("div[id*=tab]:has(div.movieplay > iframe)")
|
||||
.parallelCatchingFlatMapBlocking { server ->
|
||||
val iframe = server.selectFirst("div.movieplay > iframe")!!
|
||||
val name = document.selectFirst("ul.idTabs > li:has(a[href=#${server.id()}]) div.les-title")
|
||||
?.text()
|
||||
?.let { "[$it] - " }
|
||||
.orEmpty()
|
||||
|
||||
extractVideosFromIframe(iframe.attr("abs:src"), name)
|
||||
}
|
||||
|
||||
return videoList.sort()
|
||||
}
|
||||
|
||||
private fun extractVideosFromIframe(iframeUrl: String, name: String): List<Video> {
|
||||
return when {
|
||||
iframeUrl.contains("speedostream") -> {
|
||||
SpeedostreamExtractor(client, headers).videosFromUrl(iframeUrl, "$baseUrl/", prefix = name)
|
||||
}
|
||||
|
||||
// Prepending the server name probably isn't needed for this,
|
||||
// since it doesn't do different episodes for different servers from what I can tell
|
||||
iframeUrl.contains("movembed.cc") -> {
|
||||
MovembedExtractor(client, headers).videosFromUrl(iframeUrl)
|
||||
}
|
||||
|
||||
iframeUrl.contains("minoplres") -> {
|
||||
MinoplresExtractor(client, headers).videosFromUrl(iframeUrl, name)
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun videoListSelector() = throw UnsupportedOperationException()
|
||||
|
||||
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
|
||||
|
||||
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
|
||||
|
||||
// ============================= Utilities ==============================
|
||||
|
||||
private fun String.addPage(page: Int): String {
|
||||
return if (page == 1) {
|
||||
this
|
||||
} else {
|
||||
this.toHttpUrl().newBuilder()
|
||||
.addPathSegment("page")
|
||||
.addPathSegment(page.toString())
|
||||
.toString()
|
||||
}
|
||||
}
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
|
||||
|
||||
return sortedWith(
|
||||
compareBy(
|
||||
{ it.quality.contains(quality) },
|
||||
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
|
||||
),
|
||||
).reversed()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PREF_QUALITY_KEY = "preferred_quality"
|
||||
private const val PREF_QUALITY_DEFAULT = "1080"
|
||||
}
|
||||
|
||||
// ============================== Settings ==============================
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_QUALITY_KEY
|
||||
title = "Preferred quality"
|
||||
entries = arrayOf("1080p", "720p", "480p", "360p")
|
||||
entryValues = arrayOf("1080", "720", "480", "360")
|
||||
setDefaultValue(PREF_QUALITY_DEFAULT)
|
||||
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()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
|
||||
// ============================== Filters ===============================
|
||||
|
||||
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
|
||||
AnimeFilter.Header("Note: Only one selection at a time works, and it ignores text search"),
|
||||
AnimeFilter.Separator(),
|
||||
BollywoodFilter(),
|
||||
DualAudioFilter(),
|
||||
HollywoodFilter(),
|
||||
EnglishSeriesFilter(),
|
||||
HindiSeriesFilter(),
|
||||
GenreFilter(),
|
||||
ExtraMoviesFilter(),
|
||||
EroticFilter(),
|
||||
HotSeriesFilter(),
|
||||
)
|
||||
|
||||
// Note for filters, clicking on the "category selector" yields its own page
|
||||
|
||||
// $("ul.top-menu > li:has(a:contains(Bollywood)) ul > li").map((i,el) => `Pair("${$(el).text().trim()}", "${$(el).find("a").first().attr('href').trim().replace('https://yomovies.baby', '')}")`).get().join(',\n')
|
||||
// on /
|
||||
private class BollywoodFilter : UriPartFilter(
|
||||
"Bollywood",
|
||||
arrayOf(
|
||||
Pair("<select>", ""),
|
||||
Pair("Bollywood", "/genre/bollywood/"),
|
||||
Pair("Trending", "/genre/top-rated/"),
|
||||
Pair("Bollywood (2024)", "/account/?ptype=post&tax_category%5B%5D=bollywood&tax_release-year=2024&wpas=1"),
|
||||
Pair("Bollywood (2023)", "/account/?ptype=post&tax_category%5B%5D=bollywood&tax_release-year=2023&wpas=1"),
|
||||
Pair("Bollywood (2022)", "/account/?ptype=post&tax_category%5B%5D=bollywood&tax_release-year=2022&wpas=1"),
|
||||
Pair("Bollywood (2021)", "/account/?ptype=post&tax_category%5B%5D=bollywood&tax_release-year=2021&wpas=1"),
|
||||
),
|
||||
)
|
||||
|
||||
// $("ul.top-menu > li:has(a:contains(Dual audio)) ul > li").map((i,el) => `Pair("${$(el).text().trim()}", "${$(el).find("a").first().attr('href').trim().replace('https://yomovies.baby', '')}")`).get().join(',\n')
|
||||
// on /
|
||||
private class DualAudioFilter : UriPartFilter(
|
||||
"Dual Audio",
|
||||
arrayOf(
|
||||
Pair("<select>", ""),
|
||||
Pair("Dual Audio", "/genre/dual-audio/"),
|
||||
Pair("Hollywood Dubbed", "/account/?ptype=post&tax_category%5B%5D=dual-audio&wpas=1"),
|
||||
Pair("South Dubbed", "/account/?ptype=post&tax_category%5B%5D=dual-audio&tax_category%5B%5D=south-special&wpas=1"),
|
||||
),
|
||||
)
|
||||
|
||||
// $("ul.top-menu > li:has(a:contains(Hollywood)) ul > li").map((i,el) => `Pair("${$(el).text().trim()}", "${$(el).find("a").first().attr('href').trim().replace('https://yomovies.baby', '')}")`).get().join(',\n')
|
||||
// on /
|
||||
private class HollywoodFilter : UriPartFilter(
|
||||
"Hollywood",
|
||||
arrayOf(
|
||||
Pair("<select>", ""),
|
||||
Pair("Hollywood", "/genre/hollywood/"),
|
||||
Pair("Hollywood (2024)", "/account/?ptype=post&tax_category%5B%5D=hollywood&tax_release-year=2024&wpas=1"),
|
||||
Pair("Hollywood (2023)", "/account/?ptype=post&tax_category%5B%5D=hollywood&tax_release-year=2023&wpas=1"),
|
||||
Pair("Hollywood (2022)", "/account/?ptype=post&tax_category%5B%5D=hollywood&tax_release-year=2022&wpas=1"),
|
||||
Pair("Hollywood (2021)", "/account/?ptype=post&tax_category%5B%5D=hollywood&tax_release-year=2021&wpas=1"),
|
||||
),
|
||||
)
|
||||
|
||||
private class EnglishSeriesFilter : UriPartFilter(
|
||||
"English Series",
|
||||
arrayOf(
|
||||
Pair("<select>", ""),
|
||||
Pair("English Series", "/series/"),
|
||||
),
|
||||
)
|
||||
|
||||
// $("ul.top-menu > li:has(a:contains(Hindi Series)) ul > li").map((i,el) => `Pair("${$(el).text().trim()}", "${$(el).find("a").first().attr('href').trim().replace('https://yomovies.baby', '')}")`).get().join(',\n')
|
||||
// on /
|
||||
private class HindiSeriesFilter : UriPartFilter(
|
||||
"Hindi Series",
|
||||
arrayOf(
|
||||
Pair("<select>", ""),
|
||||
Pair("Hindi Series", "/genre/web-series/"),
|
||||
Pair("Netflix", "/director/netflix/"),
|
||||
Pair("Amazon", "/director/amazon-prime/"),
|
||||
Pair("Altbalaji", "/director/altbalaji/"),
|
||||
Pair("Zee5", "/director/zee5/"),
|
||||
Pair("Voot", "/director/voot-originals/"),
|
||||
Pair("Mx Player", "/director/mx-player/"),
|
||||
Pair("Hotstar", "/director/hotstar/"),
|
||||
Pair("Viu", "/director/viu-originals/"),
|
||||
Pair("Sony Liv", "/director/sonyliv-original/"),
|
||||
),
|
||||
)
|
||||
|
||||
// $("ul.top-menu > li:has(a:contains(Genre)) ul > li").map((i,el) => `Pair("${$(el).text().trim()}", "${$(el).find("a").first().attr('href').trim().replace('https://yomovies.baby', '')}")`).get().join(',\n')
|
||||
// on /
|
||||
private class GenreFilter : UriPartFilter(
|
||||
"Genre",
|
||||
arrayOf(
|
||||
Pair("<select>", ""),
|
||||
Pair("Action", "/genre/action/"),
|
||||
Pair("Adventure", "/genre/adventure/"),
|
||||
Pair("Animation", "/genre/animation/"),
|
||||
Pair("Biography", "/genre/biography/"),
|
||||
Pair("Comedy", "/genre/comedy/"),
|
||||
Pair("Crime", "/genre/crime/"),
|
||||
Pair("Drama", "/genre/drama/"),
|
||||
Pair("Music", "/genre/music/"),
|
||||
Pair("Mystery", "/genre/mystery/"),
|
||||
Pair("Family", "/genre/family/"),
|
||||
Pair("Fantasy", "/genre/fantasy/"),
|
||||
Pair("Horror", "/genre/horror/"),
|
||||
Pair("History", "/genre/history/"),
|
||||
Pair("Romance", "/genre/romantic/"),
|
||||
Pair("Science Fiction", "/genre/science-fiction/"),
|
||||
Pair("Thriller", "/genre/thriller/"),
|
||||
Pair("War", "/genre/war/"),
|
||||
),
|
||||
)
|
||||
|
||||
// $("ul.top-menu > li:has(a:contains(ExtraMovies)) ul > li").map((i,el) => `Pair("${$(el).text().trim()}", "${$(el).find("a").first().attr('href').trim().replace('https://yomovies.baby', '')}")`).get().join(',\n')
|
||||
// on /
|
||||
private class ExtraMoviesFilter : UriPartFilter(
|
||||
"ExtraMovies",
|
||||
arrayOf(
|
||||
Pair("<select>", ""),
|
||||
Pair("ExtraMovies", "/genre/south-special/"),
|
||||
Pair("Bengali", "/genre/bengali/"),
|
||||
Pair("Marathi", "/genre/marathi/"),
|
||||
Pair("Gujarati", "/genre/gujarati/"),
|
||||
Pair("Punjabi", "/genre/punjabi/"),
|
||||
Pair("Tamil", "/genre/tamil/"),
|
||||
Pair("Telugu", "/genre/telugu/"),
|
||||
Pair("Malayalam", "/genre/malayalam/"),
|
||||
Pair("Kannada", "/genre/kannada/"),
|
||||
Pair("Pakistani", "/genre/pakistani/"),
|
||||
),
|
||||
)
|
||||
|
||||
private class EroticFilter : UriPartFilter(
|
||||
"Erotic",
|
||||
arrayOf(
|
||||
Pair("<select>", ""),
|
||||
Pair("Erotic", "/genre/erotic-movies/"),
|
||||
),
|
||||
)
|
||||
|
||||
// $("ul.top-menu > li:has(a:contains(Hot Series)) ul > li").map((i,el) => `Pair("${$(el).text().trim()}", "${$(el).find("a").first().attr('href').trim().replace('https://yomovies.baby', '')}")`).get().join(',\n')
|
||||
// on /
|
||||
private class HotSeriesFilter : UriPartFilter(
|
||||
"Hot Series",
|
||||
arrayOf(
|
||||
Pair("<select>", ""),
|
||||
Pair("Hot Series", "/genre/tv-shows/"),
|
||||
Pair("Uncut", "/?s=uncut"),
|
||||
Pair("Fliz Movies", "/director/fliz-movies/"),
|
||||
Pair("Nuefliks", "/director/nuefliks-exclusive/"),
|
||||
Pair("Hotshots", "/director/hotshots/"),
|
||||
Pair("Ullu Originals", "/?s=ullu"),
|
||||
Pair("Kooku", "/director/kooku-originals/"),
|
||||
Pair("Gupchup", "/director/gupchup-exclusive/"),
|
||||
Pair("Feneomovies", "/director/feneomovies/"),
|
||||
Pair("Cinemadosti", "/director/cinemadosti/"),
|
||||
Pair("Primeflix", "/director/primeflix/"),
|
||||
Pair("Gemplex", "/director/gemplex/"),
|
||||
Pair("Rabbit", "/director/rabbit-original/"),
|
||||
Pair("HotMasti", "/director/hotmasti-originals/"),
|
||||
Pair("BoomMovies", "/director/boommovies-original/"),
|
||||
Pair("CliffMovies", "/director/cliff-movies/"),
|
||||
Pair("MastiPrime", "/director/masti-prime-originals/"),
|
||||
Pair("Ek Night Show", "/director/ek-night-show/"),
|
||||
Pair("Flixsksmovies", "/director/flixsksmovies/"),
|
||||
Pair("Lootlo", "/director/lootlo-original/"),
|
||||
Pair("Hootzy", "/director/hootzy-channel/"),
|
||||
Pair("Balloons", "/director/balloons-originals/"),
|
||||
Pair("Big Movie Zoo", "/director/big-movie-zoo-originals/"),
|
||||
Pair("Bambooflix", "/director/bambooflix/"),
|
||||
Pair("Piliflix", "/director/piliflix-originals/"),
|
||||
Pair("11upmovies", "/director/11upmovies-originals/"),
|
||||
Pair("Eightshots", "/director/eightshots-originals/"),
|
||||
Pair("I-Entertainment", "/director/i-entertainment-exclusive/"),
|
||||
Pair("Hotprime", "/director/hotprime-originals/"),
|
||||
Pair("BananaPrime", "/director/banana-prime/"),
|
||||
Pair("HotHitFilms", "/director/hothitfilms/"),
|
||||
Pair("Chikooflix", "/director/chikooflix-originals/"),
|
||||
Pair("Glamheart", "/?s=glamheart"),
|
||||
Pair("Worldprime", "/director/worldprime-originals/"),
|
||||
),
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package eu.kanade.tachiyomi.animeextension.hi.yomovies.extractors
|
||||
|
||||
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.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class MinoplresExtractor(private val client: OkHttpClient, private val headers: Headers) {
|
||||
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
|
||||
|
||||
fun videosFromUrl(url: String, name: String): List<Video> {
|
||||
val newHeaders = headers.newBuilder().set("Referer", url).build()
|
||||
val doc = client.newCall(GET(url, newHeaders)).execute()
|
||||
.asJsoup()
|
||||
val script = doc.selectFirst("script:containsData(sources:)")?.data()
|
||||
?: return emptyList()
|
||||
|
||||
val masterUrl = script.substringAfter("file:\"").substringBefore('"')
|
||||
return playlistUtils.extractFromHls(
|
||||
masterUrl,
|
||||
referer = url,
|
||||
videoNameGen = { "$name Minoplres - $it" },
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package eu.kanade.tachiyomi.animeextension.hi.yomovies.extractors
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.Track
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
|
||||
import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class MovembedExtractor(private val client: OkHttpClient, private val headers: Headers) {
|
||||
fun videosFromUrl(url: String): List<Video> {
|
||||
val document = client.newCall(
|
||||
GET(url, headers),
|
||||
).execute().asJsoup()
|
||||
|
||||
return document.select("ul.list-server-items > li.linkserver")
|
||||
.parallelCatchingFlatMapBlocking { server ->
|
||||
extractVideosFromIframe(server.attr("abs:data-video"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractVideosFromIframe(iframeUrl: String): List<Video> {
|
||||
return when {
|
||||
MIXDROP_DOMAINS.any { iframeUrl.contains(it) } -> {
|
||||
val url = iframeUrl.toHttpUrl()
|
||||
val subtitleList = url.queryParameter("sub1")?.let { t ->
|
||||
listOf(Track(t, url.queryParameter("sub1_label") ?: "English"))
|
||||
} ?: emptyList()
|
||||
|
||||
MixDropExtractor(client).videoFromUrl(iframeUrl, prefix = "(movembed) - ", externalSubs = subtitleList)
|
||||
}
|
||||
iframeUrl.startsWith("https://doo") -> {
|
||||
val url = iframeUrl.toHttpUrl()
|
||||
val subtitleList = url.queryParameter("c1_file")?.let { t ->
|
||||
listOf(Track(t, url.queryParameter("c1_label") ?: "English"))
|
||||
} ?: emptyList()
|
||||
|
||||
DoodExtractor(client).videoFromUrl(iframeUrl, "(movembed) Dood", externalSubs = subtitleList)?.let(::listOf) ?: emptyList()
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val MIXDROP_DOMAINS = listOf(
|
||||
"mixdrop",
|
||||
"mixdroop",
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package eu.kanade.tachiyomi.animeextension.hi.yomovies.extractors
|
||||
|
||||
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.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class SpeedostreamExtractor(private val client: OkHttpClient, private val headers: Headers) {
|
||||
fun videosFromUrl(url: String, referer: String, prefix: String = ""): List<Video> {
|
||||
val docHeaders = headers.newBuilder()
|
||||
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
|
||||
.add("Host", url.toHttpUrl().host)
|
||||
.add("Referer", referer)
|
||||
.build()
|
||||
|
||||
val document = client.newCall(
|
||||
GET(url, headers = docHeaders),
|
||||
).execute().asJsoup()
|
||||
|
||||
val masterUrl = document.selectFirst("script:containsData(file:)")
|
||||
?.data()
|
||||
?.substringAfter("file:")
|
||||
?.substringAfter("\"")
|
||||
?.substringBefore("\"")
|
||||
?: return emptyList()
|
||||
|
||||
return PlaylistUtils(client, headers).extractFromHls(
|
||||
masterUrl,
|
||||
referer = "https://${url.toHttpUrl().host}/",
|
||||
videoNameGen = { "${prefix}Speedostream - $it" },
|
||||
)
|
||||
}
|
||||
}
|