feat(arc/all): New source Jav Guru (#1930)
This commit is contained in:
22
src/all/javguru/AndroidManifest.xml
Normal file
22
src/all/javguru/AndroidManifest.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<application>
|
||||||
|
<activity
|
||||||
|
android:name=".all.javguru.JavGuruUrlActivity"
|
||||||
|
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:host="jav.guru"
|
||||||
|
android:pathPattern="/.*/..*"
|
||||||
|
android:scheme="https" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
23
src/all/javguru/build.gradle
Normal file
23
src/all/javguru/build.gradle
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.kotlin.android)
|
||||||
|
}
|
||||||
|
|
||||||
|
ext {
|
||||||
|
extName = 'Jav Guru'
|
||||||
|
pkgNameSuffix = 'all.javguru'
|
||||||
|
extClass = '.JavGuru'
|
||||||
|
extVersionCode = 1
|
||||||
|
isNsfw = true
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(':lib-streamsb-extractor'))
|
||||||
|
implementation(project(':lib-streamtape-extractor'))
|
||||||
|
implementation(project(':lib-dood-extractor'))
|
||||||
|
implementation(project(':lib-mixdrop-extractor'))
|
||||||
|
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
|
||||||
|
implementation(project(':lib-playlist-utils'))
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
BIN
src/all/javguru/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/all/javguru/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
src/all/javguru/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/all/javguru/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
src/all/javguru/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/all/javguru/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
src/all/javguru/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/all/javguru/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
BIN
src/all/javguru/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/all/javguru/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
src/all/javguru/res/web_hi_res_512.png
Normal file
BIN
src/all/javguru/res/web_hi_res_512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
@ -0,0 +1,335 @@
|
|||||||
|
package eu.kanade.tachiyomi.animeextension.all.javguru
|
||||||
|
|
||||||
|
import android.util.Base64
|
||||||
|
import eu.kanade.tachiyomi.animeextension.all.javguru.extractors.MaxStreamExtractor
|
||||||
|
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.SEpisode
|
||||||
|
import eu.kanade.tachiyomi.animesource.model.Video
|
||||||
|
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||||
|
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
|
||||||
|
import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
|
||||||
|
import eu.kanade.tachiyomi.lib.streamsbextractor.StreamSBExtractor
|
||||||
|
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.asObservable
|
||||||
|
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||||
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import okhttp3.Call
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import org.jsoup.select.Elements
|
||||||
|
import rx.Observable
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
class JavGuru : AnimeHttpSource() {
|
||||||
|
|
||||||
|
override val name = "Jav Guru"
|
||||||
|
|
||||||
|
override val baseUrl = "https://jav.guru"
|
||||||
|
|
||||||
|
override val lang = "all"
|
||||||
|
|
||||||
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
override val client = network.cloudflareClient.newBuilder()
|
||||||
|
.rateLimit(2)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
override fun headersBuilder() = super.headersBuilder()
|
||||||
|
.add("Referer", "$baseUrl/")
|
||||||
|
|
||||||
|
private val streamSbExtractor: StreamSBExtractor by lazy {
|
||||||
|
StreamSBExtractor(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val streamTapeExtractor: StreamTapeExtractor by lazy {
|
||||||
|
StreamTapeExtractor(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val doodExtractor: DoodExtractor by lazy {
|
||||||
|
DoodExtractor(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val mixDropExtractor: MixDropExtractor by lazy {
|
||||||
|
MixDropExtractor(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val maxStreamExtractor: MaxStreamExtractor by lazy {
|
||||||
|
MaxStreamExtractor(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var popularElements: Elements
|
||||||
|
|
||||||
|
override fun fetchPopularAnime(page: Int): Observable<AnimesPage> {
|
||||||
|
return if (page == 1) {
|
||||||
|
client.newCall(popularAnimeRequest(page))
|
||||||
|
.asObservableSuccess()
|
||||||
|
.map(::popularAnimeParse)
|
||||||
|
} else {
|
||||||
|
Observable.just(cachedPopularAnimeParse(page))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularAnimeRequest(page: Int) =
|
||||||
|
GET("$baseUrl/most-watched-rank/", headers)
|
||||||
|
|
||||||
|
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||||
|
popularElements = response.asJsoup().select(".tabcontent li")
|
||||||
|
|
||||||
|
return cachedPopularAnimeParse(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cachedPopularAnimeParse(page: Int): AnimesPage {
|
||||||
|
val end = min(page * 20, popularElements.size)
|
||||||
|
val entries = popularElements.subList((page - 1) * 20, end).map { element ->
|
||||||
|
SAnime.create().apply {
|
||||||
|
element.select("a").let { a ->
|
||||||
|
getIDFromUrl(a)?.let { url = it }
|
||||||
|
?: setUrlWithoutDomain(a.attr("href"))
|
||||||
|
|
||||||
|
title = a.text()
|
||||||
|
thumbnail_url = a.select("img").attr("abs:src")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AnimesPage(entries, end < popularElements.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request {
|
||||||
|
val url = baseUrl + if (page > 1) "/page/$page/" else ""
|
||||||
|
|
||||||
|
return GET(url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response): AnimesPage {
|
||||||
|
val document = response.asJsoup()
|
||||||
|
|
||||||
|
val entries = document.select("div.site-content div.inside-article:not(:contains(nothing))").map { element ->
|
||||||
|
SAnime.create().apply {
|
||||||
|
element.select("a").let { a ->
|
||||||
|
getIDFromUrl(a)?.let { url = it }
|
||||||
|
?: setUrlWithoutDomain(a.attr("href"))
|
||||||
|
}
|
||||||
|
thumbnail_url = element.select("img").attr("abs:src")
|
||||||
|
title = element.select("h2 > a").text()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val page = document.location()
|
||||||
|
.substringBeforeLast("/").toHttpUrlOrNull()
|
||||||
|
?.pathSegments?.last()?.toIntOrNull() ?: 1
|
||||||
|
|
||||||
|
val lastPage = document.select("div.wp-pagenavi a")
|
||||||
|
.last()?.attr("href")?.substringBeforeLast("/")
|
||||||
|
?.toHttpUrlOrNull()?.pathSegments?.last()?.toIntOrNull() ?: 1
|
||||||
|
|
||||||
|
return AnimesPage(entries, page < lastPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> {
|
||||||
|
if (query.startsWith(PREFIX_ID)) {
|
||||||
|
val id = query.substringAfter(PREFIX_ID)
|
||||||
|
if (id.toIntOrNull() == null) {
|
||||||
|
return Observable.just(AnimesPage(emptyList(), false))
|
||||||
|
}
|
||||||
|
val url = "/$id/"
|
||||||
|
val tempAnime = SAnime.create().apply { this.url = url }
|
||||||
|
return fetchAnimeDetails(tempAnime).map {
|
||||||
|
val anime = it.apply { this.url = url }
|
||||||
|
AnimesPage(listOf(anime), false)
|
||||||
|
}
|
||||||
|
} else if (query.isNotEmpty()) {
|
||||||
|
return client.newCall(searchAnimeRequest(page, query, filters))
|
||||||
|
.asObservableSuccess()
|
||||||
|
.map(::searchAnimeParse)
|
||||||
|
} else {
|
||||||
|
filters.forEach { filter ->
|
||||||
|
when (filter) {
|
||||||
|
is TagFilter,
|
||||||
|
is CategoryFilter,
|
||||||
|
-> {
|
||||||
|
if (filter.state != 0) {
|
||||||
|
val url = "$baseUrl${filter.toUrlPart()}" + if (page > 1) "page/$page/" else ""
|
||||||
|
val request = GET(url, headers)
|
||||||
|
return client.newCall(request)
|
||||||
|
.asObservableIgnoreCode(404)
|
||||||
|
.map(::searchAnimeParse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ActressFilter,
|
||||||
|
is ActorFilter,
|
||||||
|
is StudioFilter,
|
||||||
|
is MakerFilter,
|
||||||
|
-> {
|
||||||
|
if ((filter.state as String).isNotEmpty()) {
|
||||||
|
val url = "$baseUrl${filter.toUrlPart()}" + if (page > 1) "page/$page/" else ""
|
||||||
|
val request = GET(url, headers)
|
||||||
|
return client.newCall(request)
|
||||||
|
.asObservableIgnoreCode(404)
|
||||||
|
.map(::searchAnimeParse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("Select at least one Filter")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||||
|
val url = baseUrl.toHttpUrl().newBuilder().apply {
|
||||||
|
if (page > 1) addPathSegments("page/$page/")
|
||||||
|
addQueryParameter("s", query)
|
||||||
|
}.build().toString()
|
||||||
|
|
||||||
|
return GET(url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFilterList() = getFilters()
|
||||||
|
|
||||||
|
override fun searchAnimeParse(response: Response) = latestUpdatesParse(response)
|
||||||
|
|
||||||
|
override fun animeDetailsParse(response: Response): SAnime {
|
||||||
|
val document = response.asJsoup()
|
||||||
|
|
||||||
|
return SAnime.create().apply {
|
||||||
|
title = document.select(".titl").text()
|
||||||
|
thumbnail_url = document.select(".large-screenshot img").attr("abs:src")
|
||||||
|
genre = document.select(".infoleft a[rel*=tag]").joinToString { it.text() }
|
||||||
|
author = document.selectFirst(".infoleft li:contains(studio) a")?.text()
|
||||||
|
artist = document.selectFirst(".infoleft li:contains(label) a")?.text()
|
||||||
|
status = SAnime.COMPLETED
|
||||||
|
description = buildString {
|
||||||
|
document.selectFirst(".infoleft li:contains(code)")?.text()?.let { append("$it\n") }
|
||||||
|
document.selectFirst(".infoleft li:contains(director)")?.text()?.let { append("$it\n") }
|
||||||
|
document.selectFirst(".infoleft li:contains(studio)")?.text()?.let { append("$it\n") }
|
||||||
|
document.selectFirst(".infoleft li:contains(label)")?.text()?.let { append("$it\n") }
|
||||||
|
document.selectFirst(".infoleft li:contains(actor)")?.text()?.let { append("$it\n") }
|
||||||
|
document.selectFirst(".infoleft li:contains(actress)")?.text()?.let { append("$it\n") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
|
||||||
|
return Observable.just(
|
||||||
|
listOf(
|
||||||
|
SEpisode.create().apply {
|
||||||
|
url = anime.url
|
||||||
|
name = "Episode"
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
|
val document = response.asJsoup()
|
||||||
|
|
||||||
|
val iframeData = document.selectFirst("script:containsData(iframe_url)")?.html()
|
||||||
|
?: return emptyList()
|
||||||
|
|
||||||
|
val iframeUrls = IFRAME_B64_REGEX.findAll(iframeData)
|
||||||
|
.map { it.groupValues[1] }
|
||||||
|
.map { Base64.decode(it, Base64.DEFAULT).let(::String) }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
return iframeUrls.mapNotNull { url ->
|
||||||
|
runCatching {
|
||||||
|
val iframeResponse = client.newCall(GET(url, headers)).execute()
|
||||||
|
|
||||||
|
if (iframeResponse.isSuccessful.not()) {
|
||||||
|
iframeResponse.close()
|
||||||
|
return@mapNotNull null
|
||||||
|
}
|
||||||
|
|
||||||
|
val iframeDocument = iframeResponse.asJsoup()
|
||||||
|
|
||||||
|
val script = iframeDocument.selectFirst("script:containsData(start_player)")
|
||||||
|
?.html() ?: return@mapNotNull null
|
||||||
|
|
||||||
|
val olid = IFRAME_OLID_REGEX.find(script)?.groupValues?.get(1)?.reversed()
|
||||||
|
?: return@mapNotNull null
|
||||||
|
|
||||||
|
val olidUrl = IFRAME_OLID_URL.find(script)?.groupValues?.get(1)?.substringBeforeLast("=")?.let { "$it=$olid" }
|
||||||
|
?: return@mapNotNull null
|
||||||
|
|
||||||
|
val newHeaders = headersBuilder()
|
||||||
|
.set("Referer", url)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val redirectUrl = client.newCall(GET(olidUrl, newHeaders))
|
||||||
|
.execute().request.url.toString()
|
||||||
|
|
||||||
|
when {
|
||||||
|
STREAM_SB_DOMAINS.any { it in redirectUrl } -> {
|
||||||
|
streamSbExtractor.videosFromUrl(redirectUrl, headers)
|
||||||
|
}
|
||||||
|
redirectUrl.contains("streamtape") -> {
|
||||||
|
streamTapeExtractor.videoFromUrl(redirectUrl)?.let { listOf(it) }
|
||||||
|
}
|
||||||
|
redirectUrl.contains("dood") -> {
|
||||||
|
doodExtractor.videosFromUrl(redirectUrl)
|
||||||
|
}
|
||||||
|
MIXDROP_DOMAINS.any { it in redirectUrl } -> {
|
||||||
|
mixDropExtractor.videoFromUrl(redirectUrl)
|
||||||
|
}
|
||||||
|
redirectUrl.contains("maxstream") -> {
|
||||||
|
maxStreamExtractor.videoFromUrl(redirectUrl)
|
||||||
|
}
|
||||||
|
else -> { null }
|
||||||
|
}
|
||||||
|
}.getOrNull()
|
||||||
|
}.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getIDFromUrl(element: Elements): String? {
|
||||||
|
return element.attr("abs:href")
|
||||||
|
.toHttpUrlOrNull()
|
||||||
|
?.pathSegments
|
||||||
|
?.firstOrNull()
|
||||||
|
?.toIntOrNull()
|
||||||
|
?.toString()
|
||||||
|
?.let { "/$it/" }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Call.asObservableIgnoreCode(code: Int): Observable<Response> {
|
||||||
|
return asObservable().doOnNext { response ->
|
||||||
|
if (!response.isSuccessful && response.code != code) {
|
||||||
|
response.close()
|
||||||
|
throw Exception("HTTP error ${response.code}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PREFIX_ID = "id:"
|
||||||
|
|
||||||
|
private val IFRAME_B64_REGEX = Regex(""""iframe_url":"([^"]+)"""")
|
||||||
|
private val IFRAME_OLID_REGEX = Regex("""var OLID = '([^']+)'""")
|
||||||
|
private val IFRAME_OLID_URL = Regex("""src="([^"]+)"""")
|
||||||
|
|
||||||
|
private val STREAM_SB_DOMAINS = listOf(
|
||||||
|
"sbhight", "sbrity", "sbembed.com", "sbembed1.com", "sbplay.org",
|
||||||
|
"sbvideo.net", "streamsb.net", "sbplay.one", "cloudemb.com",
|
||||||
|
"playersb.com", "tubesb.com", "sbplay1.com", "embedsb.com",
|
||||||
|
"watchsb.com", "sbplay2.com", "japopav.tv", "viewsb.com",
|
||||||
|
"sbfast", "sbfull.com", "javplaya.com", "ssbstream.net",
|
||||||
|
"p1ayerjavseen.com", "sbthe.com", "vidmovie.xyz", "sbspeed.com",
|
||||||
|
"streamsss.net", "sblanh.com", "tvmshow.com", "sbanh.com",
|
||||||
|
"streamovies.xyz", "sblona.com",
|
||||||
|
)
|
||||||
|
private val MIXDROP_DOMAINS = listOf(
|
||||||
|
"mixdrop",
|
||||||
|
"mixdroop",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||||
|
throw UnsupportedOperationException("Not used")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,335 @@
|
|||||||
|
package eu.kanade.tachiyomi.animeextension.all.javguru
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||||
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||||
|
|
||||||
|
fun getFilters() = AnimeFilterList(
|
||||||
|
AnimeFilter.Header("Only One Filter Works at a time!!"),
|
||||||
|
AnimeFilter.Header("Ignored With Text Search!!"),
|
||||||
|
TagFilter(),
|
||||||
|
CategoryFilter(),
|
||||||
|
AnimeFilter.Separator(),
|
||||||
|
ActressFilter(),
|
||||||
|
ActorFilter(),
|
||||||
|
StudioFilter(),
|
||||||
|
MakerFilter(),
|
||||||
|
)
|
||||||
|
|
||||||
|
class UriPartFilter(val name: String, val urlPart: String)
|
||||||
|
|
||||||
|
abstract class UriPartFilters(name: String, private val tags: List<UriPartFilter>) :
|
||||||
|
AnimeFilter.Select<String>(name, tags.map { it.name }.toTypedArray()) {
|
||||||
|
fun toUrlPart() = tags[state].urlPart
|
||||||
|
}
|
||||||
|
|
||||||
|
class TagFilter : UriPartFilters("Tags", TAGS)
|
||||||
|
|
||||||
|
class CategoryFilter : UriPartFilters("Categories", CATEGORIES)
|
||||||
|
|
||||||
|
abstract class TextFilter(name: String, private val urlSubDirectory: String) : AnimeFilter.Text(name) {
|
||||||
|
fun toUrlPart() = state.trim()
|
||||||
|
.lowercase()
|
||||||
|
.replace(SPECIAL_CHAR_REGEX, "-")
|
||||||
|
.replace(TRAILING_HIPHEN_REGEX, "")
|
||||||
|
.let { "/$urlSubDirectory/$it/" }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SPECIAL_CHAR_REGEX = "[^a-z0-9]+".toRegex()
|
||||||
|
private val TRAILING_HIPHEN_REGEX = "-+$".toRegex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActressFilter : TextFilter("Actress", "actress")
|
||||||
|
|
||||||
|
class ActorFilter : TextFilter("Actor", "actor")
|
||||||
|
|
||||||
|
class StudioFilter : TextFilter("Studio", "studio")
|
||||||
|
|
||||||
|
class MakerFilter : TextFilter("Maker", "maker")
|
||||||
|
|
||||||
|
fun <T> AnimeFilter<T>.toUrlPart(): String? {
|
||||||
|
return when (this) {
|
||||||
|
is TagFilter -> this.toUrlPart()
|
||||||
|
is CategoryFilter -> this.toUrlPart()
|
||||||
|
is ActressFilter -> this.toUrlPart()
|
||||||
|
is ActorFilter -> this.toUrlPart()
|
||||||
|
is StudioFilter -> this.toUrlPart()
|
||||||
|
is MakerFilter -> this.toUrlPart()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val TAGS = listOf(
|
||||||
|
UriPartFilter("", "/"),
|
||||||
|
UriPartFilter("Solowork", "/tag/solowork/"),
|
||||||
|
UriPartFilter("Creampie", "/tag/creampie/"),
|
||||||
|
UriPartFilter("Big tits", "/tag/big-tits/"),
|
||||||
|
UriPartFilter("Beautiful Girl", "/tag/beautiful-girl/"),
|
||||||
|
UriPartFilter("Married Woman", "/tag/married-woman/"),
|
||||||
|
UriPartFilter("Amateur", "/tag/amateur/"),
|
||||||
|
UriPartFilter("Digital Mosaic", "/tag/digital-mosaic/"),
|
||||||
|
UriPartFilter("Slut", "/tag/slut/"),
|
||||||
|
UriPartFilter("Mature Woman", "/tag/mature-woman/"),
|
||||||
|
UriPartFilter("Cuckold", "/tag/cuckold/"),
|
||||||
|
UriPartFilter("3P", "/tag/3p/"),
|
||||||
|
UriPartFilter("Slender", "/tag/slender/"),
|
||||||
|
UriPartFilter("Blow", "/tag/blow/"),
|
||||||
|
UriPartFilter("Squirting", "/tag/squirting/"),
|
||||||
|
UriPartFilter("Drama", "/tag/drama/"),
|
||||||
|
UriPartFilter("Nasty", "/tag/nasty/"),
|
||||||
|
UriPartFilter("Hardcore", "/tag/hardcore/"),
|
||||||
|
UriPartFilter("School Girls", "/tag/school-girls/"),
|
||||||
|
UriPartFilter("4P", "/tag/4p/"),
|
||||||
|
UriPartFilter("Titty fuck", "/tag/titty-fuck/"),
|
||||||
|
UriPartFilter("Cowgirl", "/tag/cowgirl/"),
|
||||||
|
UriPartFilter("Incest", "/tag/incest/"),
|
||||||
|
UriPartFilter("Facials", "/tag/facials/"),
|
||||||
|
UriPartFilter("breasts", "/tag/breasts/"),
|
||||||
|
UriPartFilter("abuse", "/tag/abuse/"),
|
||||||
|
UriPartFilter("Risky Mosaic", "/tag/risky-mosaic/"),
|
||||||
|
UriPartFilter("Debut Production", "/tag/debut-production/"),
|
||||||
|
UriPartFilter("Older sister", "/tag/older-sister/"),
|
||||||
|
UriPartFilter("Huge Butt", "/tag/huge-butt/"),
|
||||||
|
UriPartFilter("4HR+", "/tag/4hr/"),
|
||||||
|
UriPartFilter("Affair", "/tag/affair/"),
|
||||||
|
UriPartFilter("Kiss", "/tag/kiss/"),
|
||||||
|
UriPartFilter("Deep Throating", "/tag/deep-throating/"),
|
||||||
|
UriPartFilter("Documentary", "/tag/documentary/"),
|
||||||
|
UriPartFilter("Mini", "/tag/mini/"),
|
||||||
|
UriPartFilter("Entertainer", "/tag/entertainer/"),
|
||||||
|
UriPartFilter("Dirty Words", "/tag/dirty-words/"),
|
||||||
|
UriPartFilter("Cosplay", "/tag/cosplay/"),
|
||||||
|
UriPartFilter("POV", "/tag/pov/"),
|
||||||
|
UriPartFilter("Shaved", "/tag/shaved/"),
|
||||||
|
UriPartFilter("butt", "/tag/butt/"),
|
||||||
|
UriPartFilter("OL", "/tag/ol/"),
|
||||||
|
UriPartFilter("Tits", "/tag/tits/"),
|
||||||
|
UriPartFilter("Promiscuity", "/tag/promiscuity/"),
|
||||||
|
UriPartFilter("Restraint", "/tag/restraint/"),
|
||||||
|
UriPartFilter("Gal", "/tag/gal/"),
|
||||||
|
UriPartFilter("planning", "/tag/planning/"),
|
||||||
|
UriPartFilter("Subjectivity", "/tag/subjectivity/"),
|
||||||
|
UriPartFilter("Handjob", "/tag/handjob/"),
|
||||||
|
UriPartFilter("Uniform", "/tag/uniform/"),
|
||||||
|
UriPartFilter("Sister", "/tag/sister/"),
|
||||||
|
UriPartFilter("Humiliation", "/tag/humiliation/"),
|
||||||
|
UriPartFilter("Prostitutes", "/tag/prostitutes/"),
|
||||||
|
UriPartFilter("School Uniform", "/tag/school-uniform/"),
|
||||||
|
UriPartFilter("Rape", "/tag/rape/"),
|
||||||
|
UriPartFilter("Lesbian", "/tag/lesbian/"),
|
||||||
|
UriPartFilter("Anal", "/tag/anal/"),
|
||||||
|
UriPartFilter("Image video", "/tag/image-video/"),
|
||||||
|
UriPartFilter("Pantyhose", "/tag/pantyhose/"),
|
||||||
|
UriPartFilter("Other fetish", "/tag/other-fetish/"),
|
||||||
|
UriPartFilter("Female College Student", "/tag/female-college-student/"),
|
||||||
|
UriPartFilter("Female teacher", "/tag/female-teacher/"),
|
||||||
|
UriPartFilter("Bukkake", "/tag/bukkake/"),
|
||||||
|
UriPartFilter("Training", "/tag/training/"),
|
||||||
|
UriPartFilter("Cum", "/tag/cum/"),
|
||||||
|
UriPartFilter("Masturbation", "/tag/masturbation/"),
|
||||||
|
UriPartFilter("Sweat", "/tag/sweat/"),
|
||||||
|
UriPartFilter("Omnibus", "/tag/omnibus/"),
|
||||||
|
UriPartFilter("Best", "/tag/best/"),
|
||||||
|
UriPartFilter("Lotion", "/tag/lotion/"),
|
||||||
|
UriPartFilter("Girl", "/tag/girl/"),
|
||||||
|
UriPartFilter("Submissive Men", "/tag/submissive-men/"),
|
||||||
|
UriPartFilter("Outdoors", "/tag/outdoors/"),
|
||||||
|
UriPartFilter("Beauty Shop", "/tag/beauty-shop/"),
|
||||||
|
UriPartFilter("Busty fetish", "/tag/busty-fetish/"),
|
||||||
|
UriPartFilter("Toy", "/tag/toy/"),
|
||||||
|
UriPartFilter("Urination", "/tag/urination/"),
|
||||||
|
UriPartFilter("huge cock", "/tag/huge-cock/"),
|
||||||
|
UriPartFilter("Gangbang", "/tag/gangbang/"),
|
||||||
|
UriPartFilter("Massage", "/tag/massage/"),
|
||||||
|
UriPartFilter("Tall", "/tag/tall/"),
|
||||||
|
UriPartFilter("Hot Spring", "/tag/hot-spring/"),
|
||||||
|
UriPartFilter("virgin man", "/tag/virgin-man/"),
|
||||||
|
UriPartFilter("Various Professions", "/tag/various-professions/"),
|
||||||
|
UriPartFilter("Bride", "/tag/bride/"),
|
||||||
|
UriPartFilter("Leg Fetish", "/tag/leg-fetish/"),
|
||||||
|
UriPartFilter("Young wife", "/tag/young-wife/"),
|
||||||
|
UriPartFilter("Maid", "/tag/maid/"),
|
||||||
|
UriPartFilter("BBW", "/tag/bbw/"),
|
||||||
|
UriPartFilter("SM", "/tag/sm/"),
|
||||||
|
UriPartFilter("Restraints", "/tag/restraints/"),
|
||||||
|
UriPartFilter("Lesbian Kiss", "/tag/lesbian-kiss/"),
|
||||||
|
UriPartFilter("Voyeur", "/tag/voyeur/"),
|
||||||
|
UriPartFilter("Mother", "/tag/mother/"),
|
||||||
|
UriPartFilter("Evil", "/tag/evil/"),
|
||||||
|
UriPartFilter("Underwear", "/tag/underwear/"),
|
||||||
|
UriPartFilter("Nurse", "/tag/nurse/"),
|
||||||
|
UriPartFilter("Glasses", "/tag/glasses/"),
|
||||||
|
UriPartFilter("Lingerie", "/tag/lingerie/"),
|
||||||
|
UriPartFilter("Drug", "/tag/drug/"),
|
||||||
|
UriPartFilter("Nampa", "/tag/nampa/"),
|
||||||
|
UriPartFilter("School Swimsuit", "/tag/school-swimsuit/"),
|
||||||
|
UriPartFilter("Stepmother", "/tag/stepmother/"),
|
||||||
|
UriPartFilter("Sailor suit", "/tag/sailor-suit/"),
|
||||||
|
UriPartFilter("Prank", "/tag/prank/"),
|
||||||
|
UriPartFilter("Cunnilingus", "/tag/cunnilingus/"),
|
||||||
|
UriPartFilter("Electric Massager", "/tag/electric-massager/"),
|
||||||
|
UriPartFilter("Molester", "/tag/molester/"),
|
||||||
|
UriPartFilter("Black Actor", "/tag/black-actor/"),
|
||||||
|
UriPartFilter("Ultra-Huge Tits", "/tag/ultra-huge-tits/"),
|
||||||
|
UriPartFilter("Original Collaboration", "/tag/original-collaboration/"),
|
||||||
|
UriPartFilter("Confinement", "/tag/confinement/"),
|
||||||
|
UriPartFilter("Shotacon", "/tag/shotacon/"),
|
||||||
|
UriPartFilter("Footjob", "/tag/footjob/"),
|
||||||
|
UriPartFilter("Female Boss", "/tag/female-boss/"),
|
||||||
|
UriPartFilter("Female investigator", "/tag/female-investigator/"),
|
||||||
|
UriPartFilter("Swimsuit", "/tag/swimsuit/"),
|
||||||
|
UriPartFilter("Bloomers", "/tag/bloomers/"),
|
||||||
|
UriPartFilter("Facesitting", "/tag/facesitting/"),
|
||||||
|
UriPartFilter("Kimono", "/tag/kimono/"),
|
||||||
|
UriPartFilter("Mourning", "/tag/mourning/"),
|
||||||
|
UriPartFilter("White Actress", "/tag/white-actress/"),
|
||||||
|
UriPartFilter("Acme · Orgasm", "/tag/acme-%c2%b7-orgasm/"),
|
||||||
|
UriPartFilter("Sun tan", "/tag/sun-tan/"),
|
||||||
|
UriPartFilter("Finger Fuck", "/tag/finger-fuck/"),
|
||||||
|
UriPartFilter("Transsexual", "/tag/transsexual/"),
|
||||||
|
UriPartFilter("Blu-ray", "/tag/blu-ray/"),
|
||||||
|
UriPartFilter("VR", "/tag/vr/"),
|
||||||
|
UriPartFilter("Cross Dressing", "/tag/cross-dressing/"),
|
||||||
|
UriPartFilter("Soapland", "/tag/soapland/"),
|
||||||
|
UriPartFilter("Fan Appreciation", "/tag/fan-appreciation/"),
|
||||||
|
UriPartFilter("AV Actress", "/tag/av-actress/"),
|
||||||
|
UriPartFilter("School Stuff", "/tag/school-stuff/"),
|
||||||
|
UriPartFilter("Love", "/tag/love/"),
|
||||||
|
UriPartFilter("Close Up", "/tag/close-up/"),
|
||||||
|
UriPartFilter("Submissive Woman", "/tag/submissive-woman/"),
|
||||||
|
UriPartFilter("Mini Skirt", "/tag/mini-skirt/"),
|
||||||
|
UriPartFilter("Impromptu Sex", "/tag/impromptu-sex/"),
|
||||||
|
UriPartFilter("Vibe", "/tag/vibe/"),
|
||||||
|
UriPartFilter("Bitch", "/tag/bitch/"),
|
||||||
|
UriPartFilter("Enema", "/tag/enema/"),
|
||||||
|
UriPartFilter("Hypnosis", "/tag/hypnosis/"),
|
||||||
|
UriPartFilter("Childhood Friend", "/tag/childhood-friend/"),
|
||||||
|
UriPartFilter("Erotic Wear", "/tag/erotic-wear/"),
|
||||||
|
UriPartFilter("Tutor", "/tag/tutor/"),
|
||||||
|
UriPartFilter("Male Squirting", "/tag/male-squirting/"),
|
||||||
|
UriPartFilter("Bath", "/tag/bath/"),
|
||||||
|
UriPartFilter("Conceived", "/tag/conceived/"),
|
||||||
|
UriPartFilter("Stewardess", "/tag/stewardess/"),
|
||||||
|
UriPartFilter("Sport", "/tag/sport/"),
|
||||||
|
UriPartFilter("Bunny Girl", "/tag/bunny-girl/"),
|
||||||
|
UriPartFilter("Piss Drinking", "/tag/piss-drinking/"),
|
||||||
|
UriPartFilter("Shibari", "/tag/shibari/"),
|
||||||
|
UriPartFilter("Couple", "/tag/couple/"),
|
||||||
|
UriPartFilter("Anchorwoman", "/tag/anchorwoman/"),
|
||||||
|
UriPartFilter("Delusion", "/tag/delusion/"),
|
||||||
|
UriPartFilter("69", "/tag/69/"),
|
||||||
|
UriPartFilter("Secretary", "/tag/secretary/"),
|
||||||
|
UriPartFilter("Idol", "/tag/idol/"),
|
||||||
|
UriPartFilter("Elder Male", "/tag/elder-male/"),
|
||||||
|
UriPartFilter("Cervix", "/tag/cervix/"),
|
||||||
|
UriPartFilter("Leotard", "/tag/leotard/"),
|
||||||
|
UriPartFilter("Miss", "/tag/miss/"),
|
||||||
|
UriPartFilter("Back", "/tag/back/"),
|
||||||
|
UriPartFilter("blog", "/tag/blog/"),
|
||||||
|
UriPartFilter("virgin", "/tag/virgin/"),
|
||||||
|
UriPartFilter("Female Doctor", "/tag/female-doctor/"),
|
||||||
|
UriPartFilter("No Bra", "/tag/no-bra/"),
|
||||||
|
UriPartFilter("Tsundere", "/tag/tsundere/"),
|
||||||
|
UriPartFilter("Race Queen", "/tag/race-queen/"),
|
||||||
|
UriPartFilter("Multiple Story", "/tag/multiple-story/"),
|
||||||
|
UriPartFilter("Widow", "/tag/widow/"),
|
||||||
|
UriPartFilter("Actress Best", "/tag/actress-best/"),
|
||||||
|
UriPartFilter("Bondage", "/tag/bondage/"),
|
||||||
|
UriPartFilter("Muscle", "/tag/muscle/"),
|
||||||
|
UriPartFilter("User Submission", "/tag/user-submission/"),
|
||||||
|
UriPartFilter("Breast Milk", "/tag/breast-milk/"),
|
||||||
|
UriPartFilter("Sexy", "/tag/sexy/"),
|
||||||
|
UriPartFilter("Travel", "/tag/travel/"),
|
||||||
|
UriPartFilter("Knee Socks", "/tag/knee-socks/"),
|
||||||
|
UriPartFilter("Date", "/tag/date/"),
|
||||||
|
UriPartFilter("For Women", "/tag/for-women/"),
|
||||||
|
UriPartFilter("Premature Ejaculation", "/tag/premature-ejaculation/"),
|
||||||
|
UriPartFilter("Hi-Def", "/tag/hi-def/"),
|
||||||
|
UriPartFilter("Time Stop", "/tag/time-stop/"),
|
||||||
|
UriPartFilter("Subordinates / Colleagues", "/tag/subordinates-colleagues/"),
|
||||||
|
UriPartFilter("Adopted Daughter", "/tag/adopted-daughter/"),
|
||||||
|
UriPartFilter("Instructor", "/tag/instructor/"),
|
||||||
|
UriPartFilter("Catgirl", "/tag/catgirl/"),
|
||||||
|
UriPartFilter("Body Conscious", "/tag/body-conscious/"),
|
||||||
|
UriPartFilter("Fighting Action", "/tag/fighting-action/"),
|
||||||
|
UriPartFilter("Featured Actress", "/tag/featured-actress/"),
|
||||||
|
UriPartFilter("Hostess", "/tag/hostess/"),
|
||||||
|
UriPartFilter("Dead Drunk", "/tag/dead-drunk/"),
|
||||||
|
UriPartFilter("Landlady", "/tag/landlady/"),
|
||||||
|
UriPartFilter("Business Attire", "/tag/business-attire/"),
|
||||||
|
UriPartFilter("Dildo", "/tag/dildo/"),
|
||||||
|
UriPartFilter("Reversed Role", "/tag/reversed-role/"),
|
||||||
|
UriPartFilter("Foreign Objects", "/tag/foreign-objects/"),
|
||||||
|
UriPartFilter("Athlete", "/tag/athlete/"),
|
||||||
|
UriPartFilter("Aunt", "/tag/aunt/"),
|
||||||
|
UriPartFilter("Model", "/tag/model/"),
|
||||||
|
UriPartFilter("Big Breasts", "/tag/big-breasts/"),
|
||||||
|
UriPartFilter("Oversea Import", "/tag/oversea-import/"),
|
||||||
|
UriPartFilter("Drinking Party", "/tag/drinking-party/"),
|
||||||
|
UriPartFilter("Booth Girl", "/tag/booth-girl/"),
|
||||||
|
UriPartFilter("Car Sex", "/tag/car-sex/"),
|
||||||
|
UriPartFilter("Blowjob", "/tag/blowjob/"),
|
||||||
|
UriPartFilter("Other Asian", "/tag/other-asian/"),
|
||||||
|
UriPartFilter("Special Effects", "/tag/special-effects/"),
|
||||||
|
UriPartFilter("Spanking", "/tag/spanking/"),
|
||||||
|
UriPartFilter("Club Activities / Manager", "/tag/club-activities-manager/"),
|
||||||
|
UriPartFilter("Naked Apron", "/tag/naked-apron/"),
|
||||||
|
UriPartFilter("Fantasy", "/tag/fantasy/"),
|
||||||
|
UriPartFilter("Female Warrior", "/tag/female-warrior/"),
|
||||||
|
UriPartFilter("Anime Characters", "/tag/anime-characters/"),
|
||||||
|
UriPartFilter("Sex Conversion / Feminized", "/tag/sex-conversion-feminized/"),
|
||||||
|
UriPartFilter("Flexible", "/tag/flexible/"),
|
||||||
|
UriPartFilter("Schoolgirl", "/tag/schoolgirl/"),
|
||||||
|
UriPartFilter("Long Boots", "/tag/long-boots/"),
|
||||||
|
UriPartFilter("No Undies", "/tag/no-undies/"),
|
||||||
|
UriPartFilter("Immediate Oral", "/tag/immediate-oral/"),
|
||||||
|
UriPartFilter("Hospital / Clinic", "/tag/hospital-clinic/"),
|
||||||
|
UriPartFilter("Dance", "/tag/dance/"),
|
||||||
|
UriPartFilter("Breast Peeker", "/tag/breast-peeker/"),
|
||||||
|
UriPartFilter("Waitress", "/tag/waitress/"),
|
||||||
|
UriPartFilter("Futanari", "/tag/futanari/"),
|
||||||
|
UriPartFilter("Rolling Back Eyes / Fainting", "/tag/rolling-back-eyes-fainting/"),
|
||||||
|
UriPartFilter("Hotel", "/tag/hotel/"),
|
||||||
|
UriPartFilter("Exposure", "/tag/exposure/"),
|
||||||
|
UriPartFilter("Torture", "/tag/torture/"),
|
||||||
|
UriPartFilter("Office Lady", "/tag/office-lady/"),
|
||||||
|
UriPartFilter("Masturbation Support", "/tag/masturbation-support/"),
|
||||||
|
UriPartFilter("facial", "/tag/facial/"),
|
||||||
|
UriPartFilter("Egg Vibrator", "/tag/egg-vibrator/"),
|
||||||
|
UriPartFilter("Fisting", "/tag/fisting/"),
|
||||||
|
UriPartFilter("Vomit", "/tag/vomit/"),
|
||||||
|
UriPartFilter("Orgy", "/tag/orgy/"),
|
||||||
|
UriPartFilter("Cruel Expression", "/tag/cruel-expression/"),
|
||||||
|
UriPartFilter("Doll", "/tag/doll/"),
|
||||||
|
UriPartFilter("Loose Socks", "/tag/loose-socks/"),
|
||||||
|
UriPartFilter("Best of 2021", "/tag/best-of-2021/"),
|
||||||
|
UriPartFilter("Reserved Role", "/tag/reserved-role/"),
|
||||||
|
UriPartFilter("Best of 2019", "/tag/best-of-2019/"),
|
||||||
|
UriPartFilter("Mother-in-law", "/tag/mother-in-law/"),
|
||||||
|
UriPartFilter("Gay", "/tag/gay/"),
|
||||||
|
UriPartFilter("Swingers", "/tag/swingers/"),
|
||||||
|
UriPartFilter("Best of 2020", "/tag/best-of-2020/"),
|
||||||
|
UriPartFilter("Mistress", "/tag/mistress/"),
|
||||||
|
UriPartFilter("Shame", "/tag/shame/"),
|
||||||
|
UriPartFilter("Yukata", "/tag/yukata/"),
|
||||||
|
UriPartFilter("Best of 2017", "/tag/best-of-2017/"),
|
||||||
|
UriPartFilter("Best of 2018", "/tag/best-of-2018/"),
|
||||||
|
UriPartFilter("Nose Hook", "/tag/nose-hook/"),
|
||||||
|
)
|
||||||
|
|
||||||
|
val CATEGORIES = listOf(
|
||||||
|
UriPartFilter("", "/"),
|
||||||
|
UriPartFilter("1080p", "/category/1080p/"),
|
||||||
|
UriPartFilter("4K", "/category/4k/"),
|
||||||
|
UriPartFilter("Amateur", "/category/amateur/"),
|
||||||
|
UriPartFilter("Blog", "/category/blog/"),
|
||||||
|
UriPartFilter("Decensored", "/category/decensored/"),
|
||||||
|
UriPartFilter("English subbed JAV", "/category/english-subbed/"),
|
||||||
|
UriPartFilter("FC2", "/category/fc2/"),
|
||||||
|
UriPartFilter("HD", "/category/hd/"),
|
||||||
|
UriPartFilter("Idol", "/category/idol/"),
|
||||||
|
UriPartFilter("JAV", "/category/jav/"),
|
||||||
|
UriPartFilter("LEGACY", "/category/legacy/"),
|
||||||
|
UriPartFilter("UNCENSORED", "/category/jav-uncensored/"),
|
||||||
|
UriPartFilter("VR AV", "/category/vr-av/"),
|
||||||
|
)
|
@ -0,0 +1,34 @@
|
|||||||
|
package eu.kanade.tachiyomi.animeextension.all.javguru
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
class JavGuruUrlActivity : Activity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val pathSegments = intent?.data?.pathSegments
|
||||||
|
if (pathSegments != null && pathSegments.size > 1) {
|
||||||
|
val id = pathSegments[0]
|
||||||
|
val mainIntent = Intent().apply {
|
||||||
|
action = "eu.kanade.tachiyomi.ANIMESEARCH"
|
||||||
|
putExtra("query", "${JavGuru.PREFIX_ID}$id")
|
||||||
|
putExtra("filter", packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
startActivity(mainIntent)
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
Log.e("JavGuruUrlActivity", e.toString())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e("JavGuruUrlActivity", "could not parse uri from intent $intent")
|
||||||
|
}
|
||||||
|
|
||||||
|
finish()
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package eu.kanade.tachiyomi.animeextension.all.javguru.extractors
|
||||||
|
|
||||||
|
import dev.datlag.jsunpacker.JsUnpacker
|
||||||
|
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 MaxStreamExtractor(private val client: OkHttpClient) {
|
||||||
|
|
||||||
|
private val playListUtils: PlaylistUtils by lazy {
|
||||||
|
PlaylistUtils(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun videoFromUrl(url: String): List<Video> {
|
||||||
|
val document = client.newCall(GET(url)).execute().asJsoup()
|
||||||
|
|
||||||
|
val script = document.selectFirst("script:containsData(function(p,a,c,k,e,d))")
|
||||||
|
?.data()
|
||||||
|
?.let(JsUnpacker::unpackAndCombine)
|
||||||
|
?: return emptyList()
|
||||||
|
|
||||||
|
val videoUrl = script.substringAfter("file:\"").substringBefore("\"")
|
||||||
|
|
||||||
|
if (videoUrl.toHttpUrlOrNull() == null) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
return playListUtils.extractFromHls(videoUrl, url, videoNameGen = { quality -> "MaxStream: $quality" })
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user