mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Merge branch 'recloudstream:master' into initial-multi-delete
This commit is contained in:
commit
b601704764
15 changed files with 484 additions and 316 deletions
|
@ -157,16 +157,16 @@ dependencies {
|
|||
testImplementation("junit:junit:4.13.2")
|
||||
testImplementation("org.json:json:20240303")
|
||||
androidTestImplementation("androidx.test:core")
|
||||
implementation("androidx.test.ext:junit-ktx:1.1.5")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
implementation("androidx.test.ext:junit-ktx:1.2.1")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.2.1")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
|
||||
|
||||
// Android Core & Lifecycle
|
||||
implementation("androidx.core:core-ktx:1.13.1")
|
||||
implementation("androidx.appcompat:appcompat:1.7.0")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.2")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.2")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.3")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
|
||||
|
||||
// Design & UI
|
||||
|
@ -182,9 +182,9 @@ dependencies {
|
|||
implementation("com.github.bumptech.glide:okhttp3-integration:4.16.0")
|
||||
|
||||
// For KSP -> Official Annotation Processors are Not Yet Supported for KSP
|
||||
ksp("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
|
||||
implementation("com.google.guava:guava:33.2.0-android")
|
||||
implementation("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
|
||||
ksp("dev.zacsweers.autoservice:auto-service-ksp:1.2.0")
|
||||
implementation("com.google.guava:guava:33.2.1-android")
|
||||
implementation("dev.zacsweers.autoservice:auto-service-ksp:1.2.0")
|
||||
|
||||
// Media 3 (ExoPlayer)
|
||||
implementation("androidx.media3:media3-ui:1.1.1")
|
||||
|
@ -200,9 +200,9 @@ dependencies {
|
|||
// PlayBack
|
||||
implementation("com.jaredrummler:colorpicker:1.1.0") // Subtitle Color Picker
|
||||
implementation("com.github.recloudstream:media-ffmpeg:1.1.0") // Custom FF-MPEG Lib for Audio Codecs
|
||||
implementation("com.github.teamnewpipe:NewPipeExtractor:fafd471") /* For Trailers
|
||||
implementation("com.github.teamnewpipe:NewPipeExtractor:592f159") /* For Trailers
|
||||
^ Update to Latest Commits if Trailers Misbehave, github.com/TeamNewPipe/NewPipeExtractor/commits/dev */
|
||||
implementation("com.github.albfernandez:juniversalchardet:2.4.0") // Subtitle Decoding
|
||||
implementation("com.github.albfernandez:juniversalchardet:2.5.0") // Subtitle Decoding
|
||||
|
||||
// Crash Reports (AcraApplication.kt)
|
||||
implementation("ch.acra:acra-core:5.11.3")
|
||||
|
@ -215,14 +215,14 @@ dependencies {
|
|||
implementation("com.github.discord:OverlappingPanels:0.1.5") // Gestures
|
||||
implementation ("androidx.biometric:biometric:1.2.0-alpha05") // Fingerprint Authentication
|
||||
implementation("com.github.rubensousa:previewseekbar-media3:1.1.1.0") // SeekBar Preview
|
||||
implementation("io.github.g0dkar:qrcode-kotlin:4.1.1") // QR code for PIN Auth on TV
|
||||
implementation("io.github.g0dkar:qrcode-kotlin:4.2.0") // QR code for PIN Auth on TV
|
||||
|
||||
// Extensions & Other Libs
|
||||
implementation("org.mozilla:rhino:1.7.15") // run JavaScript
|
||||
implementation("me.xdrop:fuzzywuzzy:1.4.0") // Library/Ext Searching with Levenshtein Distance
|
||||
implementation("com.github.LagradOst:SafeFile:0.0.6") // To Prevent the URI File Fu*kery
|
||||
implementation("org.conscrypt:conscrypt-android:2.5.2") // To Fix SSL Fu*kery on Android 9
|
||||
implementation("com.uwetrottmann.tmdb2:tmdb-java:2.10.0") // TMDB API v3 Wrapper Made with RetroFit
|
||||
implementation("com.uwetrottmann.tmdb2:tmdb-java:2.11.0") // TMDB API v3 Wrapper Made with RetroFit
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1") /* JSON Parser
|
||||
^ Don't Bump Jackson above 2.13.1 , Crashes on Android TV's and FireSticks that have Min API
|
||||
|
|
|
@ -1115,23 +1115,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
|
|||
|
||||
MainAPI.settingsForProvider = settingsForProvider
|
||||
|
||||
// Change library icon with logo of current api in sync
|
||||
libraryViewModel = ViewModelProvider(this)[LibraryViewModel::class.java]
|
||||
libraryViewModel?.currentApiName?.observe(this) {
|
||||
val syncAPI = libraryViewModel?.currentSyncApi
|
||||
Log.i("SYNC_API", "${syncAPI?.name}, ${syncAPI?.idPrefix}")
|
||||
val icon = if (syncAPI?.idPrefix == localListApi.idPrefix) {
|
||||
R.drawable.library_icon
|
||||
} else {
|
||||
syncAPI?.icon ?: R.drawable.library_icon
|
||||
}
|
||||
|
||||
binding?.apply {
|
||||
navRailView.menu.findItem(R.id.navigation_library)?.setIcon(icon)
|
||||
navView.menu.findItem(R.id.navigation_library)?.setIcon(icon)
|
||||
}
|
||||
}
|
||||
|
||||
loadThemes(this)
|
||||
updateLocale()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -1537,6 +1520,26 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
|
|||
logError(e)
|
||||
}
|
||||
}
|
||||
|
||||
// we need to run this after we init all apis, otherwise currentSyncApi will fuck itself
|
||||
this@MainActivity.runOnUiThread {
|
||||
// Change library icon with logo of current api in sync
|
||||
libraryViewModel = ViewModelProvider(this@MainActivity)[LibraryViewModel::class.java]
|
||||
libraryViewModel?.currentApiName?.observe(this@MainActivity) {
|
||||
val syncAPI = libraryViewModel?.currentSyncApi
|
||||
Log.i("SYNC_API", "${syncAPI?.name}, ${syncAPI?.idPrefix}")
|
||||
val icon = if (syncAPI?.idPrefix == localListApi.idPrefix) {
|
||||
R.drawable.library_icon
|
||||
} else {
|
||||
syncAPI?.icon ?: R.drawable.library_icon
|
||||
}
|
||||
|
||||
binding?.apply {
|
||||
navRailView.menu.findItem(R.id.navigation_library)?.setIcon(icon)
|
||||
navView.menu.findItem(R.id.navigation_library)?.setIcon(icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SearchResultBuilder.updateCache(this)
|
||||
|
|
|
@ -238,7 +238,7 @@ open class TraktProvider : MainAPI() {
|
|||
description = episode.overview,
|
||||
).apply {
|
||||
this.addDate(episode.firstAired, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
|
||||
if (nextAir == null && this.date != null && this.date!! > unixTimeMS) {
|
||||
if (nextAir == null && this.date != null && this.date!! > unixTimeMS && this.season != 0) {
|
||||
nextAir = NextAiring(
|
||||
episode = this.episode!!,
|
||||
unixTime = this.date!!.div(1000L),
|
||||
|
|
|
@ -22,6 +22,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
|
|||
val addic7ed = Addic7ed()
|
||||
val subDlApi = SubDlApi(0)
|
||||
val localListApi = LocalList()
|
||||
val subSourceApi = SubSourceApi()
|
||||
|
||||
// used to login via app intent
|
||||
val OAuth2Apis
|
||||
|
@ -51,7 +52,8 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
|
|||
get() = listOf(
|
||||
openSubtitlesApi,
|
||||
addic7ed,
|
||||
subDlApi
|
||||
subDlApi,
|
||||
subSourceApi
|
||||
)
|
||||
|
||||
const val appString = "cloudstreamapp"
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
package com.lagradost.cloudstream3.syncproviders.providers
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.TvType
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.subtitles.AbstractSubProvider
|
||||
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
|
||||
import com.lagradost.cloudstream3.subtitles.SubtitleResource
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||
|
||||
class SubSourceApi : AbstractSubProvider {
|
||||
override val idPrefix = "subsource"
|
||||
val name = "SubSource"
|
||||
|
||||
companion object {
|
||||
const val APIURL = "https://api.subsource.net/api"
|
||||
const val DOWNLOADENDPOINT = "https://api.subsource.net/api/downloadSub"
|
||||
}
|
||||
|
||||
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity>? {
|
||||
|
||||
//Only supports Imdb Id search for now
|
||||
if (query.imdbId == null) return null
|
||||
val queryLang = SubtitleHelper.fromTwoLettersToLanguage(query.lang!!)
|
||||
val type = if ((query.seasonNumber ?: 0) > 0) TvType.TvSeries else TvType.Movie
|
||||
|
||||
val searchRes = app.post(
|
||||
url = "$APIURL/searchMovie",
|
||||
data = mapOf(
|
||||
"query" to query.imdbId!!
|
||||
)
|
||||
).parsedSafe<ApiSearch>() ?: return null
|
||||
|
||||
val postData = if (type == TvType.TvSeries) {
|
||||
mapOf(
|
||||
"langs" to "[]",
|
||||
"movieName" to searchRes.found.first().linkName,
|
||||
"season" to "season-${query.seasonNumber}"
|
||||
)
|
||||
} else {
|
||||
mapOf(
|
||||
"langs" to "[]",
|
||||
"movieName" to searchRes.found.first().linkName,
|
||||
)
|
||||
}
|
||||
|
||||
val getMovieRes = app.post(
|
||||
url = "$APIURL/getMovie",
|
||||
data = postData
|
||||
).parsedSafe<ApiResponse>().let {
|
||||
// api doesn't has episode number or lang filtering
|
||||
if (type == TvType.Movie) {
|
||||
it?.subs?.filter { sub ->
|
||||
sub.lang == queryLang
|
||||
}
|
||||
} else {
|
||||
it?.subs?.filter { sub ->
|
||||
sub.releaseName!!.contains(
|
||||
String.format(
|
||||
"E%02d",
|
||||
query.epNumber
|
||||
)
|
||||
) && sub.lang == queryLang
|
||||
}
|
||||
}
|
||||
} ?: return null
|
||||
|
||||
return getMovieRes.map { subtitle ->
|
||||
AbstractSubtitleEntities.SubtitleEntity(
|
||||
idPrefix = this.idPrefix,
|
||||
name = subtitle.releaseName!!,
|
||||
lang = subtitle.lang!!,
|
||||
data = SubData(
|
||||
movie = subtitle.linkName!!,
|
||||
lang = subtitle.lang,
|
||||
id = subtitle.subId.toString(),
|
||||
).toJson(),
|
||||
type = type,
|
||||
source = this.name,
|
||||
epNumber = query.epNumber,
|
||||
seasonNumber = query.seasonNumber,
|
||||
isHearingImpaired = subtitle.hi == 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun SubtitleResource.getResources(data: AbstractSubtitleEntities.SubtitleEntity) {
|
||||
|
||||
val parsedSub = parseJson<SubData>(data.data)
|
||||
|
||||
val subRes = app.post(
|
||||
url = "$APIURL/getSub",
|
||||
data = mapOf(
|
||||
"movie" to parsedSub.movie,
|
||||
"lang" to data.lang,
|
||||
"id" to parsedSub.id
|
||||
)
|
||||
).parsedSafe<SubTitleLink>() ?: return
|
||||
|
||||
this.addZipUrl(
|
||||
"$DOWNLOADENDPOINT/${subRes.sub.downloadToken}"
|
||||
) { name, _ ->
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
data class ApiSearch(
|
||||
@JsonProperty("success") val success: Boolean,
|
||||
@JsonProperty("found") val found: List<Found>,
|
||||
)
|
||||
|
||||
data class Found(
|
||||
@JsonProperty("id") val id: Long,
|
||||
@JsonProperty("title") val title: String,
|
||||
@JsonProperty("seasons") val seasons: Long,
|
||||
@JsonProperty("type") val type: String,
|
||||
@JsonProperty("releaseYear") val releaseYear: Long,
|
||||
@JsonProperty("linkName") val linkName: String,
|
||||
)
|
||||
|
||||
data class ApiResponse(
|
||||
@JsonProperty("success") val success: Boolean,
|
||||
@JsonProperty("movie") val movie: Movie,
|
||||
@JsonProperty("subs") val subs: List<Sub>,
|
||||
)
|
||||
|
||||
data class Movie(
|
||||
@JsonProperty("id") val id: Long? = null,
|
||||
@JsonProperty("type") val type: String? = null,
|
||||
@JsonProperty("year") val year: Long? = null,
|
||||
@JsonProperty("fullName") val fullName: String? = null,
|
||||
)
|
||||
|
||||
data class Sub(
|
||||
@JsonProperty("hi") val hi: Int? = null,
|
||||
@JsonProperty("fullLink") val fullLink: String? = null,
|
||||
@JsonProperty("linkName") val linkName: String? = null,
|
||||
@JsonProperty("lang") val lang: String? = null,
|
||||
@JsonProperty("releaseName") val releaseName: String? = null,
|
||||
@JsonProperty("subId") val subId: Long? = null,
|
||||
)
|
||||
|
||||
data class SubData(
|
||||
@JsonProperty("movie") val movie: String,
|
||||
@JsonProperty("lang") val lang: String,
|
||||
@JsonProperty("id") val id: String,
|
||||
)
|
||||
|
||||
data class SubTitleLink(
|
||||
@JsonProperty("sub") val sub: SubToken,
|
||||
)
|
||||
|
||||
data class SubToken(
|
||||
@JsonProperty("downloadToken") val downloadToken: String,
|
||||
)
|
||||
}
|
|
@ -57,8 +57,7 @@ const val ACTION_PLAY_EPISODE_IN_MPV = 17
|
|||
const val ACTION_MARK_AS_WATCHED = 18
|
||||
const val ACTION_FCAST = 19
|
||||
|
||||
const val TV_EP_SIZE_LARGE = 400
|
||||
const val TV_EP_SIZE_SMALL = 300
|
||||
const val TV_EP_SIZE = 400
|
||||
data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
|
||||
|
||||
class EpisodeAdapter(
|
||||
|
@ -181,7 +180,7 @@ class EpisodeAdapter(
|
|||
fun bind(card: ResultEpisode) {
|
||||
localCard = card
|
||||
val setWidth =
|
||||
if (isLayout(TV or EMULATOR)) TV_EP_SIZE_LARGE.toPx else ViewGroup.LayoutParams.MATCH_PARENT
|
||||
if (isLayout(TV or EMULATOR)) TV_EP_SIZE.toPx else ViewGroup.LayoutParams.MATCH_PARENT
|
||||
|
||||
binding.episodeLinHolder.layoutParams.width = setWidth
|
||||
binding.episodeHolderLarge.layoutParams.width = setWidth
|
||||
|
@ -336,7 +335,7 @@ class EpisodeAdapter(
|
|||
fun bind(card: ResultEpisode) {
|
||||
binding.episodeHolder.layoutParams.apply {
|
||||
width =
|
||||
if (isLayout(TV or EMULATOR)) TV_EP_SIZE_SMALL.toPx else ViewGroup.LayoutParams.MATCH_PARENT
|
||||
if (isLayout(TV or EMULATOR)) TV_EP_SIZE.toPx else ViewGroup.LayoutParams.MATCH_PARENT
|
||||
}
|
||||
|
||||
binding.apply {
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/add_site"
|
||||
android:text="@string/add_site_pref"
|
||||
style="@style/SettingsItem">
|
||||
android:id="@+id/add_site"
|
||||
android:text="@string/add_site_pref"
|
||||
android:focusable="true"
|
||||
style="@style/SettingsItem">
|
||||
|
||||
<requestFocus />
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/remove_site"
|
||||
android:text="@string/remove_site_pref"
|
||||
style="@style/SettingsItem" />
|
||||
android:id="@+id/remove_site"
|
||||
android:text="@string/remove_site_pref"
|
||||
android:focusable="true"
|
||||
style="@style/SettingsItem" />
|
||||
</LinearLayout>
|
|
@ -90,14 +90,15 @@
|
|||
|
||||
android:textColor="?attr/textColor"
|
||||
tools:text="Episode 1" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.lagradost.cloudstream3.ui.download.button.PieFetchButton
|
||||
android:id="@+id/download_button"
|
||||
android:layout_width="@dimen/download_size"
|
||||
android:layout_height="@dimen/download_size"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginStart="-50dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:padding="10dp" />
|
||||
<com.lagradost.cloudstream3.ui.download.button.PieFetchButton
|
||||
android:id="@+id/download_button"
|
||||
android:layout_width="@dimen/download_size"
|
||||
android:layout_height="@dimen/download_size"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginStart="-60dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:padding="10dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
|
@ -1,306 +1,221 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/result_sync_holder"
|
||||
tools:visibility="visible"
|
||||
android:visibility="gone"
|
||||
android:padding="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:id="@+id/result_sync_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_sync_names"
|
||||
android:textStyle="bold"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="MyAnimeList, AniList"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:id="@+id/result_sync_names"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="MyAnimeList, AniList"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:visibility="visible"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/result_sync_sub_episode"
|
||||
android:padding="10dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/baseline_remove_24"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
app:tint="?attr/textColor" />
|
||||
android:id="@+id/result_sync_sub_episode"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/baseline_remove_24"
|
||||
app:tint="?attr/textColor" />
|
||||
|
||||
<EditText
|
||||
android:textColorHint="?attr/grayTextColor"
|
||||
android:id="@+id/result_sync_current_episodes"
|
||||
style="@style/AppEditStyle"
|
||||
tools:hint="20"
|
||||
android:textSize="20sp"
|
||||
android:inputType="number"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="LabelFor" />
|
||||
android:id="@+id/result_sync_current_episodes"
|
||||
style="@style/AppEditStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="number"
|
||||
android:textColorHint="?attr/grayTextColor"
|
||||
android:textSize="20sp"
|
||||
tools:hint="20"
|
||||
tools:ignore="LabelFor" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_sync_max_episodes"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingBottom="1dp"
|
||||
android:textSize="20sp"
|
||||
android:textColor="?attr/textColor"
|
||||
tools:text="30"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:id="@+id/result_sync_max_episodes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingBottom="1dp"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="20sp"
|
||||
tools:text="30" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/result_sync_add_episode"
|
||||
android:padding="10dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/ic_baseline_add_24"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
app:tint="?attr/textColor" />
|
||||
android:id="@+id/result_sync_add_episode"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_baseline_add_24"
|
||||
app:tint="?attr/textColor" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.core.widget.ContentLoadingProgressBar
|
||||
android:id="@+id/result_sync_episodes"
|
||||
android:padding="10dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:progress="0"
|
||||
android:indeterminate="false"
|
||||
android:progressBackgroundTint="?attr/colorPrimary"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:max="100"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<!--
|
||||
<LinearLayout
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="10dp"
|
||||
android:textSize="17sp"
|
||||
android:textColor="?attr/textColor"
|
||||
android:text="Status:"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:layout_height="30dp"
|
||||
android:text="Watching"
|
||||
android:minWidth="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="0dp"
|
||||
style="@style/BlackButton" />
|
||||
</LinearLayout>
|
||||
|
||||
<GridLayout
|
||||
android:orientation="horizontal"
|
||||
android:columnCount="2"
|
||||
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sync_completed"
|
||||
android:nextFocusRight="@id/sync_on_hold"
|
||||
android:nextFocusDown="@id/sync_plan_to_watch"
|
||||
|
||||
android:layout_row="0"
|
||||
android:layout_column="0"
|
||||
android:text="@string/type_completed"
|
||||
style="@style/SyncButton" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sync_on_hold"
|
||||
android:nextFocusDown="@id/sync_watching"
|
||||
android:nextFocusLeft="@id/sync_completed"
|
||||
|
||||
android:layout_row="0"
|
||||
android:layout_column="1"
|
||||
style="@style/SyncButton"
|
||||
android:text="@string/type_on_hold" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sync_plan_to_watch"
|
||||
android:nextFocusRight="@id/sync_plan_to_watch"
|
||||
android:nextFocusDown="@id/sync_dropped"
|
||||
android:nextFocusUp="@id/sync_completed"
|
||||
|
||||
android:layout_row="1"
|
||||
android:layout_column="0"
|
||||
android:text="@string/type_plan_to_watch"
|
||||
style="@style/SyncButton"
|
||||
/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sync_watching"
|
||||
android:nextFocusLeft="@id/sync_plan_to_watch"
|
||||
android:nextFocusDown="@id/sync_dropped"
|
||||
android:nextFocusUp="@id/sync_on_hold"
|
||||
|
||||
android:layout_row="1"
|
||||
android:layout_column="1"
|
||||
|
||||
style="@style/SyncButton"
|
||||
android:text="@string/type_watching" />
|
||||
|
||||
</GridLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/sync_dropped"
|
||||
android:nextFocusUp="@id/sync_plan_to_watch"
|
||||
|
||||
android:layout_width="match_parent"
|
||||
style="@style/SyncButton"
|
||||
|
||||
android:text="@string/type_dropped" />-->
|
||||
android:id="@+id/result_sync_episodes"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:indeterminate="false"
|
||||
android:max="100"
|
||||
android:padding="10dp"
|
||||
android:progress="0"
|
||||
android:progressBackgroundTint="?attr/colorPrimary"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="10dp"
|
||||
android:textSize="17sp"
|
||||
android:textColor="?attr/textColor"
|
||||
android:text="@string/sync_score"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="10dp"
|
||||
android:text="@string/sync_score"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="17sp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_sync_score_text"
|
||||
android:id="@+id/result_sync_score_text"
|
||||
|
||||
android:layout_height="30dp"
|
||||
android:text="7/10"
|
||||
android:minWidth="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="0dp"
|
||||
style="@style/BlackButton" />
|
||||
style="@style/BlackButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="0dp"
|
||||
android:minWidth="0dp"
|
||||
android:text="7/10" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/result_sync_rating"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="10"
|
||||
android:value="4"
|
||||
android:stepSize="1"
|
||||
app:tickVisible="false"
|
||||
android:layout_marginStart="-5dp"
|
||||
android:layout_marginEnd="-5dp"
|
||||
app:thumbRadius="10dp"
|
||||
app:labelStyle="@style/BlackLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:id="@+id/result_sync_rating"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="-5dp"
|
||||
android:layout_marginEnd="-5dp"
|
||||
app:thumbHeight="20dp"
|
||||
android:stepSize="1"
|
||||
android:value="4"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="10"
|
||||
app:labelStyle="@style/BlackLabel"
|
||||
app:thumbRadius="10dp"
|
||||
app:tickVisible="false" />
|
||||
|
||||
<FrameLayout
|
||||
android:visibility="gone"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/home_parent_item_title"
|
||||
style="@style/WatchHeaderText"
|
||||
tools:text="Recommended" />
|
||||
android:id="@+id/home_parent_item_title"
|
||||
style="@style/WatchHeaderText"
|
||||
tools:text="Recommended" />
|
||||
|
||||
<ImageView
|
||||
app:tint="?attr/textColor"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:src="@drawable/ic_baseline_arrow_forward_24"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/home_more_info" />
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:contentDescription="@string/home_more_info"
|
||||
android:src="@drawable/ic_baseline_arrow_forward_24"
|
||||
app:tint="?attr/textColor" />
|
||||
</FrameLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/result_sync_check"
|
||||
tools:listitem="@layout/sort_bottom_single_choice"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_rowWeight="1" />
|
||||
android:id="@+id/result_sync_check"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_rowWeight="1"
|
||||
tools:listitem="@layout/sort_bottom_single_choice" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_width="match_parent"
|
||||
style="@style/WhiteButton"
|
||||
android:text="@string/type_watching" />
|
||||
style="@style/WhiteButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/type_watching"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/result_sync_set_score"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_width="match_parent"
|
||||
style="@style/BlackButton"
|
||||
app:icon="@drawable/baseline_sync_24"
|
||||
android:text="@string/upload_sync" />
|
||||
android:id="@+id/result_sync_set_score"
|
||||
style="@style/BlackButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/upload_sync"
|
||||
app:icon="@drawable/baseline_sync_24" />
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
tools:visibility="gone"
|
||||
android:id="@+id/result_sync_loading_shimmer"
|
||||
app:shimmer_base_alpha="0.2"
|
||||
app:shimmer_highlight_alpha="0.3"
|
||||
app:shimmer_duration="@integer/loading_time"
|
||||
app:shimmer_auto_start="true"
|
||||
android:padding="15dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical">
|
||||
android:id="@+id/result_sync_loading_shimmer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp"
|
||||
app:shimmer_auto_start="true"
|
||||
app:shimmer_base_alpha="0.2"
|
||||
app:shimmer_duration="@integer/loading_time"
|
||||
app:shimmer_highlight_alpha="0.3"
|
||||
tools:visibility="gone"
|
||||
tools:ignore="MissingClass">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp" />
|
||||
android:layout_height="30dp" />
|
||||
|
||||
<include layout="@layout/loading_line_short" />
|
||||
|
||||
<include layout="@layout/loading_line" />
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp" />
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp" />
|
||||
|
||||
<include layout="@layout/loading_line_short" />
|
||||
|
||||
<include layout="@layout/loading_line" />
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp" />
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp" />
|
||||
|
||||
<include layout="@layout/loading_line_short" />
|
||||
|
||||
|
@ -313,8 +228,8 @@
|
|||
<include layout="@layout/loading_line_short" />
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp" />
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="30dp" />
|
||||
|
||||
<include layout="@layout/loading_line" />
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.lagradost.cloudstream3.extractors.helper.AesHelper.cryptoAESHandler
|
|||
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||
import kotlin.run
|
||||
|
||||
class Moviesapi : Chillx() {
|
||||
override val name = "Moviesapi"
|
||||
|
@ -28,17 +29,22 @@ open class Chillx : ExtractorApi() {
|
|||
override val requiresReferer = true
|
||||
|
||||
companion object {
|
||||
private val keySource = "https://rowdy-avocado.github.io/multi-keys/"
|
||||
|
||||
private var key: String? = null
|
||||
|
||||
suspend fun fetchKey(): String {
|
||||
return if (key != null) {
|
||||
key!!
|
||||
} else {
|
||||
val fetch = app.get("https://raw.githubusercontent.com/rushi-chavan/multi-keys/keys/keys.json").parsedSafe<Keys>()?.key?.get(0) ?: throw ErrorLoadingException("Unable to get key")
|
||||
key = fetch
|
||||
key!!
|
||||
}
|
||||
private suspend fun fetchKey(): String {
|
||||
return key
|
||||
?: run {
|
||||
val res =
|
||||
app.get(keySource).parsedSafe<KeysData>()
|
||||
?: throw ErrorLoadingException("Unable to get keys")
|
||||
key = res.keys.get(0)
|
||||
res.keys.get(0)
|
||||
}
|
||||
}
|
||||
|
||||
private data class KeysData(@JsonProperty("chillx") val keys: List<String>)
|
||||
}
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
|
@ -97,11 +103,4 @@ open class Chillx : ExtractorApi() {
|
|||
it.groupValues[1].toInt(16).toChar().toString()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
data class Keys(
|
||||
@JsonProperty("chillx") val key: List<String>
|
||||
)
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,11 @@ class FourPlayRu : ContentX() {
|
|||
override var mainUrl = "https://four.playru.net"
|
||||
}
|
||||
|
||||
class Pichive : ContentX() {
|
||||
override var name = "Pichive"
|
||||
override var mainUrl = "https://pichive.online"
|
||||
}
|
||||
|
||||
class FourPichive : ContentX() {
|
||||
override var name = "FourPichive"
|
||||
override var mainUrl = "https://four.pichive.online"
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
||||
|
||||
package com.lagradost.cloudstream3.extractors
|
||||
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
|
||||
open class Sobreatsesuyp : ExtractorApi() {
|
||||
override val name = "Sobreatsesuyp"
|
||||
override val mainUrl = "https://sobreatsesuyp.com"
|
||||
override val requiresReferer = true
|
||||
|
||||
override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) {
|
||||
val extRef = referer ?: ""
|
||||
|
||||
val videoReq = app.get(url, referer = extRef).text
|
||||
|
||||
val file = Regex("""file\":\"([^\"]+)""").find(videoReq)?.groupValues?.get(1) ?: throw ErrorLoadingException("File not found")
|
||||
val postLink = "${mainUrl}/" + file.replace("\\", "")
|
||||
val rawList = app.post(postLink, referer = extRef).parsedSafe<List<Any>>() ?: throw ErrorLoadingException("Post link not found")
|
||||
|
||||
val postJson: List<SobreatsesuypVideoData> = rawList.drop(1).map { item ->
|
||||
val mapItem = item as Map<*, *>
|
||||
SobreatsesuypVideoData(
|
||||
title = mapItem["title"] as? String,
|
||||
file = mapItem["file"] as? String
|
||||
)
|
||||
}
|
||||
|
||||
for (item in postJson) {
|
||||
if (item.file == null || item.title == null) continue
|
||||
|
||||
val videoData = app.post("${mainUrl}/playlist/${item.file.substring(1)}.txt", referer = extRef).text
|
||||
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
source = this.name,
|
||||
name = "${this.name} - ${item.title}",
|
||||
url = videoData,
|
||||
referer = extRef,
|
||||
quality = Qualities.Unknown.value,
|
||||
type = INFER_TYPE
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class SobreatsesuypVideoData(
|
||||
@JsonProperty("title") val title: String? = null,
|
||||
@JsonProperty("file") val file: String? = null
|
||||
)
|
||||
}
|
|
@ -26,7 +26,13 @@ class VidSrcTo : ExtractorApi() {
|
|||
callback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
val mediaId = app.get(url).document.selectFirst("ul.episodes li a")?.attr("data-id") ?: return
|
||||
val res = app.get("$mainUrl/ajax/embed/episode/$mediaId/sources").parsedSafe<VidsrctoEpisodeSources>() ?: return
|
||||
val subtitlesLink = "$mainUrl/ajax/embed/episode/$mediaId/subtitles"
|
||||
val subRes = app.get(subtitlesLink).parsedSafe<Array<VidsrctoSubtitles>>()
|
||||
subRes?.forEach {
|
||||
if (it.kind.equals("captions")) subtitleCallback.invoke(SubtitleFile(it.label, it.file))
|
||||
}
|
||||
val sourcesLink = "$mainUrl/ajax/embed/episode/$mediaId/sources"
|
||||
val res = app.get(sourcesLink).parsedSafe<VidsrctoEpisodeSources>() ?: return
|
||||
if (res.status != 200) return
|
||||
res.result?.amap { source ->
|
||||
try {
|
||||
|
@ -68,5 +74,11 @@ class VidSrcTo : ExtractorApi() {
|
|||
@JsonProperty("result") val result: VidsrctoUrl
|
||||
)
|
||||
|
||||
data class VidsrctoSubtitles(
|
||||
@JsonProperty("file") val file: String,
|
||||
@JsonProperty("label") val label: String,
|
||||
@JsonProperty("kind") val kind: String
|
||||
)
|
||||
|
||||
data class VidsrctoUrl(@JsonProperty("url") val encUrl: String)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.lagradost.cloudstream3.extractors
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.ErrorLoadingException
|
||||
import com.lagradost.cloudstream3.SubtitleFile
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.base64Encode
|
||||
|
@ -9,6 +10,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
|
|||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import kotlin.run
|
||||
|
||||
// Code found in https://github.com/KillerDogeEmpire/vidplay-keys
|
||||
// special credits to @KillerDogeEmpire for providing key
|
||||
|
@ -35,8 +37,25 @@ open class Vidplay : ExtractorApi() {
|
|||
override val name = "Vidplay"
|
||||
override val mainUrl = "https://vidplay.site"
|
||||
override val requiresReferer = true
|
||||
open val key =
|
||||
"https://raw.githubusercontent.com/KillerDogeEmpire/vidplay-keys/keys/keys.json"
|
||||
|
||||
companion object {
|
||||
private val keySource = "https://rowdy-avocado.github.io/multi-keys/"
|
||||
|
||||
private var keys: List<String>? = null
|
||||
|
||||
private suspend fun getKeys(): List<String> {
|
||||
return keys
|
||||
?: run {
|
||||
val res =
|
||||
app.get(keySource).parsedSafe<KeysData>()
|
||||
?: throw ErrorLoadingException("Unable to get keys")
|
||||
keys = res.keys
|
||||
res.keys
|
||||
}
|
||||
}
|
||||
|
||||
private data class KeysData(@JsonProperty("vidplay") val keys: List<String>)
|
||||
}
|
||||
|
||||
override suspend fun getUrl(
|
||||
url: String,
|
||||
|
@ -70,10 +89,6 @@ open class Vidplay : ExtractorApi() {
|
|||
|
||||
}
|
||||
|
||||
private suspend fun getKeys(): List<String> {
|
||||
return app.get(key).parsed()
|
||||
}
|
||||
|
||||
private suspend fun callFutoken(id: String, url: String): String? {
|
||||
val script = app.get("$mainUrl/futoken", referer = url).text
|
||||
val k = "k='(\\S+)'".toRegex().find(script)?.groupValues?.get(1) ?: return null
|
||||
|
|
|
@ -115,6 +115,7 @@ import com.lagradost.cloudstream3.extractors.Hotlinger
|
|||
import com.lagradost.cloudstream3.extractors.FourCX
|
||||
import com.lagradost.cloudstream3.extractors.PlayRu
|
||||
import com.lagradost.cloudstream3.extractors.FourPlayRu
|
||||
import com.lagradost.cloudstream3.extractors.Pichive
|
||||
import com.lagradost.cloudstream3.extractors.FourPichive
|
||||
import com.lagradost.cloudstream3.extractors.HDMomPlayer
|
||||
import com.lagradost.cloudstream3.extractors.HDPlayerSystem
|
||||
|
@ -124,6 +125,7 @@ import com.lagradost.cloudstream3.extractors.HDStreamAble
|
|||
import com.lagradost.cloudstream3.extractors.RapidVid
|
||||
import com.lagradost.cloudstream3.extractors.TRsTX
|
||||
import com.lagradost.cloudstream3.extractors.VidMoxy
|
||||
import com.lagradost.cloudstream3.extractors.Sobreatsesuyp
|
||||
import com.lagradost.cloudstream3.extractors.PixelDrain
|
||||
import com.lagradost.cloudstream3.extractors.MailRu
|
||||
import com.lagradost.cloudstream3.extractors.Mediafire
|
||||
|
@ -734,6 +736,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
|
|||
FourCX(),
|
||||
PlayRu(),
|
||||
FourPlayRu(),
|
||||
Pichive(),
|
||||
FourPichive(),
|
||||
HDMomPlayer(),
|
||||
HDPlayerSystem(),
|
||||
|
@ -743,6 +746,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
|
|||
RapidVid(),
|
||||
TRsTX(),
|
||||
VidMoxy(),
|
||||
Sobreatsesuyp(),
|
||||
PixelDrain(),
|
||||
MailRu(),
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue