mirror of
https://github.com/Jacekun/cs3xxx-repo.git
synced 2024-08-14 23:57:09 +00:00
[WIP] JavSub provider
This commit is contained in:
parent
86cfd92785
commit
ecdacc78c0
5 changed files with 281 additions and 0 deletions
24
JavSubProvider/build.gradle.kts
Normal file
24
JavSubProvider/build.gradle.kts
Normal file
|
@ -0,0 +1,24 @@
|
|||
// use an integer for version numbers
|
||||
version = 1
|
||||
|
||||
|
||||
cloudstream {
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
description = "High quality JAV subbed"
|
||||
authors = listOf("Jace")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
|
||||
// List of video source types. Users are able to filter for extensions in a given category.
|
||||
// You can find a list of avaliable types here:
|
||||
// https://recloudstream.github.io/cloudstream/html/app/com.lagradost.cloudstream3/-tv-type/index.html
|
||||
tvTypes = listOf("NSFW")
|
||||
}
|
2
JavSubProvider/src/main/AndroidManifest.xml
Normal file
2
JavSubProvider/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.example"/>
|
241
JavSubProvider/src/main/kotlin/com/jacekun/JavSubProvider.kt
Normal file
241
JavSubProvider/src/main/kotlin/com/jacekun/JavSubProvider.kt
Normal file
|
@ -0,0 +1,241 @@
|
|||
package com.jacekun
|
||||
|
||||
import android.util.Log
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.extractors.FEmbed
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
class JavSubProvider : MainAPI() {
|
||||
override var name = "JavSub"
|
||||
override var mainUrl = "https://javsub.co"
|
||||
override val supportedTypes: Set<TvType> get() = setOf(TvType.NSFW)
|
||||
override val hasDownloadSupport: Boolean get() = true
|
||||
override val hasMainPage: Boolean get() = true
|
||||
override val hasQuickSearch: Boolean get() = false
|
||||
|
||||
private val prefixTag = "dummyTag" //For use on stream links to differentiate links
|
||||
private val tvType = TvType.NSFW
|
||||
|
||||
data class ResponseMovieDetails(
|
||||
@JsonProperty("name") val name: String?,
|
||||
@JsonProperty("description") val description: String?,
|
||||
@JsonProperty("thumbnailUrl") val thumbnailUrl: String?,
|
||||
@JsonProperty("uploadDate") val uploadDate: String?,
|
||||
@JsonProperty("contentUrl") val contentUrl: String?
|
||||
)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/page/" to "Main Page",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||
val categoryData = request.data
|
||||
val categoryName = request.name
|
||||
val pagedlink = if (page > 0) categoryData + page else categoryData
|
||||
val document = app.get(pagedlink).document
|
||||
val homepage = document.select("main#main-content").map { it2 ->
|
||||
val inner = it2?.select("article > div.post-item-wrap") ?: return@map null
|
||||
//Log.i(this.name, "inner => $inner")
|
||||
val elements: List<SearchResponse> = inner.mapNotNull {
|
||||
//Log.i(this.name, "Inner content => $innerArticle")
|
||||
val innerA = it.selectFirst("div.blog-pic-wrap > a")?: return@mapNotNull null
|
||||
val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null
|
||||
|
||||
val imgArticle = innerA.selectFirst("img")
|
||||
val name = innerA.attr("title") ?: imgArticle?.attr("alt") ?: "<No Title>"
|
||||
val image = imgArticle?.attr("data-src")
|
||||
val year = null
|
||||
//Log.i(this.name, "image => $image")
|
||||
|
||||
MovieSearchResponse(
|
||||
name = name,
|
||||
url = link,
|
||||
apiName = this.name,
|
||||
type = tvType,
|
||||
posterUrl = image,
|
||||
year = year
|
||||
)
|
||||
}.distinctBy { a -> a.url }
|
||||
|
||||
HomePageList(
|
||||
name = categoryName,
|
||||
list = elements,
|
||||
isHorizontalImages = true
|
||||
)
|
||||
}.filterNotNull().filter { a -> a.list.isNotEmpty() }
|
||||
//TODO: Replace 'homepage.first()' with 'homepage' after adding overload on newHomePageResponse()
|
||||
if (homepage.isNotEmpty()) {
|
||||
return newHomePageResponse(
|
||||
list = homepage.first(),
|
||||
hasNext = true
|
||||
)
|
||||
}
|
||||
throw ErrorLoadingException("No homepage data found!")
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val url = "$mainUrl/?s=${query}"
|
||||
val document = app.get(url).document.getElementsByTag("body")
|
||||
.select("main#main-content")?.select("article")
|
||||
|
||||
return document?.mapNotNull {
|
||||
if (it == null) { return@mapNotNull null }
|
||||
val innerA = it.selectFirst("div.blog-pic-wrap > a")?: return@mapNotNull null
|
||||
val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null
|
||||
|
||||
val imgArticle = innerA.selectFirst("img")
|
||||
val title = innerA.attr("title") ?: imgArticle?.attr("alt") ?: "<No Title>"
|
||||
val image = imgArticle?.attr("data-src")
|
||||
val year = null
|
||||
|
||||
MovieSearchResponse(
|
||||
name = title,
|
||||
url = link,
|
||||
apiName = this.name,
|
||||
type = tvType,
|
||||
posterUrl = image,
|
||||
year = year
|
||||
)
|
||||
}?.distinctBy { b -> b.url } ?: listOf()
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val document = app.get(url).document
|
||||
val body = document.getElementsByTag("body")
|
||||
|
||||
// Default values
|
||||
var title = ""
|
||||
var poster : String? = null
|
||||
var year : Int? = null
|
||||
var descript : String? = null
|
||||
|
||||
// Video details
|
||||
var scriptJson = ""
|
||||
run breaking@{
|
||||
body.select("script").forEach {
|
||||
val scrAttr = it?.attr("type") ?: return@forEach
|
||||
if (scrAttr.equals("application/ld+json", ignoreCase = true)) {
|
||||
scriptJson = it.html() ?: ""
|
||||
return@breaking
|
||||
}
|
||||
}
|
||||
}
|
||||
//Log.i(this.name, "Result => (scriptJson) $scriptJson")
|
||||
|
||||
// Video stream
|
||||
val playerIframes: MutableList<String> = try {
|
||||
//Note: Fetch all multi-link urls
|
||||
document.selectFirst("div.series-listing")?.select("a")?.mapNotNull {
|
||||
it?.attr("href") ?: return@mapNotNull null
|
||||
}?.toMutableList() ?: mutableListOf()
|
||||
} catch (e: Exception) {
|
||||
Log.i(this.name, "Result => Exception (load) $e")
|
||||
mutableListOf()
|
||||
}
|
||||
|
||||
// JAV Info
|
||||
tryParseJson<ResponseMovieDetails>(scriptJson)?.let {
|
||||
val contentUrl = it.contentUrl
|
||||
title = it.name ?: ""
|
||||
poster = it.thumbnailUrl
|
||||
year = it.uploadDate?.take(4)?.toIntOrNull()
|
||||
descript = "Title: $title ${System.lineSeparator()} ${it.description}"
|
||||
|
||||
// Add additional links, Raw link without needing to fetch from JavSub API
|
||||
//if (!contentUrl.isNullOrBlank()) {
|
||||
//playerIframes.add("$prefixTag$contentUrl")
|
||||
//}
|
||||
//Log.i(this.name, "Result => (contentUrl) $contentUrl")
|
||||
}
|
||||
|
||||
Log.i(this.name, "Result => (playerIframes) ${playerIframes.toJson()}")
|
||||
|
||||
return MovieLoadResponse(
|
||||
name = title,
|
||||
url = url,
|
||||
apiName = this.name,
|
||||
type = tvType,
|
||||
dataUrl = playerIframes.toJson(),
|
||||
posterUrl = poster,
|
||||
year = year,
|
||||
plot = descript
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
var count = 0
|
||||
tryParseJson<List<String>>(data)?.apmap { link ->
|
||||
Log.i(this.name, "Result => (link) $link")
|
||||
if (link.startsWith(prefixTag)) {
|
||||
val linkUrl = link.removePrefix(prefixTag)
|
||||
val success = extractStreamLink(linkUrl, subtitleCallback, callback)
|
||||
if (success) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
else {
|
||||
val innerDoc =
|
||||
app.get(link).document.selectFirst("script#beeteam368_obj_wes-js-extra")
|
||||
var innerText = innerDoc?.html() ?: ""
|
||||
if (innerText.isNotBlank()) {
|
||||
"(?<=single_video_url\":)(.*)(?=,)".toRegex().find(innerText)
|
||||
?.groupValues?.get(0)?.let { iframe ->
|
||||
innerText = iframe.trim().trim('"')
|
||||
}
|
||||
Jsoup.parse(innerText)?.selectFirst("iframe")?.attr("src")?.let { server ->
|
||||
val serverLink = server.replace("\\", "").replace("\"", "")
|
||||
val success = extractStreamLink(serverLink, subtitleCallback, callback)
|
||||
if (success) {
|
||||
count++
|
||||
}
|
||||
Log.i(this.name, "Result => (streamLink add) $serverLink")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//Log.i(this.name, "Result => count: $count")
|
||||
return count > 0
|
||||
}
|
||||
|
||||
private suspend fun extractStreamLink(
|
||||
link: String,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit)
|
||||
: Boolean {
|
||||
if (link.isNotBlank()) {
|
||||
when {
|
||||
link.contains("watch-jav") -> {
|
||||
val extractor = FEmbed()
|
||||
extractor.domainUrl = "embedsito.com"
|
||||
extractor.getSafeUrl(
|
||||
url = link,
|
||||
referer = mainUrl,
|
||||
subtitleCallback = subtitleCallback,
|
||||
callback = callback
|
||||
)
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
return loadExtractor(
|
||||
url = link,
|
||||
referer = mainUrl,
|
||||
subtitleCallback = subtitleCallback,
|
||||
callback = callback
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.jacekun
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class TestPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(JavSubProvider())
|
||||
}
|
||||
}
|
|
@ -77,6 +77,7 @@ subprojects {
|
|||
implementation(kotlin("stdlib")) // adds standard kotlin features, like listOf, mapOf etc
|
||||
implementation("com.github.Blatzar:NiceHttp:0.3.2") // http library
|
||||
implementation("org.jsoup:jsoup:1.13.1") // html parser
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue