forked from recloudstream/cloudstream
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
10f4d33c59
70 changed files with 3433 additions and 268 deletions
15
.github/ISSUE_TEMPLATE/application-bug.yml
vendored
15
.github/ISSUE_TEMPLATE/application-bug.yml
vendored
|
@ -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
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal 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.
|
84
.github/ISSUE_TEMPLATE/provider-bug.yml
vendored
84
.github/ISSUE_TEMPLATE/provider-bug.yml
vendored
|
@ -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
63
.github/workflows/issue-action.yml
vendored
Normal 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'
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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")*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
|
@ -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 "*/*")
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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?
|
||||
)
|
||||
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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?
|
||||
)
|
||||
|
||||
}
|
|
@ -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?
|
||||
)
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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?,
|
||||
)
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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"))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}*/
|
|
@ -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) {
|
||||
|
|
|
@ -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", ""),
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue