[WIP] JavSub provider

This commit is contained in:
Jace 2022-08-19 09:51:03 +08:00
parent 86cfd92785
commit ecdacc78c0
5 changed files with 281 additions and 0 deletions

View 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")
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.example"/>

View 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
}
}

View file

@ -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())
}
}

View file

@ -77,6 +77,7 @@ subprojects {
implementation(kotlin("stdlib")) // adds standard kotlin features, like listOf, mapOf etc implementation(kotlin("stdlib")) // adds standard kotlin features, like listOf, mapOf etc
implementation("com.github.Blatzar:NiceHttp:0.3.2") // http library implementation("com.github.Blatzar:NiceHttp:0.3.2") // http library
implementation("org.jsoup:jsoup:1.13.1") // html parser implementation("org.jsoup:jsoup:1.13.1") // html parser
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1")
} }
} }