Merge remote-tracking branch 'origin/master'

This commit is contained in:
reduplicated 2022-08-20 19:40:03 +02:00
commit 10f4d33c59
70 changed files with 3433 additions and 268 deletions

View file

@ -39,11 +39,11 @@ body:
- type: input
id: cloudstream-version
attributes:
label: Cloudstream version
label: Cloudstream version and commit hash
description: |
You can find your Cloudstream version in **Settings**.
You can find your Cloudstream version in **Settings**. Commit hash is the 7 character string next to the version.
placeholder: |
Example: "2.8.16"
Example: "2.8.16 a49f466"
validations:
required: true
@ -58,6 +58,15 @@ body:
validations:
required: true
- type: textarea
id: logcat
attributes:
label: Logcat
placeholder: |
To get logcat please go to Settings > Updates and backup > Show logcat 🐈.
You can attach a file or link to some pastebin service if the file is too big.
render: java
- type: textarea
id: other-details
attributes:

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Report provider bug
url: https://github.com/recloudstream
about: Please do not report any provider bugs here. This repository does not contain any providers. Please find the appropriate repository and report your issue there or join the discord.
- name: Discord
url: https://discord.gg/5Hus6fM
about: Join our discord for faster support on smaller issues.

View file

@ -1,84 +0,0 @@
name: 🐞 Provider Issue Report
description: Report a source issue
labels: [bug]
body:
- type: input
id: source
attributes:
label: Source information
description: |
You can find the source name in navigation drawer.
placeholder: |
Example: "Bflix"
validations:
required: true
- type: input
id: source-url
attributes:
label: Source link
placeholder: |
Example:
"www.example.org"
validations:
required: true
- type: textarea
id: reproduce-steps
attributes:
label: Steps to reproduce
description: Provide an example of the issue.
placeholder: |
Example:
1. First step
2. Second step
3. Issue here
validations:
required: true
- type: input
id: cloudstream-version
attributes:
label: CloudStream version
description: |
You can find your CloudStream version in **Settings**.
placeholder: |
Example: "2.8.16"
validations:
required: true
- type: input
id: android-version
attributes:
label: Android version
description: |
You can find this somewhere in your Android settings.
placeholder: |
Example: "Android 12"
validations:
required: true
- type: textarea
id: other-details
attributes:
label: Other details
placeholder: |
Additional details and attachments.
- type: checkboxes
id: acknowledgements
attributes:
label: Acknowledgements
description: Your issue will be closed if you haven't done these steps.
options:
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue.
required: true
- label: I have written a short but informative title.
required: true
- label: I have updated the app to pre-release version **[Latest](https://github.com/LagradOst/CloudStream-3/releases)**.
required: true
- label: If related to a provider, I have checked the site and it works, but not the app.
required: true
- label: I will fill out all of the requested information in this form.
required: true

63
.github/workflows/issue-action.yml vendored Normal file
View file

@ -0,0 +1,63 @@
name: Issue automatic actions
on:
issues:
types: [opened, edited]
jobs:
issue-moderator:
runs-on: ubuntu-latest
steps:
- name: Generate access token
id: generate_token
uses: tibdex/github-app-token@v1
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_KEY }}
- name: Similarity analysis
uses: actions-cool/issues-similarity-analysis@v1
with:
token: ${{ steps.generate_token.outputs.token }}
filter-threshold: 0.5
title-excludes: ''
comment-title: |
### Your issue looks similar to these issues:
Please close if duplicate.
comment-body: '${index}. ${similarity} #${number}'
- uses: actions/checkout@v2
- name: Automatically close issues that dont follow the issue template
uses: lucasbento/auto-close-issues@v1.0.2
with:
github-token: ${{ steps.generate_token.outputs.token }}
issue-close-message: |
@${issue.user.login}: hello! :wave:
This issue is being automatically closed because it does not follow the issue template."
closed-issues-label: "invalid"
- name: Check if issue mentions a provider
id: provider_check
env:
GH_TEXT: "${{ github.event.issue.title }} ${{ github.event.issue.body }}"
run: |
wget --output-document check_issue.py "https://raw.githubusercontent.com/recloudstream/.github/master/.github/check_issue.py"
pip3 install httpx
RES="$(python3 ./check_issue.py)"
echo "::set-output name=name::${RES}"
- name: Comment if issue mentions a provider
if: steps.provider_check.outputs.name != 'none'
uses: actions-cool/issues-helper@v3
with:
actions: 'create-comment'
token: ${{ steps.generate_token.outputs.token }}
body: |
Hello ${{ github.event.issue.user.login }}.
Please do not report any provider bugs here. This repository does not contain any providers. Please find the appropriate repository and report your issue there or join the [discord](https://discord.gg/5Hus6fM).
Found provider name: `${{ steps.provider_check.outputs.name }}`
- name: Add eyes reaction to all issues
uses: actions-cool/emoji-helper@v1.0.0
with:
type: 'issue'
token: ${{ steps.generate_token.outputs.token }}
emoji: 'eyes'

View file

@ -232,26 +232,23 @@ object APIHolder {
}
fun Context.filterProviderByPreferredMedia(hasHomePageIsRequired: Boolean = true): List<MainAPI> {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val currentPrefMedia =
settingsManager.getInt(this.getString(R.string.prefer_media_type_key), 0)
val default = enumValues<TvType>().sorted().filter { it != TvType.NSFW }.map { it.ordinal }
val defaultSet = default.map { it.toString() }.toSet()
val currentPrefMedia = try {
PreferenceManager.getDefaultSharedPreferences(this)
.getStringSet(this.getString(R.string.prefer_media_type_key), defaultSet)
?.mapNotNull { it.toIntOrNull() ?: return@mapNotNull null }
} catch (e: Throwable) {
null
} ?: default
val langs = this.getApiProviderLangSettings()
val allApis = apis.filter { langs.contains(it.lang) }
.filter { api -> api.hasMainPage || !hasHomePageIsRequired }
return if (currentPrefMedia < 1) {
return if (currentPrefMedia.isEmpty()) {
allApis
} else {
// Filter API depending on preferred media type
val listEnumAnime = listOf(TvType.Anime, TvType.AnimeMovie, TvType.OVA)
val listEnumMovieTv =
listOf(TvType.Movie, TvType.TvSeries, TvType.Cartoon, TvType.AsianDrama)
val listEnumDoc = listOf(TvType.Documentary)
val mediaTypeList = when (currentPrefMedia) {
2 -> listEnumAnime
3 -> listEnumDoc
else -> listEnumMovieTv
}
allApis.filter { api -> api.supportedTypes.any { it in mediaTypeList } }
allApis.filter { api -> api.supportedTypes.any { currentPrefMedia.contains(it.ordinal) } }
}
}
@ -591,19 +588,19 @@ enum class DubStatus(val id: Int) {
Subbed(0),
}
enum class TvType {
Movie,
AnimeMovie,
TvSeries,
Cartoon,
Anime,
OVA,
Torrent,
Documentary,
AsianDrama,
Live,
NSFW,
Others
enum class TvType(value: Int?) {
Movie(1),
AnimeMovie(2),
TvSeries(3),
Cartoon(4),
Anime(5),
OVA(6),
Torrent(7),
Documentary(8),
AsianDrama(9),
Live(10),
NSFW(11),
Others(12)
}
// IN CASE OF FUTURE ANIME MOVIE OR SMTH

View file

@ -697,17 +697,5 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
// }
// }
/*
val relativePath = (Environment.DIRECTORY_DOWNLOADS) + File.separatorChar
val displayName = "output.dex" //""output.dex"
val file = getExternalFilesDir(null)?.absolutePath + File.separatorChar + displayName//"${Environment.getExternalStorageDirectory()}${File.separatorChar}$relativePath$displayName"
println(file)
val realFile = File(file)
println("REAALFILE: ${realFile.exists()} at ${realFile.length()}" )
val src = ExtensionManager.getSourceFromDex(this, "com.example.testdex2.TestClassToDex", File(file))
val output = src?.doMath()
println("MASTER OUTPUT = $output")*/
}
}

View file

@ -0,0 +1,40 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.base64Decode
import com.lagradost.cloudstream3.utils.*
class Acefile : ExtractorApi() {
override val name = "Acefile"
override val mainUrl = "https://acefile.co"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val sources = mutableListOf<ExtractorLink>()
app.get(url).document.select("script").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
val data = getAndUnpack(script.data())
val id = data.substringAfter("{\"id\":\"").substringBefore("\",")
val key = data.substringAfter("var nfck=\"").substringBefore("\";")
app.get("https://acefile.co/local/$id?key=$key").text.let {
base64Decode(
it.substringAfter("JSON.parse(atob(\"").substringBefore("\"))")
).let { res ->
sources.add(
ExtractorLink(
name,
name,
res.substringAfter("\"file\":\"").substringBefore("\","),
"$mainUrl/",
Qualities.Unknown.value,
headers = mapOf("range" to "bytes=0-")
)
)
}
}
}
}
return sources
}
}

View file

@ -0,0 +1,46 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.getQualityFromName
import java.net.URI
class AsianLoad : ExtractorApi() {
override var name = "AsianLoad"
override var mainUrl = "https://asianembed.io"
override val requiresReferer = true
private val sourceRegex = Regex("""sources:[\W\w]*?file:\s*?["'](.*?)["']""")
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
with(app.get(url, referer = referer)) {
sourceRegex.findAll(this.text).forEach { sourceMatch ->
val extractedUrl = sourceMatch.groupValues[1]
// Trusting this isn't mp4, may fuck up stuff
if (URI(extractedUrl).path.endsWith(".m3u8")) {
M3u8Helper.generateM3u8(
name,
extractedUrl,
url,
headers = mapOf("referer" to this.url)
).forEach { link ->
extractedLinksList.add(link)
}
} else if (extractedUrl.endsWith(".mp4")) {
extractedLinksList.add(
ExtractorLink(
name,
name,
extractedUrl,
url.replace(" ", "%20"),
getQualityFromName(sourceMatch.groupValues[2]),
)
)
}
}
return extractedLinksList
}
}
}

View file

@ -0,0 +1,45 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
class Blogger : ExtractorApi() {
override val name = "Blogger"
override val mainUrl = "https://www.blogger.com"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val sources = mutableListOf<ExtractorLink>()
with(app.get(url).document) {
this.select("script").map { script ->
if (script.data().contains("\"streams\":[")) {
val data = script.data().substringAfter("\"streams\":[")
.substringBefore("]")
tryParseJson<List<ResponseSource>>("[$data]")?.map {
sources.add(
ExtractorLink(
name,
name,
it.play_url,
referer = "https://www.youtube.com/",
quality = when (it.format_id) {
18 -> 360
22 -> 720
else -> Qualities.Unknown.value
}
)
)
}
}
}
}
return sources
}
private data class ResponseSource(
@JsonProperty("play_url") val play_url: String,
@JsonProperty("format_id") val format_id: Int
)
}

View file

@ -0,0 +1,29 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
class BullStream : ExtractorApi() {
override val name = "BullStream"
override val mainUrl = "https://bullstream.xyz"
override val requiresReferer = false
val regex = Regex("(?<=sniff\\()(.*)(?=\\)\\);)")
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val data = regex.find(app.get(url).text)?.value
?.replace("\"", "")
?.split(",")
?: return null
val m3u8 = "$mainUrl/m3u8/${data[1]}/${data[2]}/master.txt?s=1&cache=${data[4]}"
println("shiv : $m3u8")
return M3u8Helper.generateM3u8(
name,
m3u8,
url,
headers = mapOf("referer" to url, "accept" to "*/*")
)
}
}

View file

@ -0,0 +1,64 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getQualityFromName
import kotlinx.coroutines.delay
class DoodCxExtractor : DoodLaExtractor() {
override var mainUrl = "https://dood.cx"
}
class DoodShExtractor : DoodLaExtractor() {
override var mainUrl = "https://dood.sh"
}
class DoodWatchExtractor : DoodLaExtractor() {
override var mainUrl = "https://dood.watch"
}
class DoodPmExtractor : DoodLaExtractor() {
override var mainUrl = "https://dood.pm"
}
class DoodToExtractor : DoodLaExtractor() {
override var mainUrl = "https://dood.to"
}
class DoodSoExtractor : DoodLaExtractor() {
override var mainUrl = "https://dood.so"
}
class DoodWsExtractor : DoodLaExtractor() {
override var mainUrl = "https://dood.ws"
}
open class DoodLaExtractor : ExtractorApi() {
override var name = "DoodStream"
override var mainUrl = "https://dood.la"
override val requiresReferer = false
override fun getExtractorUrl(id: String): String {
return "$mainUrl/d/$id"
}
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val response0 = app.get(url).text // html of DoodStream page to look for /pass_md5/...
val md5 =mainUrl+(Regex("/pass_md5/[^']*").find(response0)?.value ?: return null) // get https://dood.ws/pass_md5/...
val trueUrl = app.get(md5, referer = url).text + "zUEJeL3mUN?token=" + md5.substringAfterLast("/") //direct link to extract (zUEJeL3mUN is random)
val quality = Regex("\\d{3,4}p").find(response0.substringAfter("<title>").substringBefore("</title>"))?.groupValues?.get(0)
return listOf(
ExtractorLink(
trueUrl,
this.name,
trueUrl,
mainUrl,
getQualityFromName(quality),
false
)
) // links are valid in 8h
}
}

View file

@ -0,0 +1,54 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.app
class Evoload1 : Evoload() {
override var mainUrl = "https://evoload.io"
}
open class Evoload : ExtractorApi() {
override val name: String = "Evoload"
override val mainUrl: String = "https://www.evoload.io"
//private val srcRegex = Regex("""video .*src="(.*)""""") // would be possible to use the parse and find src attribute
override val requiresReferer = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val lang = url.substring(0, 2)
val flag =
if (lang == "vo") {
" \uD83C\uDDEC\uD83C\uDDE7"
}
else if (lang == "vf"){
" \uD83C\uDDE8\uD83C\uDDF5"
} else {
""
}
val cleaned_url = if (lang == "ht") { // if url doesn't contain a flag and the url starts with http://
url
} else {
url.substring(2, url.length)
}
//println(lang)
//println(cleaned_url)
val id = cleaned_url.replace("https://evoload.io/e/", "") // wanted media id
val csrv_token = app.get("https://csrv.evosrv.com/captcha?m412548=").text // whatever that is
val captchaPass = app.get("https://cd2.evosrv.com/html/jsx/e.jsx").text.take(300).split("captcha_pass = '")[1].split("\'")[0] //extract the captcha pass from the js response (located in the 300 first chars)
val payload = mapOf("code" to id, "csrv_token" to csrv_token, "pass" to captchaPass)
val r = app.post("https://evoload.io/SecurePlayer", data=(payload)).text
val link = Regex("src\":\"(.*?)\"").find(r)?.destructured?.component1() ?: return listOf()
return listOf(
ExtractorLink(
name,
name + flag,
link,
cleaned_url,
Qualities.Unknown.value,
)
)
}
}

View file

@ -0,0 +1,39 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
class Fastream: ExtractorApi() {
override var mainUrl = "https://fastream.to"
override var name = "Fastream"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val id = Regex("emb\\.html\\?(.*)\\=(enc|)").find(url)?.destructured?.component1() ?: return emptyList()
val sources = mutableListOf<ExtractorLink>()
val response = app.post("$mainUrl/dl",
data = mapOf(
Pair("op","embed"),
Pair("file_code",id),
Pair("auto","1")
)).document
response.select("script").apmap { script ->
if (script.data().contains("sources")) {
val m3u8regex = Regex("((https:|http:)\\/\\/.*\\.m3u8)")
val m3u8 = m3u8regex.find(script.data())?.value ?: return@apmap
generateM3u8(
name,
m3u8,
mainUrl
).forEach { link ->
sources.add(link)
}
}
}
return sources
}
}

View file

@ -0,0 +1,38 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
class Filesim : ExtractorApi() {
override val name = "Filesim"
override val mainUrl = "https://files.im"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val sources = mutableListOf<ExtractorLink>()
with(app.get(url).document) {
this.select("script").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
val data = getAndUnpack(script.data()).substringAfter("sources:[").substringBefore("]")
tryParseJson<List<ResponseSource>>("[$data]")?.map {
M3u8Helper.generateM3u8(
name,
it.file,
"$mainUrl/",
).forEach { m3uData -> sources.add(m3uData) }
}
}
}
}
return sources
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
}

View file

@ -0,0 +1,39 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
class GMPlayer : ExtractorApi() {
override val name = "GM Player"
override val mainUrl = "https://gmplayer.xyz"
override val requiresReferer = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val ref = referer ?: return null
val id = url.substringAfter("/video/").substringBefore("/")
val m3u8 = app.post(
"$mainUrl/player/index.php?data=$id&do=getVideo",
mapOf(
"accept" to "*/*",
"referer" to ref,
"x-requested-with" to "XMLHttpRequest",
"origin" to mainUrl
),
data = mapOf("hash" to id, "r" to ref)
).parsed<GmResponse>().videoSource ?: return null
return M3u8Helper.generateM3u8(
name,
m3u8,
ref,
headers = mapOf("accept" to "*/*")
)
}
private data class GmResponse(
val videoSource: String? = null
)
}

View file

@ -0,0 +1,33 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
open class GenericM3U8 : ExtractorApi() {
override var name = "Upstream"
override var mainUrl = "https://upstream.to"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val response = app.get(
url, interceptor = WebViewResolver(
Regex("""master\.m3u8""")
)
)
val sources = mutableListOf<ExtractorLink>()
if (response.url.contains("m3u8"))
M3u8Helper.generateM3u8(
name,
response.url,
url,
headers = response.headers.toMap()
).forEach { link ->
sources.add(link)
}
return sources
}
}

View file

@ -0,0 +1,36 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
open class GuardareStream : ExtractorApi() {
override var name = "Guardare"
override var mainUrl = "https://guardare.stream"
override val requiresReferer = false
data class GuardareJsonData (
@JsonProperty("data") val data : List<GuardareData>,
)
data class GuardareData (
@JsonProperty("file") val file : String,
@JsonProperty("label") val label : String,
@JsonProperty("type") val type : String
)
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val response = app.post(url.replace("/v/","/api/source/"), data = mapOf("d" to mainUrl)).text
val jsonvideodata = AppUtils.parseJson<GuardareJsonData>(response)
return jsonvideodata.data.map {
ExtractorLink(
it.file+".${it.type}",
this.name,
it.file+".${it.type}",
mainUrl,
it.label.filter{ it.isDigit() }.toInt(),
false
)
}
}
}

View file

@ -0,0 +1,100 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
class Neonime7n : Hxfile() {
override val name = "Neonime7n"
override val mainUrl = "https://7njctn.neonime.watch"
override val redirect = false
}
class Neonime8n : Hxfile() {
override val name = "Neonime8n"
override val mainUrl = "https://8njctn.neonime.net"
override val redirect = false
}
class KotakAnimeid : Hxfile() {
override val name = "KotakAnimeid"
override val mainUrl = "https://kotakanimeid.com"
override val requiresReferer = true
}
class Yufiles : Hxfile() {
override val name = "Yufiles"
override val mainUrl = "https://yufiles.com"
}
class Aico : Hxfile() {
override val name = "Aico"
override val mainUrl = "https://aico.pw"
}
open class Hxfile : ExtractorApi() {
override val name = "Hxfile"
override val mainUrl = "https://hxfile.co"
override val requiresReferer = false
open val redirect = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val sources = mutableListOf<ExtractorLink>()
val document = app.get(url, allowRedirects = redirect, referer = referer).document
with(document) {
this.select("script").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
val data =
getAndUnpack(script.data()).substringAfter("sources:[").substringBefore("]")
tryParseJson<List<ResponseSource>>("[$data]")?.map {
sources.add(
ExtractorLink(
name,
name,
it.file,
referer = mainUrl,
quality = when {
url.contains("hxfile.co") -> getQualityFromName(
Regex("\\d\\.(.*?).mp4").find(
document.select("title").text()
)?.groupValues?.get(1).toString()
)
else -> getQualityFromName(it.label)
}
)
)
}
} else if (script.data().contains("\"sources\":[")) {
val data = script.data().substringAfter("\"sources\":[").substringBefore("]")
tryParseJson<List<ResponseSource>>("[$data]")?.map {
sources.add(
ExtractorLink(
name,
name,
it.file,
referer = mainUrl,
quality = when {
it.label?.contains("HD") == true -> Qualities.P720.value
it.label?.contains("SD") == true -> Qualities.P480.value
else -> getQualityFromName(it.label)
}
)
)
}
}
else {
null
}
}
}
return sources
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
}

View file

@ -0,0 +1,81 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getQualityFromName
class Meownime : JWPlayer() {
override val name = "Meownime"
override val mainUrl = "https://meownime.ltd"
}
class DesuOdchan : JWPlayer() {
override val name = "DesuOdchan"
override val mainUrl = "https://desustream.me/odchan/"
}
class DesuArcg : JWPlayer() {
override val name = "DesuArcg"
override val mainUrl = "https://desustream.me/arcg/"
}
class DesuDrive : JWPlayer() {
override val name = "DesuDrive"
override val mainUrl = "https://desustream.me/desudrive/"
}
class DesuOdvip : JWPlayer() {
override val name = "DesuOdvip"
override val mainUrl = "https://desustream.me/odvip/"
}
open class JWPlayer : ExtractorApi() {
override val name = "JWPlayer"
override val mainUrl = "https://www.jwplayer.com"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val sources = mutableListOf<ExtractorLink>()
with(app.get(url).document) {
val data = this.select("script").mapNotNull { script ->
if (script.data().contains("sources: [")) {
script.data().substringAfter("sources: [")
.substringBefore("],").replace("'", "\"")
} else if (script.data().contains("otakudesu('")) {
script.data().substringAfter("otakudesu('")
.substringBefore("');")
} else {
null
}
}
tryParseJson<List<ResponseSource>>("$data")?.map {
sources.add(
ExtractorLink(
name,
name,
it.file,
referer = url,
quality = getQualityFromName(
Regex("(\\d{3,4}p)").find(it.file)?.groupValues?.get(
1
)
)
)
)
}
}
return sources
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
}

View file

@ -0,0 +1,27 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
open class Jawcloud : ExtractorApi() {
override var name = "Jawcloud"
override var mainUrl = "https://jawcloud.co"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val doc = app.get(url).document
val urlString = doc.select("html body div source").attr("src")
val sources = mutableListOf<ExtractorLink>()
if (urlString.contains("m3u8"))
M3u8Helper.generateM3u8(
name,
urlString,
url,
headers = app.get(url).headers.toMap()
).forEach { link -> sources.add(link) }
return sources
}
}

View file

@ -0,0 +1,46 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
class Linkbox : ExtractorApi() {
override val name = "Linkbox"
override val mainUrl = "https://www.linkbox.to"
override val requiresReferer = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val id = url.substringAfter("id=")
val sources = mutableListOf<ExtractorLink>()
app.get("$mainUrl/api/open/get_url?itemId=$id", referer=url).parsedSafe<Responses>()?.data?.rList?.map { link ->
sources.add(
ExtractorLink(
name,
name,
link.url,
url,
getQualityFromName(link.resolution)
)
)
}
return sources
}
data class RList(
@JsonProperty("url") val url: String,
@JsonProperty("resolution") val resolution: String?,
)
data class Data(
@JsonProperty("rList") val rList: List<RList>?,
)
data class Responses(
@JsonProperty("data") val data: Data?,
)
}

View file

@ -0,0 +1,16 @@
package com.lagradost.cloudstream3.extractors
//{"auto":"/manifests/movies/15559/1624728920/qDwu5BOsfAwfTmnnjmkmXA/master.m3u8","1080p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/1080p/index.m3u8","720p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/720p/index.m3u8","360p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/360p/index.m3u8","480p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/480p/index.m3u8"}
object M3u8Manifest {
// URL = first, QUALITY = second
fun extractLinks(m3u8Data: String): ArrayList<Pair<String, String>> {
val data: ArrayList<Pair<String, String>> = ArrayList()
Regex("\"(.*?)\":\"(.*?)\"").findAll(m3u8Data).forEach {
var quality = it.groupValues[1].replace("auto", "Auto")
if (quality != "Auto" && !quality.endsWith('p')) quality += "p"
val url = it.groupValues[2]
data.add(Pair(url, quality))
}
return data
}
}

View file

@ -0,0 +1,29 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
open class Maxstream : ExtractorApi() {
override var name = "Maxstream"
override var mainUrl = "https://maxstream.video/"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
val response = app.get(url).text
val jstounpack = Regex("cript\">eval((.|\\n)*?)</script>").find(response)?.groups?.get(1)?.value
val unpacjed = JsUnpacker(jstounpack).unpack()
val extractedUrl = unpacjed?.let { Regex("""src:"((.|\n)*?)",type""").find(it) }?.groups?.get(1)?.value.toString()
M3u8Helper.generateM3u8(
name,
extractedUrl,
url,
headers = mapOf("referer" to url)
).forEach { link ->
extractedLinksList.add(link)
}
return extractedLinksList
}
}

View file

@ -0,0 +1,7 @@
package com.lagradost.cloudstream3.extractors
open class Mcloud : WcoStream() {
override var name = "Mcloud"
override var mainUrl = "https://mcloud.to"
override val requiresReferer = true
}

View file

@ -0,0 +1,45 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
class MixDropBz : MixDrop(){
override var mainUrl = "https://mixdrop.bz"
}
class MixDropCh : MixDrop(){
override var mainUrl = "https://mixdrop.ch"
}
class MixDropTo : MixDrop(){
override var mainUrl = "https://mixdrop.to"
}
open class MixDrop : ExtractorApi() {
override var name = "MixDrop"
override var mainUrl = "https://mixdrop.co"
private val srcRegex = Regex("""wurl.*?=.*?"(.*?)";""")
override val requiresReferer = false
override fun getExtractorUrl(id: String): String {
return "$mainUrl/e/$id"
}
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
with(app.get(url)) {
getAndUnpack(this.text).let { unpackedText ->
srcRegex.find(unpackedText)?.groupValues?.get(1)?.let { link ->
return listOf(
ExtractorLink(
name,
name,
httpsify(link),
url,
Qualities.Unknown.value,
)
)
}
}
}
return null
}
}

View file

@ -0,0 +1,34 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getAndUnpack
class Mp4Upload : ExtractorApi() {
override var name = "Mp4Upload"
override var mainUrl = "https://www.mp4upload.com"
private val srcRegex = Regex("""player\.src\("(.*?)"""")
override val requiresReferer = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
with(app.get(url)) {
getAndUnpack(this.text).let { unpackedText ->
val quality = unpackedText.lowercase().substringAfter(" height=").substringBefore(" ").toIntOrNull()
srcRegex.find(unpackedText)?.groupValues?.get(1)?.let { link ->
return listOf(
ExtractorLink(
name,
name,
link,
url,
quality ?: Qualities.Unknown.value,
)
)
}
}
}
return null
}
}

View file

@ -0,0 +1,59 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getQualityFromName
import java.net.URI
class MultiQuality : ExtractorApi() {
override var name = "MultiQuality"
override var mainUrl = "https://gogo-play.net"
private val sourceRegex = Regex("""file:\s*['"](.*?)['"],label:\s*['"](.*?)['"]""")
private val m3u8Regex = Regex(""".*?(\d*).m3u8""")
private val urlRegex = Regex("""(.*?)([^/]+$)""")
override val requiresReferer = false
override fun getExtractorUrl(id: String): String {
return "$mainUrl/loadserver.php?id=$id"
}
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
with(app.get(url)) {
sourceRegex.findAll(this.text).forEach { sourceMatch ->
val extractedUrl = sourceMatch.groupValues[1]
// Trusting this isn't mp4, may fuck up stuff
if (URI(extractedUrl).path.endsWith(".m3u8")) {
with(app.get(extractedUrl)) {
m3u8Regex.findAll(this.text).forEach { match ->
extractedLinksList.add(
ExtractorLink(
name,
name = name,
urlRegex.find(this.url)!!.groupValues[1] + match.groupValues[0],
url,
getQualityFromName(match.groupValues[1]),
isM3u8 = true
)
)
}
}
} else if (extractedUrl.endsWith(".mp4")) {
extractedLinksList.add(
ExtractorLink(
name,
"$name ${sourceMatch.groupValues[2]}",
extractedUrl,
url.replace(" ", "%20"),
Qualities.Unknown.value,
)
)
}
}
return extractedLinksList
}
}
}

View file

@ -0,0 +1,67 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
data class DataOptionsJson (
@JsonProperty("flashvars") var flashvars : Flashvars? = Flashvars(),
)
data class Flashvars (
@JsonProperty("metadata") var metadata : String? = null,
@JsonProperty("hlsManifestUrl") var hlsManifestUrl : String? = null, //m3u8
)
data class MetadataOkru (
@JsonProperty("videos") var videos: ArrayList<Videos> = arrayListOf(),
)
data class Videos (
@JsonProperty("name") var name : String,
@JsonProperty("url") var url : String,
@JsonProperty("seekSchema") var seekSchema : Int? = null,
@JsonProperty("disallowed") var disallowed : Boolean? = null
)
class OkRuHttps: OkRu(){
override var mainUrl = "https://ok.ru"
}
open class OkRu : ExtractorApi() {
override var name = "Okru"
override var mainUrl = "http://ok.ru"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val doc = app.get(url).document
val sources = ArrayList<ExtractorLink>()
val datajson = doc.select("div[data-options]").attr("data-options")
if (datajson.isNotBlank()) {
val main = parseJson<DataOptionsJson>(datajson)
val metadatajson = parseJson<MetadataOkru>(main.flashvars?.metadata!!)
val servers = metadatajson.videos
servers.forEach {
val quality = it.name.uppercase()
.replace("MOBILE","144p")
.replace("LOWEST","240p")
.replace("LOW","360p")
.replace("SD","480p")
.replace("HD","720p")
.replace("FULL","1080p")
.replace("QUAD","1440p")
.replace("ULTRA","4k")
val extractedurl = it.url.replace("\\\\u0026", "&")
sources.add(ExtractorLink(
name,
name = this.name,
extractedurl,
url,
getQualityFromName(quality),
isM3u8 = false
))
}
}
return sources
}
}

View file

@ -0,0 +1,99 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.extractorApis
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
/**
* overrideMainUrl is necessary for for other vidstream clones like vidembed.cc
* If they diverge it'd be better to make them separate.
* */
class Pelisplus(val mainUrl: String) {
val name: String = "Vidstream"
private fun getExtractorUrl(id: String): String {
return "$mainUrl/play?id=$id"
}
private fun getDownloadUrl(id: String): String {
return "$mainUrl/download?id=$id"
}
private val normalApis = arrayListOf(MultiQuality())
// https://gogo-stream.com/streaming.php?id=MTE3NDg5
suspend fun getUrl(
id: String,
isCasting: Boolean = false,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
try {
normalApis.apmap { api ->
val url = api.getExtractorUrl(id)
api.getSafeUrl(url, subtitleCallback = subtitleCallback, callback = callback)
}
val extractorUrl = getExtractorUrl(id)
/** Stolen from GogoanimeProvider.kt extractor */
suspendSafeApiCall {
val link = getDownloadUrl(id)
println("Generated vidstream download link: $link")
val page = app.get(link, referer = extractorUrl)
val pageDoc = Jsoup.parse(page.text)
val qualityRegex = Regex("(\\d+)P")
//a[download]
pageDoc.select(".dowload > a")?.apmap { element ->
val href = element.attr("href") ?: return@apmap
val qual = if (element.text()
.contains("HDP")
) "1080" else qualityRegex.find(element.text())?.destructured?.component1()
.toString()
if (!loadExtractor(href, link, subtitleCallback, callback)) {
callback.invoke(
ExtractorLink(
this.name,
name = this.name,
href,
page.url,
getQualityFromName(qual),
element.attr("href").contains(".m3u8")
)
)
}
}
}
with(app.get(extractorUrl)) {
val document = Jsoup.parse(this.text)
val primaryLinks = document.select("ul.list-server-items > li.linkserver")
//val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
// All vidstream links passed to extractors
primaryLinks.distinctBy { it.attr("data-video") }.forEach { element ->
val link = element.attr("data-video")
//val name = element.text()
// Matches vidstream links with extractors
extractorApis.filter { !it.requiresReferer || !isCasting }.apmap { api ->
if (link.startsWith(api.mainUrl)) {
api.getSafeUrl(link, extractorUrl, subtitleCallback, callback)
}
}
}
return true
}
} catch (e: Exception) {
return false
}
}
}

View file

@ -0,0 +1,31 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
open class PlayerVoxzer : ExtractorApi() {
override var name = "Voxzer"
override var mainUrl = "https://player.voxzer.org"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val listurl = url.replace("/view/","/list/")
val urltext = app.get(listurl, referer = url).text
val m3u8regex = Regex("((https:|http:)\\/\\/.*\\.m3u8)")
val sources = mutableListOf<ExtractorLink>()
val listm3 = m3u8regex.find(urltext)?.value
if (listm3?.contains("m3u8") == true)
M3u8Helper.generateM3u8(
name,
listm3,
url,
headers = app.get(url).headers.toMap()
).forEach { link ->
sources.add(link)
}
return sources
}
}

View file

@ -0,0 +1,86 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getPostForm
import org.jsoup.Jsoup
//class SBPlay1 : SBPlay() {
// override var mainUrl = "https://sbplay1.com"
//}
//class SBPlay2 : SBPlay() {
// override var mainUrl = "https://sbplay2.com"
//}
open class SBPlay : ExtractorApi() {
override var mainUrl = "https://sbplay.one"
override var name = "SBPlay"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val response = app.get(url, referer = referer).text
val document = Jsoup.parse(response)
val links = ArrayList<ExtractorLink>()
val tree = document.select("table > tbody > tr > td > a")
for (item in tree) {
val onDownload = item.attr("onclick")
val name = "${this.name} - ${item.text()}"
try {
Regex("download_video\\('(.*?)','(.*?)','(.*?)'\\)").matchEntire(onDownload)?.let {
val id = it.groupValues[1]
val mode = it.groupValues[2]
val hash = it.groupValues[3]
val href = "https://sbplay.one/dl?op=download_orig&id=$id&mode=$mode&hash=$hash"
val hrefResponse = app.get(href).text
app.post("https://sbplay.one/?op=notifications&open=&_=$unixTimeMS", referer = href)
val hrefDocument = Jsoup.parse(hrefResponse)
val hrefSpan = hrefDocument.selectFirst("span > a")
if (hrefSpan == null) {
getPostForm(href, hrefResponse)?.let { form ->
val postDocument = Jsoup.parse(form)
val downloadBtn = postDocument.selectFirst("a.downloadbtn")?.attr("href")
if (downloadBtn.isNullOrEmpty()) {
val hrefSpan2 = postDocument.selectFirst("span > a")?.attr("href")
if (hrefSpan2?.startsWith("https://") == true) {
links.add(
ExtractorLink(
this.name, name,
hrefSpan2, "", Qualities.Unknown.value, false
)
)
} else {
// no link found!!!
}
} else {
links.add(
ExtractorLink(
this.name,
name,
downloadBtn,
"",
Qualities.Unknown.value,
false
)
)
}
}
} else {
val link = hrefSpan.attr("href")
links.add(ExtractorLink(this.name, name, link, "", Qualities.Unknown.value, false))
}
}
} catch (e: Exception) {
logError(e)
}
}
return links
}
}

View file

@ -0,0 +1,45 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
class Solidfiles : ExtractorApi() {
override val name = "Solidfiles"
override val mainUrl = "https://www.solidfiles.com"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val sources = mutableListOf<ExtractorLink>()
with(app.get(url).document) {
this.select("script").map { script ->
if (script.data().contains("\"streamUrl\":")) {
val data = script.data().substringAfter("constant('viewerOptions', {").substringBefore("});")
val source = tryParseJson<ResponseSource>("{$data}")
val quality = Regex("\\d{3,4}p").find(source!!.nodeName)?.groupValues?.get(0)
sources.add(
ExtractorLink(
name,
name,
source.streamUrl,
referer = url,
quality = getQualityFromName(quality)
)
)
}
}
}
return sources
}
private data class ResponseSource(
@JsonProperty("streamUrl") val streamUrl: String,
@JsonProperty("nodeName") val nodeName: String
)
}

View file

@ -0,0 +1,38 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
class SpeedoStream : ExtractorApi() {
override val name = "SpeedoStream"
override val mainUrl = "https://speedostream.com"
override val requiresReferer = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val sources = mutableListOf<ExtractorLink>()
app.get(url, referer = referer).document.select("script").map { script ->
if (script.data().contains("jwplayer(\"vplayer\").setup(")) {
val data = script.data().substringAfter("sources: [")
.substringBefore("],").replace("file", "\"file\"").trim()
tryParseJson<File>(data)?.let {
M3u8Helper.generateM3u8(
name,
it.file,
"$mainUrl/",
).forEach { m3uData -> sources.add(m3uData) }
}
}
}
return sources
}
private data class File(
@JsonProperty("file") val file: String,
)
}

View file

@ -0,0 +1,126 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
class Ssbstream : StreamSB() {
override var mainUrl = "https://ssbstream.net"
}
class SBfull : StreamSB() {
override var mainUrl = "https://sbfull.com"
}
class StreamSB1 : StreamSB() {
override var mainUrl = "https://sbplay1.com"
}
class StreamSB2 : StreamSB() {
override var mainUrl = "https://sbplay2.com"
}
class StreamSB3 : StreamSB() {
override var mainUrl = "https://sbplay3.com"
}
class StreamSB4 : StreamSB() {
override var mainUrl = "https://cloudemb.com"
}
class StreamSB5 : StreamSB() {
override var mainUrl = "https://sbplay.org"
}
class StreamSB6 : StreamSB() {
override var mainUrl = "https://embedsb.com"
}
class StreamSB7 : StreamSB() {
override var mainUrl = "https://pelistop.co"
}
class StreamSB8 : StreamSB() {
override var mainUrl = "https://streamsb.net"
}
class StreamSB9 : StreamSB() {
override var mainUrl = "https://sbplay.one"
}
class StreamSB10 : StreamSB() {
override var mainUrl = "https://sbplay2.xyz"
}
// This is a modified version of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/genoanime/src/eu/kanade/tachiyomi/animeextension/en/genoanime/extractors/StreamSBExtractor.kt
// The following code is under the Apache License 2.0 https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE
open class StreamSB : ExtractorApi() {
override var name = "StreamSB"
override var mainUrl = "https://watchsb.com"
override val requiresReferer = false
private val hexArray = "0123456789ABCDEF".toCharArray()
private fun bytesToHex(bytes: ByteArray): String {
val hexChars = CharArray(bytes.size * 2)
for (j in bytes.indices) {
val v = bytes[j].toInt() and 0xFF
hexChars[j * 2] = hexArray[v ushr 4]
hexChars[j * 2 + 1] = hexArray[v and 0x0F]
}
return String(hexChars)
}
data class Subs (
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
)
data class StreamData (
@JsonProperty("file") val file: String,
@JsonProperty("cdn_img") val cdnImg: String,
@JsonProperty("hash") val hash: String,
@JsonProperty("subs") val subs: List<Subs>?,
@JsonProperty("length") val length: String,
@JsonProperty("id") val id: String,
@JsonProperty("title") val title: String,
@JsonProperty("backup") val backup: String,
)
data class Main (
@JsonProperty("stream_data") val streamData: StreamData,
@JsonProperty("status_code") val statusCode: Int,
)
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val regexID = Regex("(embed-[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+|\\/e\\/[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)")
val id = regexID.findAll(url).map {
it.value.replace(Regex("(embed-|\\/e\\/)"),"")
}.first()
val bytes = id.toByteArray()
val bytesToHex = bytesToHex(bytes)
val master = "$mainUrl/sources43/6d6144797752744a454267617c7c${bytesToHex.lowercase()}7c7c4e61755a56456f34385243727c7c73747265616d7362/6b4a33767968506e4e71374f7c7c343837323439333133333462353935333633373836643638376337633462333634663539343137373761333635313533333835333763376333393636363133393635366136323733343435323332376137633763373337343732363536313664373336327c7c504d754478413835306633797c7c73747265616d7362"
val headers = mapOf(
"watchsb" to "streamsb",
)
val urltext = app.get(master,
headers = headers,
allowRedirects = false
).text
val mapped = urltext.let { parseJson<Main>(it) }
val testurl = app.get(mapped.streamData.file, headers = headers).text
// val urlmain = mapped.streamData.file.substringBefore("/hls/")
if (urltext.contains("m3u8") && testurl.contains("EXTM3U"))
return M3u8Helper.generateM3u8(
name,
mapped.streamData.file,
url,
headers = headers
)
return null
}
}

View file

@ -0,0 +1,33 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
class StreamTape : ExtractorApi() {
override var name = "StreamTape"
override var mainUrl = "https://streamtape.com"
override val requiresReferer = false
private val linkRegex =
Regex("""'robotlink'\)\.innerHTML = '(.+?)'\+ \('(.+?)'\)""")
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
with(app.get(url)) {
linkRegex.find(this.text)?.let {
val extractedUrl = "https:${it.groups[1]!!.value + it.groups[2]!!.value.substring(3,)}"
return listOf(
ExtractorLink(
name,
name,
extractedUrl,
url,
Qualities.Unknown.value,
)
)
}
}
return null
}
}

View file

@ -0,0 +1,39 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.JsUnpacker
import com.lagradost.cloudstream3.utils.Qualities
import java.net.URI
class Streamhub : ExtractorApi() {
override var mainUrl = "https://streamhub.to"
override var name = "Streamhub"
override val requiresReferer = false
override fun getExtractorUrl(id: String): String {
return "$mainUrl/e/$id"
}
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val response = app.get(url).text
Regex("eval((.|\\n)*?)</script>").find(response)?.groupValues?.get(1)?.let { jsEval ->
JsUnpacker("eval$jsEval").unpack()?.let { unPacked ->
Regex("sources:\\[\\{src:\"(.*?)\"").find(unPacked)?.groupValues?.get(1)?.let { link ->
return listOf(
ExtractorLink(
this.name,
this.name,
link,
referer ?: "",
Qualities.Unknown.value,
URI(link).path.endsWith(".m3u8")
)
)
}
}
}
return null
}
}

View file

@ -0,0 +1,60 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.nicehttp.RequestBodyTypes
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
class Streamlare : Slmaxed() {
override val mainUrl = "https://streamlare.com/"
}
open class Slmaxed : ExtractorApi() {
override val name = "Streamlare"
override val mainUrl = "https://slmaxed.com/"
override val requiresReferer = true
// https://slmaxed.com/e/oLvgezw3LjPzbp8E -> oLvgezw3LjPzbp8E
val embedRegex = Regex("""/e/([^/]*)""")
data class JsonResponse(
@JsonProperty val status: String? = null,
@JsonProperty val message: String? = null,
@JsonProperty val type: String? = null,
@JsonProperty val token: String? = null,
@JsonProperty val result: Map<String, Result>? = null
)
data class Result(
@JsonProperty val label: String? = null,
@JsonProperty val file: String? = null,
@JsonProperty val type: String? = null
)
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val id = embedRegex.find(url)!!.groupValues[1]
val json = app.post(
"${mainUrl}api/video/stream/get",
requestBody = """{"id":"$id"}""".toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull())
).parsed<JsonResponse>()
return json.result?.mapNotNull {
it.value.let { result ->
ExtractorLink(
this.name,
this.name,
result.file ?: return@mapNotNull null,
url,
result.label?.replace("p", "", ignoreCase = true)?.trim()?.toIntOrNull()
?: Qualities.Unknown.value,
isM3u8 = result.type?.contains("hls", ignoreCase = true) == true
)
}
}
}
}

View file

@ -0,0 +1,41 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
data class Files(
@JsonProperty("file") val id: String,
@JsonProperty("label") val label: String? = null,
)
open class Supervideo : ExtractorApi() {
override var name = "Supervideo"
override var mainUrl = "https://supervideo.tv"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
val response = app.get(url).text
val jstounpack = Regex("eval((.|\\n)*?)</script>").find(response)?.groups?.get(1)?.value
val unpacjed = JsUnpacker(jstounpack).unpack()
val extractedUrl = unpacjed?.let { Regex("""sources:((.|\n)*?)image""").find(it) }?.groups?.get(1)?.value.toString().replace("file",""""file"""").replace("label",""""label"""").substringBeforeLast(",")
val parsedlinks = parseJson<List<Files>>(extractedUrl)
parsedlinks.forEach { data ->
if (data.label.isNullOrBlank()){ // mp4 links (with labels) are slow. Use only m3u8 link.
M3u8Helper.generateM3u8(
name,
data.id,
url,
headers = mapOf("referer" to url)
).forEach { link ->
extractedLinksList.add(link)
}
}
}
return extractedLinksList
}
}

View file

@ -0,0 +1,42 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
open class Tantifilm : ExtractorApi() {
override var name = "Tantifilm"
override var mainUrl = "https://cercafilm.net"
override val requiresReferer = false
data class TantifilmJsonData (
@JsonProperty("success") val success : Boolean,
@JsonProperty("data") val data : List<TantifilmData>,
@JsonProperty("captions")val captions : List<String>,
@JsonProperty("is_vr") val is_vr : Boolean
)
data class TantifilmData (
@JsonProperty("file") val file : String,
@JsonProperty("label") val label : String,
@JsonProperty("type") val type : String
)
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val link = "$mainUrl/api/source/${url.substringAfterLast("/")}"
val response = app.post(link).text.replace("""\""","")
val jsonvideodata = parseJson<TantifilmJsonData>(response)
return jsonvideodata.data.map {
ExtractorLink(
it.file+".${it.type}",
this.name,
it.file+".${it.type}",
mainUrl,
it.label.filter{ it.isDigit() }.toInt(),
false
)
}
}
}

View file

@ -0,0 +1,41 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
class Cinestart: Tomatomatela() {
override var name = "Cinestart"
override var mainUrl = "https://cinestart.net"
override val details = "vr.php?v="
}
open class Tomatomatela : ExtractorApi() {
override var name = "Tomatomatela"
override var mainUrl = "https://tomatomatela.com"
override val requiresReferer = false
private data class Tomato (
@JsonProperty("status") val status: Int,
@JsonProperty("file") val file: String
)
open val details = "details.php?v="
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val link = url.replace("$mainUrl/embed.html#","$mainUrl/$details")
val server = app.get(link, allowRedirects = false).text
val json = parseJson<Tomato>(server)
if (json.status == 200) return listOf(
ExtractorLink(
name,
name,
json.file,
"",
Qualities.Unknown.value,
isM3u8 = false
)
)
return null
}
}

View file

@ -0,0 +1,59 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
class UpstreamExtractor: ExtractorApi() {
override val name: String = "Upstream.to"
override val mainUrl: String = "https://upstream.to"
override val requiresReferer = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
// WIP: m3u8 link fetched but sometimes not playing
//Log.i(this.name, "Result => (no extractor) ${url}")
val sources: MutableList<ExtractorLink> = mutableListOf()
val doc = app.get(url, referer = referer).text
if (doc.isNotBlank()) {
var reg = Regex("(?<=master)(.*)(?=hls)")
val result = reg.find(doc)?.groupValues?.map {
it.trim('|')
}?.toList()
reg = Regex("(?<=\\|file\\|)(.*)(?=\\|remove\\|)")
val domainList = reg.find(doc)?.groupValues?.get(1)?.split("|")
var domain = when (!domainList.isNullOrEmpty()) {
true -> {
if (domainList.isNotEmpty()) {
var domName = ""
for (part in domainList) {
domName = "${part}.${domName}"
}
domName.trimEnd('.')
} else { "" }
}
false -> ""
}
//Log.i(this.name, "Result => (domain) ${domain}")
if (domain.isEmpty()) {
domain = "s96.upstreamcdn.co"
//Log.i(this.name, "Result => (default domain) ${domain}")
}
result?.forEach {
val linkUrl = "https://${domain}/hls/${it}/master.m3u8"
sources.add(
ExtractorLink(
name = "Upstream m3u8",
source = this.name,
url = linkUrl,
quality = Qualities.Unknown.value,
referer = referer ?: linkUrl,
isM3u8 = true
)
)
}
}
return sources
}
}

View file

@ -0,0 +1,49 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.app
class Uqload1 : Uqload() {
override var mainUrl = "https://uqload.com"
}
open class Uqload : ExtractorApi() {
override val name: String = "Uqload"
override val mainUrl: String = "https://www.uqload.com"
private val srcRegex = Regex("""sources:.\[(.*?)\]""") // would be possible to use the parse and find src attribute
override val requiresReferer = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val lang = url.substring(0, 2)
val flag =
if (lang == "vo") {
" \uD83C\uDDEC\uD83C\uDDE7"
}
else if (lang == "vf"){
" \uD83C\uDDE8\uD83C\uDDF5"
} else {
""
}
val cleaned_url = if (lang == "ht") { // if url doesn't contain a flag and the url starts with http://
url
} else {
url.substring(2, url.length)
}
with(app.get(cleaned_url)) { // raised error ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED (3003) is due to the response: "error_nofile"
srcRegex.find(this.text)?.groupValues?.get(1)?.replace("\"", "")?.let { link ->
return listOf(
ExtractorLink(
name,
name + flag,
link,
cleaned_url,
Qualities.Unknown.value,
)
)
}
}
return null
}
}

View file

@ -0,0 +1,117 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
import org.mozilla.javascript.Context
import org.mozilla.javascript.EvaluatorException
import org.mozilla.javascript.Scriptable
import java.util.*
open class Userload : ExtractorApi() {
override var name = "Userload"
override var mainUrl = "https://userload.co"
override val requiresReferer = false
private fun splitInput(input: String): List<String> {
var counter = 0
val array = ArrayList<String>()
var buffer = ""
for (c in input) {
when (c) {
'(' -> counter++
')' -> counter--
else -> {}
}
buffer += c
if (counter == 0) {
if (buffer.isNotBlank() && buffer != "+")
array.add(buffer)
buffer = ""
}
}
return array
}
private fun evaluateMath(mathExpression : String): String {
val rhino = Context.enter()
rhino.initStandardObjects()
rhino.optimizationLevel = -1
val scope: Scriptable = rhino.initStandardObjects()
return try {
rhino.evaluateString(scope, "eval($mathExpression)", "JavaScript", 1, null).toString()
}
catch (e: EvaluatorException){
""
}
}
private fun decodeVideoJs(text: String): List<String> {
text.replace("""\s+|/\*.*?\*/""".toRegex(), "")
val data = text.split("""+(゚Д゚)[゚o゚]""")[1]
val chars = data.split("""+ (゚Д゚)[゚ε゚]+""").drop(1)
val newchars = chars.map { char ->
char.replace("(o゚ー゚o)", "u")
.replace("c", "0")
.replace("(゚Д゚)['0']", "c")
.replace("゚Θ゚", "1")
.replace("!+[]", "1")
.replace("-~", "1+")
.replace("o", "3")
.replace("_", "3")
.replace("゚ー゚", "4")
.replace("(+", "(")
}
val subchar = mutableListOf<String>()
newchars.dropLast(1).forEach { v ->
subchar.add(splitInput(v).map { evaluateMath(it).substringBefore(".") }.toString().filter { it.isDigit() })
}
var txtresult = ""
subchar.forEach{
txtresult = txtresult.plus(Char(it.toInt(8)))
}
val val1 = Regex(""""morocco="((.|\\n)*?)"&mycountry="""").find(txtresult)?.groups?.get(1)?.value.toString().drop(1).dropLast(1)
val val2 = txtresult.substringAfter("""&mycountry="+""").substringBefore(")")
return listOf(
val1,
val2
)
}
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
val response = app.get(url).text
val jsToUnpack = Regex("ext/javascript\">eval((.|\\n)*?)</script>").find(response)?.groups?.get(1)?.value
val unpacked = JsUnpacker(jsToUnpack).unpack()
val videoJs = app.get("$mainUrl/api/assets/userload/js/videojs.js")
val videoJsToDecode = videoJs.text
val values = decodeVideoJs(videoJsToDecode)
val morocco = unpacked!!.split(";").filter { it.contains(values[0]) }[0].split("=")[1].drop(1).dropLast(1)
val mycountry = unpacked.split(";").filter { it.contains(values[1]) }[0].split("=")[1].drop(1).dropLast(1)
val videoLinkPage = app.post("$mainUrl/api/request/", data = mapOf(
"morocco" to morocco,
"mycountry" to mycountry
))
val videoLink = videoLinkPage.text
val nameSource = app.get(url).document.head().selectFirst("title")!!.text()
extractedLinksList.add(
ExtractorLink(
name,
name,
videoLink,
mainUrl,
getQualityFromName(nameSource),
)
)
return extractedLinksList
}
}

View file

@ -0,0 +1,68 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.loadExtractor
class VidSrcExtractor2 : VidSrcExtractor() {
override val mainUrl = "https://vidsrc.me/embed"
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val newUrl = url.lowercase().replace(mainUrl, super.mainUrl)
super.getUrl(newUrl, referer, subtitleCallback, callback)
}
}
open class VidSrcExtractor : ExtractorApi() {
override val name = "VidSrc"
private val absoluteUrl = "https://v2.vidsrc.me"
override val mainUrl = "$absoluteUrl/embed"
override val requiresReferer = false
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val iframedoc = app.get(url).document
val serverslist =
iframedoc.select("div#sources.button_content div#content div#list div").map {
val datahash = it.attr("data-hash")
if (datahash.isNotBlank()) {
val links = try {
app.get("$absoluteUrl/src/$datahash", referer = "https://source.vidsrc.me/").url
} catch (e: Exception) {
""
}
links
} else ""
}
serverslist.apmap { server ->
val linkfixed = server.replace("https://vidsrc.xyz/", "https://embedsito.com/")
if (linkfixed.contains("/pro")) {
val srcresponse = app.get(server, referer = absoluteUrl).text
val m3u8Regex = Regex("((https:|http:)//.*\\.m3u8)")
val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@apmap
M3u8Helper.generateM3u8(
name,
srcm3u8,
absoluteUrl
).forEach(callback)
} else {
loadExtractor(linkfixed, url, subtitleCallback, callback)
}
}
}
}

View file

@ -0,0 +1,271 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
import kotlinx.coroutines.delay
import java.math.BigInteger
class VideovardSX : WcoStream() {
override var mainUrl = "https://videovard.sx"
}
class VideoVard : ExtractorApi() {
override var name = "Videovard" // Cause works for animekisa and wco
override var mainUrl = "https://videovard.to"
override val requiresReferer = false
//The following code was extracted from https://github.com/saikou-app/saikou/blob/main/app/src/main/java/ani/saikou/parsers/anime/extractors/VideoVard.kt
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val id = url.substringAfter("e/").substringBefore("/")
val sources = mutableListOf<ExtractorLink>()
val hash = app.get("$mainUrl/api/make/download/$id").parsed<HashResponse>()
delay(11_000)
val resm3u8 = app.post(
"$mainUrl/api/player/setup",
mapOf("Referer" to "$mainUrl/"),
data = mapOf(
"cmd" to "get_stream",
"file_code" to id,
"hash" to hash.hash!!
)
).parsed<SetupResponse>()
val m3u8 = decode(resm3u8.src!!, resm3u8.seed)
sources.addAll(
generateM3u8(
name,
m3u8,
mainUrl,
headers = mapOf("Referer" to mainUrl)
)
)
return sources
}
companion object {
private val big0 = 0.toBigInteger()
private val big3 = 3.toBigInteger()
private val big4 = 4.toBigInteger()
private val big15 = 15.toBigInteger()
private val big16 = 16.toBigInteger()
private val big255 = 255.toBigInteger()
private fun decode(dataFile: String, seed: String): String {
val dataSeed = replace(seed)
val newDataSeed = binaryDigest(dataSeed)
val newDataFile = bytes2blocks(ascii2bytes(dataFile))
var list = listOf(1633837924, 1650680933).map { it.toBigInteger() }
val xorList = mutableListOf<BigInteger>()
for (i in newDataFile.indices step 2) {
val temp = newDataFile.slice(i..i + 1)
xorList += xorBlocks(list, tearDecode(temp, newDataSeed))
list = temp
}
val result = replace(unPad(blocks2bytes(xorList)).map { it.toInt().toChar() }.joinToString(""))
return padLastChars(result)
}
private fun binaryDigest(input: String): List<BigInteger> {
val keys = listOf(1633837924, 1650680933, 1667523942, 1684366951).map { it.toBigInteger() }
var list1 = keys.slice(0..1)
var list2 = list1
val blocks = bytes2blocks(digestPad(input))
for (i in blocks.indices step 4) {
list1 = tearCode(xorBlocks(blocks.slice(i..i + 1), list1), keys).toMutableList()
list2 = tearCode(xorBlocks(blocks.slice(i + 2..i + 3), list2), keys).toMutableList()
val temp = list1[0]
list1[0] = list1[1]
list1[1] = list2[0]
list2[0] = list2[1]
list2[1] = temp
}
return listOf(list1[0], list1[1], list2[0], list2[1])
}
private fun tearDecode(a90: List<BigInteger>, a91: List<BigInteger>): MutableList<BigInteger> {
var (a95, a96) = a90
var a97 = (-957401312).toBigInteger()
for (_i in 0 until 32) {
a96 -= ((((a95 shl 4) xor rShift(a95, 5)) + a95) xor (a97 + a91[rShift(a97, 11).and(3.toBigInteger()).toInt()]))
a97 += 1640531527.toBigInteger()
a95 -= ((((a96 shl 4) xor rShift(a96, 5)) + a96) xor (a97 + a91[a97.and(3.toBigInteger()).toInt()]))
}
return mutableListOf(a95, a96)
}
private fun digestPad(string: String): List<BigInteger> {
val empList = mutableListOf<BigInteger>()
val length = string.length
val extra = big15 - (length.toBigInteger() % big16)
empList.add(extra)
for (i in 0 until length) {
empList.add(string[i].code.toBigInteger())
}
for (i in 0 until extra.toInt()) {
empList.add(big0)
}
return empList
}
private fun bytes2blocks(a22: List<BigInteger>): List<BigInteger> {
val empList = mutableListOf<BigInteger>()
val length = a22.size
var listIndex = 0
for (i in 0 until length) {
val subIndex = i % 4
val shiftedByte = a22[i] shl (3 - subIndex) * 8
if (subIndex == 0) {
empList.add(shiftedByte)
} else {
empList[listIndex] = empList[listIndex] or shiftedByte
}
if (subIndex == 3) listIndex += 1
}
return empList
}
private fun blocks2bytes(inp: List<BigInteger>): List<BigInteger> {
val tempList = mutableListOf<BigInteger>()
inp.indices.forEach { i ->
tempList += (big255 and rShift(inp[i], 24))
tempList += (big255 and rShift(inp[i], 16))
tempList += (big255 and rShift(inp[i], 8))
tempList += (big255 and inp[i])
}
return tempList
}
private fun unPad(a46: List<BigInteger>): List<BigInteger> {
val evenOdd = a46[0].toInt().mod(2)
return (1 until (a46.size - evenOdd)).map {
a46[it]
}
}
private fun xorBlocks(a76: List<BigInteger>, a77: List<BigInteger>): List<BigInteger> {
return listOf(a76[0] xor a77[0], a76[1] xor a77[1])
}
private fun rShift(input: BigInteger, by: Int): BigInteger {
return (input.mod(4294967296.toBigInteger()) shr by)
}
private fun tearCode(list1: List<BigInteger>, list2: List<BigInteger>): MutableList<BigInteger> {
var a1 = list1[0]
var a2 = list1[1]
var temp = big0
for (_i in 0 until 32) {
a1 += (a2 shl 4 xor rShift(a2, 5)) + a2 xor temp + list2[(temp and big3).toInt()]
temp -= 1640531527.toBigInteger()
a2 += (a1 shl 4 xor rShift(a1, 5)) + a1 xor temp + list2[(rShift(temp, 11) and big3).toInt()]
}
return mutableListOf(a1, a2)
}
private fun ascii2bytes(input: String): List<BigInteger> {
val abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
val abcMap = abc.mapIndexed { i, c -> c to i.toBigInteger() }.toMap()
var index = -1
val length = input.length
var listIndex = 0
val bytes = mutableListOf<BigInteger>()
while (true) {
for (i in input) {
if (abc.contains(i)) {
index++
break
}
}
bytes.add((abcMap[input.getOrNull(index)?:return bytes]!! * big4))
while (true) {
index++
if (abc.contains(input[index])) {
break
}
}
var temp = abcMap[input[index]]!!
bytes[listIndex] = bytes[listIndex] or rShift(temp, 4)
listIndex++
temp = (big15.and(temp))
if ((temp == big0) && (index == (length - 1))) return bytes
bytes.add((temp * big4 * big4))
while (true) {
index++
if (index >= length) return bytes
if (abc.contains(input[index])) break
}
temp = abcMap[input[index]]!!
bytes[listIndex] = bytes[listIndex] or rShift(temp, 2)
listIndex++
temp = (big3 and temp)
if ((temp == big0) && (index == (length - 1))) {
return bytes
}
bytes.add((temp shl 6))
for (i in input) {
index++
if (abc.contains(input[index])) {
break
}
}
bytes[listIndex] = bytes[listIndex] or abcMap[input[index]]!!
listIndex++
}
}
private fun replace(a: String): String {
val map = mapOf(
'0' to '5',
'1' to '6',
'2' to '7',
'5' to '0',
'6' to '1',
'7' to '2'
)
var b = ""
a.forEach {
b += if (map.containsKey(it)) map[it] else it
}
return b
}
private fun padLastChars(input:String):String{
return if(input.reversed()[3].isDigit()) input
else input.dropLast(4)
}
private data class HashResponse(
val hash: String? = null,
val version:String? = null
)
private data class SetupResponse(
val seed: String,
val src: String?=null,
val link:String?=null
)
}
}

View file

@ -0,0 +1,101 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.argamap
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.extractorApis
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
/**
* overrideMainUrl is necessary for for other vidstream clones like vidembed.cc
* If they diverge it'd be better to make them separate.
* */
class Vidstream(val mainUrl: String) {
val name: String = "Vidstream"
private fun getExtractorUrl(id: String): String {
return "$mainUrl/streaming.php?id=$id"
}
private fun getDownloadUrl(id: String): String {
return "$mainUrl/download?id=$id"
}
private val normalApis = arrayListOf(MultiQuality())
// https://gogo-stream.com/streaming.php?id=MTE3NDg5
suspend fun getUrl(
id: String,
isCasting: Boolean = false,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
): Boolean {
val extractorUrl = getExtractorUrl(id)
argamap(
{
normalApis.apmap { api ->
val url = api.getExtractorUrl(id)
api.getSafeUrl(
url,
callback = callback,
subtitleCallback = subtitleCallback
)
}
}, {
/** Stolen from GogoanimeProvider.kt extractor */
val link = getDownloadUrl(id)
println("Generated vidstream download link: $link")
val page = app.get(link, referer = extractorUrl)
val pageDoc = Jsoup.parse(page.text)
val qualityRegex = Regex("(\\d+)P")
//a[download]
pageDoc.select(".dowload > a")?.apmap { element ->
val href = element.attr("href") ?: return@apmap
val qual = if (element.text()
.contains("HDP")
) "1080" else qualityRegex.find(element.text())?.destructured?.component1()
.toString()
if (!loadExtractor(href, link, subtitleCallback, callback)) {
callback.invoke(
ExtractorLink(
this.name,
name = this.name,
href,
page.url,
getQualityFromName(qual),
element.attr("href").contains(".m3u8")
)
)
}
}
}, {
with(app.get(extractorUrl)) {
val document = Jsoup.parse(this.text)
val primaryLinks = document.select("ul.list-server-items > li.linkserver")
//val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
// All vidstream links passed to extractors
primaryLinks.distinctBy { it.attr("data-video") }.forEach { element ->
val link = element.attr("data-video")
//val name = element.text()
// Matches vidstream links with extractors
extractorApis.filter { !it.requiresReferer || !isCasting }.apmap { api ->
if (link.startsWith(api.mainUrl)) {
api.getSafeUrl(link, extractorUrl, subtitleCallback, callback)
}
}
}
}
}
)
return true
}
}

View file

@ -0,0 +1,51 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
open class VoeExtractor : ExtractorApi() {
override val name: String = "Voe"
override val mainUrl: String = "https://voe.sx"
override val requiresReferer = false
private data class ResponseLinks(
@JsonProperty("hls") val url: String?,
@JsonProperty("video_height") val label: Int?
//val type: String // Mp4
)
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
val doc = app.get(url).text
if (doc.isNotBlank()) {
val start = "const sources ="
var src = doc.substring(doc.indexOf(start))
src = src.substring(start.length, src.indexOf(";"))
.replace("0,", "0")
.trim()
//Log.i(this.name, "Result => (src) ${src}")
parseJson<ResponseLinks?>(src)?.let { voelink ->
//Log.i(this.name, "Result => (voelink) ${voelink}")
val linkUrl = voelink.url
val linkLabel = voelink.label?.toString() ?: ""
if (!linkUrl.isNullOrEmpty()) {
extractedLinksList.add(
ExtractorLink(
name = this.name,
source = this.name,
url = linkUrl,
quality = getQualityFromName(linkLabel),
referer = url,
isM3u8 = true
)
)
}
}
}
return extractedLinksList
}
}

View file

@ -0,0 +1,23 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
open class WatchSB : ExtractorApi() {
override var name = "WatchSB"
override var mainUrl = "https://watchsb.com"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val response = app.get(
url, interceptor = WebViewResolver(
Regex("""master\.m3u8""")
)
)
return generateM3u8(name, response.url, url, headers = response.headers.toMap())
}
}

View file

@ -0,0 +1,127 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.extractors.helper.NineAnimeHelper.cipher
import com.lagradost.cloudstream3.extractors.helper.NineAnimeHelper.encrypt
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
class Vidstreamz : WcoStream() {
override var mainUrl = "https://vidstreamz.online"
}
class Vizcloud : WcoStream() {
override var mainUrl = "https://vizcloud2.ru"
}
class Vizcloud2 : WcoStream() {
override var mainUrl = "https://vizcloud2.online"
}
class VizcloudOnline : WcoStream() {
override var mainUrl = "https://vizcloud.online"
}
class VizcloudXyz : WcoStream() {
override var mainUrl = "https://vizcloud.xyz"
}
class VizcloudLive : WcoStream() {
override var mainUrl = "https://vizcloud.live"
}
class VizcloudInfo : WcoStream() {
override var mainUrl = "https://vizcloud.info"
}
class MwvnVizcloudInfo : WcoStream() {
override var mainUrl = "https://mwvn.vizcloud.info"
}
class VizcloudDigital : WcoStream() {
override var mainUrl = "https://vizcloud.digital"
}
class VizcloudCloud : WcoStream() {
override var mainUrl = "https://vizcloud.cloud"
}
class VizcloudSite : WcoStream() {
override var mainUrl = "https://vizcloud.site"
}
open class WcoStream : ExtractorApi() {
override var name = "VidStream" // Cause works for animekisa and wco
override var mainUrl = "https://vidstream.pro"
override val requiresReferer = false
private val regex = Regex("(.+?/)e(?:mbed)?/([a-zA-Z0-9]+)")
companion object {
// taken from https://github.com/saikou-app/saikou/blob/b35364c8c2a00364178a472fccf1ab72f09815b4/app/src/main/java/ani/saikou/parsers/anime/extractors/VizCloud.kt
// GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md
private var lastChecked = 0L
private const val jsonLink =
"https://raw.githubusercontent.com/chenkaslowankiya/BruhFlow/main/keys.json"
private var cipherKey: VizCloudKey? = null
suspend fun getKey(): VizCloudKey {
cipherKey =
if (cipherKey != null && (lastChecked - System.currentTimeMillis()) < 1000 * 60 * 30) cipherKey!!
else {
lastChecked = System.currentTimeMillis()
app.get(jsonLink).parsed()
}
return cipherKey!!
}
data class VizCloudKey(
@JsonProperty("cipherKey") val cipherKey: String,
@JsonProperty("mainKey") val mainKey: String,
@JsonProperty("encryptKey") val encryptKey: String,
@JsonProperty("dashTable") val dashTable: String
)
private const val baseTable =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+=/_"
private fun dashify(id: String, dashTable: String): String {
val table = dashTable.split(" ")
return id.mapIndexedNotNull { i, c ->
table.getOrNull((baseTable.indexOf(c) * 16) + (i % 16))
}.joinToString("-")
}
}
//private val key = "LCbu3iYC7ln24K7P" // key credits @Modder4869
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val group = regex.find(url)?.groupValues!!
val host = group[1]
val viz = getKey()
val id = encrypt(
cipher(
viz.cipherKey,
encrypt(group[2], viz.encryptKey).also { println(it) }
).also { println(it) },
viz.encryptKey
).also { println(it) }
val link =
"${host}mediainfo/${dashify(id, viz.dashTable)}?key=${viz.mainKey}" //
val response = app.get(link, referer = referer)
data class Sources(@JsonProperty("file") val file: String)
data class Media(@JsonProperty("sources") val sources: List<Sources>)
data class Data(@JsonProperty("media") val media: Media)
data class Response(@JsonProperty("data") val data: Data)
if (!response.text.startsWith("{")) throw ErrorLoadingException("Seems like 9Anime kiddies changed stuff again, Go touch some grass for bout an hour Or use a different Server")
return response.parsed<Response>().data.media.sources.map {
ExtractorLink(name, it.file,it.file,host,Qualities.Unknown.value,it.file.contains(".m3u8"))
}
}
}

View file

@ -0,0 +1,93 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
class LayarKaca: XStreamCdn() {
override val name: String = "LayarKaca-xxi"
override val mainUrl: String = "https://layarkacaxxi.icu"
}
class DBfilm: XStreamCdn() {
override val name: String = "DBfilm"
override val mainUrl: String = "https://dbfilm.bar"
}
class Luxubu : XStreamCdn(){
override val name: String = "FE"
override val mainUrl: String = "https://www.luxubu.review"
}
class FEmbed: XStreamCdn() {
override val name: String = "FEmbed"
override val mainUrl: String = "https://www.fembed.com"
}
class Fplayer: XStreamCdn() {
override val name: String = "Fplayer"
override val mainUrl: String = "https://fplayer.info"
}
class FeHD: XStreamCdn() {
override val name: String = "FeHD"
override val mainUrl: String = "https://fembed-hd.com"
override var domainUrl: String = "fembed-hd.com"
}
open class XStreamCdn : ExtractorApi() {
override val name: String = "XStreamCdn"
override val mainUrl: String = "https://embedsito.com"
override val requiresReferer = false
open var domainUrl: String = "embedsito.com"
private data class ResponseData(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
//val type: String // Mp4
)
private data class ResponseJson(
@JsonProperty("success") val success: Boolean,
@JsonProperty("data") val data: List<ResponseData>?
)
override fun getExtractorUrl(id: String): String {
return "$domainUrl/api/source/$id"
}
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val headers = mapOf(
"Referer" to url,
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0",
)
val id = url.trimEnd('/').split("/").last()
val newUrl = "https://${domainUrl}/api/source/${id}"
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
with(app.post(newUrl, headers = headers)) {
if (this.code != 200) return listOf()
val text = this.text
if (text.isEmpty()) return listOf()
if (text == """{"success":false,"data":"Video not found or has been removed"}""") return listOf()
AppUtils.parseJson<ResponseJson?>(text)?.let {
if (it.success && it.data != null) {
it.data.forEach { data ->
extractedLinksList.add(
ExtractorLink(
name,
name = name,
data.file,
url,
getQualityFromName(data.label),
)
)
}
}
}
}
return extractedLinksList
}
}

View file

@ -0,0 +1,47 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
class YourUpload: ExtractorApi() {
override val name = "Yourupload"
override val mainUrl = "https://www.yourupload.com"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val sources = mutableListOf<ExtractorLink>()
with(app.get(url).document) {
val quality = Regex("\\d{3,4}p").find(this.select("title").text())?.groupValues?.get(0)
this.select("script").map { script ->
if (script.data().contains("var jwplayerOptions = {")) {
val data =
script.data().substringAfter("var jwplayerOptions = {").substringBefore(",\n")
val link = tryParseJson<ResponseSource>(
"{${
data.replace("file", "\"file\"").replace("'", "\"")
}}"
)
sources.add(
ExtractorLink(
source = name,
name = name,
url = link!!.file,
referer = url,
quality = getQualityFromName(quality)
)
)
}
}
}
return sources
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
)
}

View file

@ -0,0 +1,88 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.schemaStripRegex
import org.schabi.newpipe.extractor.ServiceList
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory
import org.schabi.newpipe.extractor.stream.SubtitlesStream
import org.schabi.newpipe.extractor.stream.VideoStream
class YoutubeShortLinkExtractor : YoutubeExtractor() {
override val mainUrl = "https://youtu.be"
override fun getExtractorUrl(id: String): String {
return "$mainUrl/$id"
}
}
class YoutubeMobileExtractor : YoutubeExtractor() {
override val mainUrl = "https://m.youtube.com"
}
class YoutubeNoCookieExtractor : YoutubeExtractor() {
override val mainUrl = "https://www.youtube-nocookie.com"
}
open class YoutubeExtractor : ExtractorApi() {
override val mainUrl = "https://www.youtube.com"
override val requiresReferer = false
override val name = "YouTube"
companion object {
private var ytVideos: MutableMap<String, List<VideoStream>> = mutableMapOf()
private var ytVideosSubtitles: MutableMap<String, List<SubtitlesStream>> = mutableMapOf()
}
override fun getExtractorUrl(id: String): String {
return "$mainUrl/watch?v=$id"
}
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
if (ytVideos[url].isNullOrEmpty()) {
val link =
YoutubeStreamLinkHandlerFactory.getInstance().fromUrl(
url.replace(
schemaStripRegex, ""
)
)
val s = object : YoutubeStreamExtractor(
ServiceList.YouTube,
link
) {
}
s.fetchPage()
ytVideos[url] = s.videoStreams
ytVideosSubtitles[url] = try {
s.subtitlesDefault.filterNotNull()
} catch (e: Exception) {
logError(e)
emptyList()
}
}
ytVideos[url]?.mapNotNull {
if (it.isVideoOnly || it.height <= 0) return@mapNotNull null
ExtractorLink(
this.name,
this.name,
it.url ?: return@mapNotNull null,
"",
it.height
)
}?.forEach(callback)
ytVideosSubtitles[url]?.mapNotNull {
SubtitleFile(it.languageTag ?: return@mapNotNull null, it.url ?: return@mapNotNull null)
}?.forEach(subtitleCallback)
}
}

View file

@ -0,0 +1,58 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.getAndUnpack
class Zplayer: ZplayerV2() {
override var name: String = "Zplayer"
override var mainUrl: String = "https://zplayer.live"
}
class Upstream: ZplayerV2() {
override var name: String = "Upstream" //Here 'cause works
override var mainUrl: String = "https://upstream.to"
}
class Streamhub2: ZplayerV2() {
override var name = "Streamhub" //Here 'cause works
override var mainUrl = "https://streamhub.to"
}
open class ZplayerV2 : ExtractorApi() {
override var name = "Zplayer V2"
override var mainUrl = "https://v2.zplayer.live"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val doc = app.get(url).document
val sources = mutableListOf<ExtractorLink>()
doc.select("script").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
val testdata = getAndUnpack(script.data())
val m3u8regex = Regex("((https:|http:)\\/\\/.*\\.m3u8)")
m3u8regex.findAll(testdata).map {
it.value
}.toList().apmap { urlm3u8 ->
if (urlm3u8.contains("m3u8")) {
val testurl = app.get(urlm3u8, headers = mapOf("Referer" to url)).text
if (testurl.contains("EXTM3U")) {
M3u8Helper.generateM3u8(
name,
urlm3u8,
url,
headers = mapOf("Referer" to url)
).forEach { link ->
sources.add(link)
}
}
}
}
}
}
return sources
}
}

View file

@ -0,0 +1,32 @@
package com.lagradost.cloudstream3.extractors.helper
import android.util.Log
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
class AsianEmbedHelper {
companion object {
suspend fun getUrls(
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
// Fetch links
val doc = app.get(url).document
val links = doc.select("div#list-server-more > ul > li.linkserver")
if (!links.isNullOrEmpty()) {
links.apmap {
val datavid = it.attr("data-video") ?: ""
//Log.i("AsianEmbed", "Result => (datavid) ${datavid}")
if (datavid.isNotBlank()) {
val res = loadExtractor(datavid, url, subtitleCallback, callback)
Log.i("AsianEmbed", "Result => ($res) (datavid) $datavid")
}
}
}
}
}
}

View file

@ -0,0 +1,112 @@
package com.lagradost.cloudstream3.extractors.helper
// taken from https://github.com/saikou-app/saikou/blob/b35364c8c2a00364178a472fccf1ab72f09815b4/app/src/main/java/ani/saikou/parsers/anime/NineAnime.kt
// GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md
object NineAnimeHelper {
private const val nineAnimeKey =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
private const val cipherKey = "kMXzgyNzT3k5dYab"
fun encodeVrf(text: String, mainKey: String): String {
return encode(
encrypt(
cipher(mainKey, encode(text)),
nineAnimeKey
)//.replace("""=+$""".toRegex(), "")
)
}
fun decodeVrf(text: String, mainKey: String): String {
return decode(cipher(mainKey, decrypt(text, nineAnimeKey)))
}
fun encrypt(input: String, key: String): String {
if (input.any { it.code > 255 }) throw Exception("illegal characters!")
var output = ""
for (i in input.indices step 3) {
val a = intArrayOf(-1, -1, -1, -1)
a[0] = input[i].code shr 2
a[1] = (3 and input[i].code) shl 4
if (input.length > i + 1) {
a[1] = a[1] or (input[i + 1].code shr 4)
a[2] = (15 and input[i + 1].code) shl 2
}
if (input.length > i + 2) {
a[2] = a[2] or (input[i + 2].code shr 6)
a[3] = 63 and input[i + 2].code
}
for (n in a) {
if (n == -1) output += "="
else {
if (n in 0..63) output += key[n]
}
}
}
return output
}
fun cipher(key: String, text: String): String {
val arr = IntArray(256) { it }
var u = 0
var r: Int
arr.indices.forEach {
u = (u + arr[it] + key[it % key.length].code) % 256
r = arr[it]
arr[it] = arr[u]
arr[u] = r
}
u = 0
var c = 0
return text.indices.map { j ->
c = (c + 1) % 256
u = (u + arr[c]) % 256
r = arr[c]
arr[c] = arr[u]
arr[u] = r
(text[j].code xor arr[(arr[c] + arr[u]) % 256]).toChar()
}.joinToString("")
}
@Suppress("SameParameterValue")
private fun decrypt(input: String, key: String): String {
val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) {
input.replace("""==?$""".toRegex(), "")
} else input
if (t.length % 4 == 1 || t.contains("""[^+/0-9A-Za-z]""".toRegex())) throw Exception("bad input")
var i: Int
var r = ""
var e = 0
var u = 0
for (o in t.indices) {
e = e shl 6
i = key.indexOf(t[o])
e = e or i
u += 6
if (24 == u) {
r += ((16711680 and e) shr 16).toChar()
r += ((65280 and e) shr 8).toChar()
r += (255 and e).toChar()
e = 0
u = 0
}
}
return if (12 == u) {
e = e shr 4
r + e.toChar()
} else {
if (18 == u) {
e = e shr 2
r += ((65280 and e) shr 8).toChar()
r += (255 and e).toChar()
}
r
}
}
fun encode(input: String): String =
java.net.URLEncoder.encode(input, "utf-8").replace("+", "%20")
private fun decode(input: String): String = java.net.URLDecoder.decode(input, "utf-8")
}

View file

@ -0,0 +1,58 @@
package com.lagradost.cloudstream3.extractors.helper
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
class VstreamhubHelper {
companion object {
private val baseUrl: String = "https://vstreamhub.com"
private val baseName: String = "Vstreamhub"
suspend fun getUrls(
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
if (url.startsWith(baseUrl)) {
// Fetch links
val doc = app.get(url).document.select("script")
doc?.forEach {
val innerText = it?.toString()
if (!innerText.isNullOrEmpty()) {
if (innerText.contains("file:")) {
val startString = "file: "
val aa = innerText.substring(innerText.indexOf(startString))
val linkUrl =
aa.substring(startString.length + 1, aa.indexOf("\",")).trim()
//Log.i(baseName, "Result => (linkUrl) ${linkUrl}")
val exlink = ExtractorLink(
name = "$baseName m3u8",
source = baseName,
url = linkUrl,
quality = Qualities.Unknown.value,
referer = url,
isM3u8 = true
)
callback.invoke(exlink)
}
if (innerText.contains("playerInstance")) {
val aa =
innerText.substring(innerText.indexOf("playerInstance.addButton"))
val startString = "window.open(["
val bb = aa.substring(aa.indexOf(startString))
val datavid = bb.substring(startString.length, bb.indexOf("]"))
.removeSurrounding("\"")
if (datavid.isNotBlank()) {
loadExtractor(datavid, url, subtitleCallback, callback)
//Log.i(baseName, "Result => (datavid) ${datavid}")
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,56 @@
package com.lagradost.cloudstream3.extractors.helper
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.app
class WcoHelper {
companion object {
private const val BACKUP_KEY_DATA = "github_keys_backup"
data class ExternalKeys(
@JsonProperty("wco_key")
val wcoKey: String? = null,
@JsonProperty("wco_cipher_key")
val wcocipher: String? = null
)
data class NewExternalKeys(
@JsonProperty("cipherKey")
val cipherkey: String? = null,
@JsonProperty("encryptKey")
val encryptKey: String? = null,
@JsonProperty("mainKey")
val mainKey: String? = null,
)
private var keys: ExternalKeys? = null
private var newKeys: NewExternalKeys? = null
private suspend fun getKeys() {
keys = keys
?: app.get("https://raw.githubusercontent.com/reduplicated/Cloudstream/master/docs/keys.json")
.parsedSafe<ExternalKeys>()?.also { setKey(BACKUP_KEY_DATA, it) } ?: getKey(
BACKUP_KEY_DATA
)
}
suspend fun getWcoKey(): ExternalKeys? {
getKeys()
return keys
}
private suspend fun getNewKeys() {
newKeys = newKeys
?: app.get("https://raw.githubusercontent.com/chekaslowakiya/BruhFlow/main/keys.json")
.parsedSafe<NewExternalKeys>()?.also { setKey(BACKUP_KEY_DATA, it) } ?: getKey(
BACKUP_KEY_DATA
)
}
suspend fun getNewWcoKey(): NewExternalKeys? {
getNewKeys()
return newKeys
}
}
}

View file

@ -143,8 +143,9 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
/**
* Some languages do not use the normal country codes on OpenSubtitles
* */
private val languageExceptions = mapOf(
"pt" to "pt-PT"
private val languageExceptions = mapOf<String, String>(
// "pt" to "pt-PT",
// "pt" to "pt-BR"
)
private fun fixLanguage(language: String?) : String? {
return languageExceptions[language] ?: language

View file

@ -734,7 +734,7 @@ class GeneratorPlayer : FullScreenPlayer() {
if ((currentMeta as? ResultEpisode)?.tvType?.isLiveStream() == true) return
val (position, duration) = posDur
if(duration == 0L) return // idk how you achieved this, but div by zero crash
if (duration == 0L) return // idk how you achieved this, but div by zero crash
viewModel.getId()?.let {
DataStoreHelper.setViewPos(it, position, duration)
@ -1015,7 +1015,8 @@ class GeneratorPlayer : FullScreenPlayer() {
limitTitle = settingsManager.getInt(ctx.getString(R.string.prefer_limit_title_key), 0)
updateForcedEncoding(ctx)
filterSubByLang = settingsManager.getBoolean(getString(R.string.filter_sub_lang_key), false)
filterSubByLang =
settingsManager.getBoolean(getString(R.string.filter_sub_lang_key), false)
if (filterSubByLang) {
val langFromPrefMedia = settingsManager.getStringSet(
this.getString(R.string.provider_lang_key),

View file

@ -5,19 +5,15 @@ import android.os.Bundle
import android.view.View
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
import com.lagradost.cloudstream3.utils.SubtitleHelper
@ -100,25 +96,26 @@ class SettingsLang : PreferenceFragmentCompat() {
}
getPref(R.string.prefer_media_type_key)?.setOnPreferenceClickListener {
val prefNames = resources.getStringArray(R.array.media_type_pref)
val prefValues = resources.getIntArray(R.array.media_type_pref_values)
val names = enumValues<TvType>().sorted().map { it.name }
val default = enumValues<TvType>().sorted().filter { it != TvType.NSFW }.map { it.ordinal }
val defaultSet = default.map { it.toString() }.toSet()
val currentList = settingsManager.getStringSet(getString(R.string.prefer_media_type_key), defaultSet)?.map {
it.toInt()
} ?: default
val currentPrefMedia =
settingsManager.getInt(getString(R.string.prefer_media_type_key), 0)
activity?.showBottomDialog(
prefNames.toList(),
prefValues.indexOf(currentPrefMedia),
activity?.showMultiDialog(
names,
currentList,
getString(R.string.preferred_media_settings),
true,
{}) {
settingsManager.edit()
.putInt(getString(R.string.prefer_media_type_key), prefValues[it])
.apply()
{}) { selectedList ->
settingsManager.edit().putStringSet(
this.getString(R.string.prefer_media_type_key),
selectedList.map { it.toString() }.toMutableSet()
).apply()
removeKey(USER_SELECTED_HOMEPAGE_API)
// (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) }
//(context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) }
}
return@setOnPreferenceClickListener true
}

View file

@ -175,12 +175,12 @@ class PluginAdapter(
itemView.ext_version?.isVisible = true
itemView.ext_version?.text = "v${metadata.version}"
if (metadata.language != null) {
if (metadata.language.isNullOrBlank()) {
itemView.lang_icon?.isVisible = false
} else {
itemView.lang_icon?.isVisible = true
//itemView.lang_icon.text = getFlagFromIso(metadata.language)
itemView.lang_icon.text = fromTwoLettersToLanguage(metadata.language)
} else {
itemView.lang_icon?.isVisible = false
}

View file

@ -6,10 +6,12 @@ import android.view.View
import android.view.ViewGroup
import android.widget.AbsListView
import android.widget.ArrayAdapter
import androidx.core.util.forEach
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
@ -35,24 +37,34 @@ class SetupFragmentMedia : Fragment() {
val arrayAdapter =
ArrayAdapter<String>(this, R.layout.sort_bottom_single_choice)
val currentPrefMedia =
settingsManager.getInt(getString(R.string.prefer_media_type_key), 0)
val names = enumValues<TvType>().sorted().map { it.name }
val selected = mutableListOf<Int>()
val prefNames = resources.getStringArray(R.array.media_type_pref)
val prefValues = resources.getIntArray(R.array.media_type_pref_values)
arrayAdapter.addAll(names)
listview1?.let {
it.adapter = arrayAdapter
it.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE
arrayAdapter.addAll(prefNames.toList())
listview1?.adapter = arrayAdapter
listview1?.choiceMode = AbsListView.CHOICE_MODE_SINGLE
listview1?.setItemChecked(currentPrefMedia, true)
it.setOnItemClickListener { _, _, _, _ ->
it.checkedItemPositions?.forEach { key, value ->
if (value) {
selected.add(key)
} else {
selected.remove(key)
}
}
val prefValues = selected.mapNotNull { pos ->
val item = it.getItemAtPosition(pos)?.toString() ?: return@mapNotNull null
val itemVal = TvType.valueOf(item)
itemVal.ordinal.toString()
}.toSet()
settingsManager.edit()
.putStringSet(getString(R.string.prefer_media_type_key), prefValues)
.apply()
listview1?.setOnItemClickListener { _, _, position, _ ->
settingsManager.edit()
.putInt(getString(R.string.prefer_media_type_key), prefValues[position])
.apply()
// Regenerate set homepage
removeKey(USER_SELECTED_HOMEPAGE_API)
// Regenerate set homepage
removeKey(USER_SELECTED_HOMEPAGE_API)
}
}
next_btt?.setOnClickListener {

View file

@ -88,6 +88,58 @@ object BackupUtils {
@JsonProperty("settings") val settings: BackupVars
)
fun Context.getBackup(): BackupFile {
val allData = getSharedPrefs().all.filter { it.key.isTransferable() }
val allSettings = getDefaultSharedPrefs().all.filter { it.key.isTransferable() }
val allDataSorted = BackupVars(
allData.filter { it.value is Boolean } as? Map<String, Boolean>,
allData.filter { it.value is Int } as? Map<String, Int>,
allData.filter { it.value is String } as? Map<String, String>,
allData.filter { it.value is Float } as? Map<String, Float>,
allData.filter { it.value is Long } as? Map<String, Long>,
allData.filter { it.value as? Set<String> != null } as? Map<String, Set<String>>
)
val allSettingsSorted = BackupVars(
allSettings.filter { it.value is Boolean } as? Map<String, Boolean>,
allSettings.filter { it.value is Int } as? Map<String, Int>,
allSettings.filter { it.value is String } as? Map<String, String>,
allSettings.filter { it.value is Float } as? Map<String, Float>,
allSettings.filter { it.value is Long } as? Map<String, Long>,
allSettings.filter { it.value as? Set<String> != null } as? Map<String, Set<String>>
)
return BackupFile(
allDataSorted,
allSettingsSorted
)
}
fun Context.restore(
backupFile: BackupFile,
restoreSettings: Boolean,
restoreDataStore: Boolean
) {
if (restoreSettings) {
restoreMap(backupFile.settings._Bool, true)
restoreMap(backupFile.settings._Int, true)
restoreMap(backupFile.settings._String, true)
restoreMap(backupFile.settings._Float, true)
restoreMap(backupFile.settings._Long, true)
restoreMap(backupFile.settings._StringSet, true)
}
if (restoreDataStore) {
restoreMap(backupFile.datastore._Bool)
restoreMap(backupFile.datastore._Int)
restoreMap(backupFile.datastore._String)
restoreMap(backupFile.datastore._Float)
restoreMap(backupFile.datastore._Long)
restoreMap(backupFile.datastore._StringSet)
}
}
fun FragmentActivity.backup() {
try {
if (checkWrite()) {
@ -95,32 +147,7 @@ object BackupUtils {
val date = SimpleDateFormat("yyyy_MM_dd_HH_mm").format(Date(currentTimeMillis()))
val ext = "json"
val displayName = "CS3_Backup_${date}"
val allData = getSharedPrefs().all.filter { it.key.isTransferable() }
val allSettings = getDefaultSharedPrefs().all.filter { it.key.isTransferable() }
val allDataSorted = BackupVars(
allData.filter { it.value is Boolean } as? Map<String, Boolean>,
allData.filter { it.value is Int } as? Map<String, Int>,
allData.filter { it.value is String } as? Map<String, String>,
allData.filter { it.value is Float } as? Map<String, Float>,
allData.filter { it.value is Long } as? Map<String, Long>,
allData.filter { it.value as? Set<String> != null } as? Map<String, Set<String>>
)
val allSettingsSorted = BackupVars(
allSettings.filter { it.value is Boolean } as? Map<String, Boolean>,
allSettings.filter { it.value is Int } as? Map<String, Int>,
allSettings.filter { it.value is String } as? Map<String, String>,
allSettings.filter { it.value is Float } as? Map<String, Float>,
allSettings.filter { it.value is Long } as? Map<String, Long>,
allSettings.filter { it.value as? Set<String> != null } as? Map<String, Set<String>>
)
val backupFile = BackupFile(
allDataSorted,
allSettingsSorted
)
val backupFile = getBackup()
val steam =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && subDir?.isDownloadDir() == true) {
@ -251,28 +278,4 @@ object BackupUtils {
setKeyRaw(it.key, it.value, isEditingAppSettings)
}
}
fun Context.restore(
backupFile: BackupFile,
restoreSettings: Boolean,
restoreDataStore: Boolean
) {
if (restoreSettings) {
restoreMap(backupFile.settings._Bool, true)
restoreMap(backupFile.settings._Int, true)
restoreMap(backupFile.settings._String, true)
restoreMap(backupFile.settings._Float, true)
restoreMap(backupFile.settings._Long, true)
restoreMap(backupFile.settings._StringSet, true)
}
if (restoreDataStore) {
restoreMap(backupFile.datastore._Bool)
restoreMap(backupFile.datastore._Int)
restoreMap(backupFile.datastore._String)
restoreMap(backupFile.datastore._Float)
restoreMap(backupFile.datastore._Long)
restoreMap(backupFile.datastore._StringSet)
}
}
}

View file

@ -1,23 +0,0 @@
package com.lagradost.cloudstream3.utils
/*
import android.content.Context
import dalvik.system.PathClassLoader
import java.io.File
open class TestSource {
open fun doMath(): Int {
return 33
}
}
object ExtensionManager {
fun getSourceFromDex(context: Context, pkgName: String, file: File): TestSource? {
val loader = PathClassLoader(file.absolutePath, context.classLoader)
val obj = Class.forName(pkgName, false, loader).newInstance()
if (obj is TestSource) {
println("MATH : ${obj.doMath()}")
}
return null
}
}*/

View file

@ -6,6 +6,7 @@ import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.USER_AGENT
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.extractors.*
import kotlinx.coroutines.delay
import org.jsoup.Jsoup
import kotlin.collections.MutableList
@ -189,7 +190,138 @@ suspend fun loadExtractor(
return false
}
val extractorApis: MutableList<ExtractorApi> = arrayListOf()
val extractorApis: MutableList<ExtractorApi> = arrayListOf(
//AllProvider(),
WcoStream(),
Vidstreamz(),
Vizcloud(),
Vizcloud2(),
VizcloudOnline(),
VizcloudXyz(),
VizcloudLive(),
VizcloudInfo(),
MwvnVizcloudInfo(),
VizcloudDigital(),
VizcloudCloud(),
VizcloudSite(),
VideoVard(),
VideovardSX(),
Mp4Upload(),
StreamTape(),
//mixdrop extractors
MixDropBz(),
MixDropCh(),
MixDropTo(),
MixDrop(),
Mcloud(),
XStreamCdn(),
StreamSB(),
StreamSB1(),
StreamSB2(),
StreamSB3(),
StreamSB4(),
StreamSB5(),
StreamSB6(),
StreamSB7(),
StreamSB8(),
StreamSB9(),
StreamSB10(),
SBfull(),
// Streamhub(), cause Streamhub2() works
Streamhub2(),
Ssbstream(),
Fastream(),
FEmbed(),
FeHD(),
Fplayer(),
DBfilm(),
Luxubu(),
LayarKaca(),
// WatchSB(), 'cause StreamSB.kt works
Uqload(),
Uqload1(),
Evoload(),
Evoload1(),
VoeExtractor(),
// UpstreamExtractor(), GenericM3U8.kt works
Tomatomatela(),
Cinestart(),
OkRu(),
OkRuHttps(),
// dood extractors
DoodCxExtractor(),
DoodPmExtractor(),
DoodToExtractor(),
DoodSoExtractor(),
DoodLaExtractor(),
DoodWsExtractor(),
DoodShExtractor(),
DoodWatchExtractor(),
AsianLoad(),
// GenericM3U8(),
Jawcloud(),
Zplayer(),
ZplayerV2(),
Upstream(),
Maxstream(),
Tantifilm(),
Userload(),
Supervideo(),
GuardareStream(),
// StreamSB.kt works
// SBPlay(),
// SBPlay1(),
// SBPlay2(),
PlayerVoxzer(),
BullStream(),
GMPlayer(),
Blogger(),
Solidfiles(),
YourUpload(),
Hxfile(),
KotakAnimeid(),
Neonime8n(),
Neonime7n(),
Yufiles(),
Aico(),
JWPlayer(),
Meownime(),
DesuArcg(),
DesuOdchan(),
DesuOdvip(),
DesuDrive(),
Filesim(),
Linkbox(),
Acefile(),
SpeedoStream(),
YoutubeExtractor(),
YoutubeShortLinkExtractor(),
YoutubeMobileExtractor(),
YoutubeNoCookieExtractor(),
Streamlare(),
VidSrcExtractor(),
VidSrcExtractor2(),
)
fun getExtractorApiFromName(name: String): ExtractorApi {
for (api in extractorApis) {

View file

@ -71,7 +71,8 @@ object SubtitleHelper {
/** ISO_639_1 -> lang*/
fun fromTwoLettersToLanguage(input: String): String? {
if (input.length != 2) return null
// pr-BR
if (input.substringBefore("-").length != 2) return null
if (ISO_639_1Map.isEmpty()) {
initISO6391Map()
}
@ -269,6 +270,8 @@ object SubtitleHelper {
"pl" to "PL",
"ps" to "AF",
"pt" to "PT",
"pt-pt" to "PT",
"pt-br" to "BR",
"rm" to "CH",
"rn" to "BI",
"ro" to "RO",
@ -452,7 +455,9 @@ object SubtitleHelper {
Language639("Persian", "فارسی", "fa", "fas", "", "fas", ""),
Language639("Polish", "język polski, polszczyzna", "pl", "pol", "pol", "pol", "pols"),
Language639("Pashto", "پښتو", "ps", "pus", "pus", "pus", ""),
Language639("Portuguese", "português", "pt", "por", "por", "por", ""),
Language639("Portuguese", "português", "pt-pt", "por", "por", "por", ""),
// Addition to support Brazilian Portuguese properly, might break other things
Language639("Portuguese (Brazilian)", "português", "pt-br", "por", "por", "por", ""),
Language639("Quechua", "Runa Simi, Kichwa", "qu", "que", "que", "que", ""),
Language639("Romansh", "rumantsch grischun", "rm", "roh", "roh", "roh", ""),
Language639("Kirundi", "Ikirundi", "rn", "run", "run", "run", ""),

View file

@ -228,6 +228,7 @@
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal"
android:layoutDirection="ltr"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
@ -390,6 +391,7 @@
android:id="@+id/player_video_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutDirection="ltr"
android:orientation="horizontal">
<TextView
@ -532,6 +534,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layoutDirection="ltr"
android:orientation="horizontal">
<RelativeLayout
@ -591,14 +594,14 @@
tools:visibility="visible">
<ImageView
android:id="@+id/player_progressbar_right_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginBottom="220dp"
android:src="@drawable/ic_baseline_brightness_7_24"
app:tint="@android:color/white"
tools:ignore="ContentDescription">
android:id="@+id/player_progressbar_right_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginBottom="220dp"
android:src="@drawable/ic_baseline_brightness_7_24"
app:tint="@android:color/white"
tools:ignore="ContentDescription">
</ImageView>

View file

@ -226,6 +226,7 @@
android:id="@+id/player_video_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutDirection="ltr"
android:orientation="horizontal">
<ImageView

View file

@ -44,6 +44,7 @@
android:id="@+id/lang_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="5dp"
android:text="🇷🇼"
android:textColor="?attr/grayTextColor"
@ -55,7 +56,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:text="v1"
android:textColor="?attr/grayTextColor"
android:visibility="gone"
@ -66,7 +67,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:text="100MB"
android:textColor="?attr/grayTextColor"
android:visibility="gone"
@ -76,7 +77,6 @@
android:id="@+id/nsfw_marker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@string/is_adult"
android:textColor="@color/adultColor"
android:visibility="gone"

View file

@ -29,19 +29,6 @@
<item>4</item>
</array>
<array name="media_type_pref">
<item>All</item>
<item>Movies and TV</item>
<item>Anime</item>
<item>Documentary</item>
</array>
<array name="media_type_pref_values">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</array>
<array name="limit_title_rez_pref_names">
<item>@string/resolution_and_title</item>
<item>@string/title</item>