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("junit:junit:4.13.2")
|
||||||
testImplementation("org.json:json:20240303")
|
testImplementation("org.json:json:20240303")
|
||||||
androidTestImplementation("androidx.test:core")
|
androidTestImplementation("androidx.test:core")
|
||||||
implementation("androidx.test.ext:junit-ktx:1.1.5")
|
implementation("androidx.test.ext:junit-ktx:1.2.1")
|
||||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
androidTestImplementation("androidx.test.ext:junit:1.2.1")
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
|
||||||
|
|
||||||
// Android Core & Lifecycle
|
// Android Core & Lifecycle
|
||||||
implementation("androidx.core:core-ktx:1.13.1")
|
implementation("androidx.core:core-ktx:1.13.1")
|
||||||
implementation("androidx.appcompat:appcompat:1.7.0")
|
implementation("androidx.appcompat:appcompat:1.7.0")
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
|
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
|
||||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.2")
|
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.3")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.2")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3")
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
|
||||||
|
|
||||||
// Design & UI
|
// Design & UI
|
||||||
|
@ -182,9 +182,9 @@ dependencies {
|
||||||
implementation("com.github.bumptech.glide:okhttp3-integration:4.16.0")
|
implementation("com.github.bumptech.glide:okhttp3-integration:4.16.0")
|
||||||
|
|
||||||
// For KSP -> Official Annotation Processors are Not Yet Supported for KSP
|
// For KSP -> Official Annotation Processors are Not Yet Supported for KSP
|
||||||
ksp("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.0-android")
|
implementation("com.google.guava:guava:33.2.1-android")
|
||||||
implementation("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
|
implementation("dev.zacsweers.autoservice:auto-service-ksp:1.2.0")
|
||||||
|
|
||||||
// Media 3 (ExoPlayer)
|
// Media 3 (ExoPlayer)
|
||||||
implementation("androidx.media3:media3-ui:1.1.1")
|
implementation("androidx.media3:media3-ui:1.1.1")
|
||||||
|
@ -200,9 +200,9 @@ dependencies {
|
||||||
// PlayBack
|
// PlayBack
|
||||||
implementation("com.jaredrummler:colorpicker:1.1.0") // Subtitle Color Picker
|
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.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 */
|
^ 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)
|
// Crash Reports (AcraApplication.kt)
|
||||||
implementation("ch.acra:acra-core:5.11.3")
|
implementation("ch.acra:acra-core:5.11.3")
|
||||||
|
@ -215,14 +215,14 @@ dependencies {
|
||||||
implementation("com.github.discord:OverlappingPanels:0.1.5") // Gestures
|
implementation("com.github.discord:OverlappingPanels:0.1.5") // Gestures
|
||||||
implementation ("androidx.biometric:biometric:1.2.0-alpha05") // Fingerprint Authentication
|
implementation ("androidx.biometric:biometric:1.2.0-alpha05") // Fingerprint Authentication
|
||||||
implementation("com.github.rubensousa:previewseekbar-media3:1.1.1.0") // SeekBar Preview
|
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
|
// Extensions & Other Libs
|
||||||
implementation("org.mozilla:rhino:1.7.15") // run JavaScript
|
implementation("org.mozilla:rhino:1.7.15") // run JavaScript
|
||||||
implementation("me.xdrop:fuzzywuzzy:1.4.0") // Library/Ext Searching with Levenshtein Distance
|
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("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("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")
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
|
||||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1") /* JSON Parser
|
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
|
^ 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
|
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)
|
loadThemes(this)
|
||||||
updateLocale()
|
updateLocale()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -1537,6 +1520,26 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
|
||||||
logError(e)
|
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)
|
SearchResultBuilder.updateCache(this)
|
||||||
|
|
|
@ -238,7 +238,7 @@ open class TraktProvider : MainAPI() {
|
||||||
description = episode.overview,
|
description = episode.overview,
|
||||||
).apply {
|
).apply {
|
||||||
this.addDate(episode.firstAired, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
|
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(
|
nextAir = NextAiring(
|
||||||
episode = this.episode!!,
|
episode = this.episode!!,
|
||||||
unixTime = this.date!!.div(1000L),
|
unixTime = this.date!!.div(1000L),
|
||||||
|
|
|
@ -22,6 +22,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
|
||||||
val addic7ed = Addic7ed()
|
val addic7ed = Addic7ed()
|
||||||
val subDlApi = SubDlApi(0)
|
val subDlApi = SubDlApi(0)
|
||||||
val localListApi = LocalList()
|
val localListApi = LocalList()
|
||||||
|
val subSourceApi = SubSourceApi()
|
||||||
|
|
||||||
// used to login via app intent
|
// used to login via app intent
|
||||||
val OAuth2Apis
|
val OAuth2Apis
|
||||||
|
@ -51,7 +52,8 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
|
||||||
get() = listOf(
|
get() = listOf(
|
||||||
openSubtitlesApi,
|
openSubtitlesApi,
|
||||||
addic7ed,
|
addic7ed,
|
||||||
subDlApi
|
subDlApi,
|
||||||
|
subSourceApi
|
||||||
)
|
)
|
||||||
|
|
||||||
const val appString = "cloudstreamapp"
|
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_MARK_AS_WATCHED = 18
|
||||||
const val ACTION_FCAST = 19
|
const val ACTION_FCAST = 19
|
||||||
|
|
||||||
const val TV_EP_SIZE_LARGE = 400
|
const val TV_EP_SIZE = 400
|
||||||
const val TV_EP_SIZE_SMALL = 300
|
|
||||||
data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
|
data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
|
||||||
|
|
||||||
class EpisodeAdapter(
|
class EpisodeAdapter(
|
||||||
|
@ -181,7 +180,7 @@ class EpisodeAdapter(
|
||||||
fun bind(card: ResultEpisode) {
|
fun bind(card: ResultEpisode) {
|
||||||
localCard = card
|
localCard = card
|
||||||
val setWidth =
|
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.episodeLinHolder.layoutParams.width = setWidth
|
||||||
binding.episodeHolderLarge.layoutParams.width = setWidth
|
binding.episodeHolderLarge.layoutParams.width = setWidth
|
||||||
|
@ -336,7 +335,7 @@ class EpisodeAdapter(
|
||||||
fun bind(card: ResultEpisode) {
|
fun bind(card: ResultEpisode) {
|
||||||
binding.episodeHolder.layoutParams.apply {
|
binding.episodeHolder.layoutParams.apply {
|
||||||
width =
|
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 {
|
binding.apply {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/add_site"
|
android:id="@+id/add_site"
|
||||||
android:text="@string/add_site_pref"
|
android:text="@string/add_site_pref"
|
||||||
|
android:focusable="true"
|
||||||
style="@style/SettingsItem">
|
style="@style/SettingsItem">
|
||||||
|
|
||||||
<requestFocus />
|
<requestFocus />
|
||||||
|
@ -15,5 +16,6 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/remove_site"
|
android:id="@+id/remove_site"
|
||||||
android:text="@string/remove_site_pref"
|
android:text="@string/remove_site_pref"
|
||||||
|
android:focusable="true"
|
||||||
style="@style/SettingsItem" />
|
style="@style/SettingsItem" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -90,14 +90,15 @@
|
||||||
|
|
||||||
android:textColor="?attr/textColor"
|
android:textColor="?attr/textColor"
|
||||||
tools:text="Episode 1" />
|
tools:text="Episode 1" />
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.lagradost.cloudstream3.ui.download.button.PieFetchButton
|
<com.lagradost.cloudstream3.ui.download.button.PieFetchButton
|
||||||
android:id="@+id/download_button"
|
android:id="@+id/download_button"
|
||||||
android:layout_width="@dimen/download_size"
|
android:layout_width="@dimen/download_size"
|
||||||
android:layout_height="@dimen/download_size"
|
android:layout_height="@dimen/download_size"
|
||||||
android:layout_gravity="center_vertical|end"
|
android:layout_gravity="center_vertical|end"
|
||||||
android:layout_marginStart="-50dp"
|
android:layout_marginStart="-60dp"
|
||||||
android:background="?selectableItemBackgroundBorderless"
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
android:padding="10dp" />
|
android:padding="10dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
|
@ -8,221 +8,135 @@
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
android:id="@+id/result_sync_holder"
|
android:id="@+id/result_sync_holder"
|
||||||
tools:visibility="visible"
|
android:layout_width="match_parent"
|
||||||
android:visibility="gone"
|
android:layout_height="wrap_content"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
android:layout_width="match_parent"
|
android:visibility="gone"
|
||||||
android:layout_height="wrap_content">
|
tools:visibility="visible">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/result_sync_names"
|
android:id="@+id/result_sync_names"
|
||||||
android:textStyle="bold"
|
android:layout_width="wrap_content"
|
||||||
android:textSize="16sp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:text="MyAnimeList, AniList"
|
android:text="MyAnimeList, AniList"
|
||||||
android:layout_width="wrap_content"
|
android:textSize="16sp"
|
||||||
android:layout_height="wrap_content" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:visibility="visible"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:visibility="visible">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/result_sync_sub_episode"
|
android:id="@+id/result_sync_sub_episode"
|
||||||
android:padding="10dp"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
|
||||||
android:src="@drawable/baseline_remove_24"
|
|
||||||
android:layout_gravity="end|center_vertical"
|
android:layout_gravity="end|center_vertical"
|
||||||
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:src="@drawable/baseline_remove_24"
|
||||||
app:tint="?attr/textColor" />
|
app:tint="?attr/textColor" />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:textColorHint="?attr/grayTextColor"
|
|
||||||
android:id="@+id/result_sync_current_episodes"
|
android:id="@+id/result_sync_current_episodes"
|
||||||
style="@style/AppEditStyle"
|
style="@style/AppEditStyle"
|
||||||
tools:hint="20"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:inputType="number"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number"
|
||||||
|
android:textColorHint="?attr/grayTextColor"
|
||||||
|
android:textSize="20sp"
|
||||||
|
tools:hint="20"
|
||||||
tools:ignore="LabelFor" />
|
tools:ignore="LabelFor" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/result_sync_max_episodes"
|
android:id="@+id/result_sync_max_episodes"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:paddingBottom="1dp"
|
android:paddingBottom="1dp"
|
||||||
android:textSize="20sp"
|
|
||||||
android:textColor="?attr/textColor"
|
android:textColor="?attr/textColor"
|
||||||
tools:text="30"
|
android:textSize="20sp"
|
||||||
android:layout_width="wrap_content"
|
tools:text="30" />
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/result_sync_add_episode"
|
android:id="@+id/result_sync_add_episode"
|
||||||
android:padding="10dp"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="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"
|
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" />
|
app:tint="?attr/textColor" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.core.widget.ContentLoadingProgressBar
|
<androidx.core.widget.ContentLoadingProgressBar
|
||||||
android:id="@+id/result_sync_episodes"
|
android:id="@+id/result_sync_episodes"
|
||||||
android:padding="10dp"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="20dp"
|
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"
|
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" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<!--
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:padding="10dp"
|
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" />-->
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="horizontal"
|
|
||||||
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="@string/sync_score"
|
android:text="@string/sync_score"
|
||||||
android:layout_width="wrap_content"
|
android:textColor="?attr/textColor"
|
||||||
android:layout_height="wrap_content" />
|
android:textSize="17sp" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/result_sync_score_text"
|
android:id="@+id/result_sync_score_text"
|
||||||
|
|
||||||
android:layout_height="30dp"
|
style="@style/BlackButton"
|
||||||
android:text="7/10"
|
|
||||||
android:minWidth="0dp"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="30dp"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_marginStart="0dp"
|
android:layout_marginStart="0dp"
|
||||||
style="@style/BlackButton" />
|
android:minWidth="0dp"
|
||||||
|
android:text="7/10" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.slider.Slider
|
<com.google.android.material.slider.Slider
|
||||||
android:id="@+id/result_sync_rating"
|
android:id="@+id/result_sync_rating"
|
||||||
android:valueFrom="0"
|
android:layout_width="wrap_content"
|
||||||
android:valueTo="10"
|
android:layout_height="wrap_content"
|
||||||
android:value="4"
|
|
||||||
android:stepSize="1"
|
|
||||||
app:tickVisible="false"
|
|
||||||
android:layout_marginStart="-5dp"
|
android:layout_marginStart="-5dp"
|
||||||
android:layout_marginEnd="-5dp"
|
android:layout_marginEnd="-5dp"
|
||||||
app:thumbRadius="10dp"
|
app:thumbHeight="20dp"
|
||||||
|
android:stepSize="1"
|
||||||
|
android:value="4"
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="10"
|
||||||
app:labelStyle="@style/BlackLabel"
|
app:labelStyle="@style/BlackLabel"
|
||||||
android:layout_width="wrap_content"
|
app:thumbRadius="10dp"
|
||||||
android:layout_height="wrap_content" />
|
app:tickVisible="false" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:visibility="gone"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
android:paddingTop="12dp"
|
android:paddingTop="12dp"
|
||||||
android:paddingBottom="12dp"
|
android:paddingBottom="12dp"
|
||||||
android:orientation="horizontal"
|
android:visibility="gone">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/home_parent_item_title"
|
android:id="@+id/home_parent_item_title"
|
||||||
|
@ -230,52 +144,53 @@
|
||||||
tools:text="Recommended" />
|
tools:text="Recommended" />
|
||||||
|
|
||||||
<ImageView
|
<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_width="30dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:contentDescription="@string/home_more_info" />
|
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>
|
</FrameLayout>
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
android:id="@+id/result_sync_check"
|
android:id="@+id/result_sync_check"
|
||||||
tools:listitem="@layout/sort_bottom_single_choice"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_rowWeight="1" />
|
android:layout_rowWeight="1"
|
||||||
|
tools:listitem="@layout/sort_bottom_single_choice" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:visibility="gone"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
style="@style/WhiteButton"
|
style="@style/WhiteButton"
|
||||||
android:text="@string/type_watching" />
|
android:layout_width="match_parent"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:text="@string/type_watching"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/result_sync_set_score"
|
android:id="@+id/result_sync_set_score"
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
style="@style/BlackButton"
|
style="@style/BlackButton"
|
||||||
app:icon="@drawable/baseline_sync_24"
|
android:layout_width="match_parent"
|
||||||
android:text="@string/upload_sync" />
|
android:layout_marginTop="10dp"
|
||||||
|
android:text="@string/upload_sync"
|
||||||
|
app:icon="@drawable/baseline_sync_24" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<com.facebook.shimmer.ShimmerFrameLayout
|
<com.facebook.shimmer.ShimmerFrameLayout
|
||||||
tools:visibility="gone"
|
|
||||||
android:id="@+id/result_sync_loading_shimmer"
|
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_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:orientation="vertical">
|
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
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -6,6 +6,7 @@ import com.lagradost.cloudstream3.extractors.helper.AesHelper.cryptoAESHandler
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorApi
|
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||||
|
import kotlin.run
|
||||||
|
|
||||||
class Moviesapi : Chillx() {
|
class Moviesapi : Chillx() {
|
||||||
override val name = "Moviesapi"
|
override val name = "Moviesapi"
|
||||||
|
@ -28,17 +29,22 @@ open class Chillx : ExtractorApi() {
|
||||||
override val requiresReferer = true
|
override val requiresReferer = true
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val keySource = "https://rowdy-avocado.github.io/multi-keys/"
|
||||||
|
|
||||||
private var key: String? = null
|
private var key: String? = null
|
||||||
|
|
||||||
suspend fun fetchKey(): String {
|
private suspend fun fetchKey(): String {
|
||||||
return if (key != null) {
|
return key
|
||||||
key!!
|
?: run {
|
||||||
} else {
|
val res =
|
||||||
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")
|
app.get(keySource).parsedSafe<KeysData>()
|
||||||
key = fetch
|
?: throw ErrorLoadingException("Unable to get keys")
|
||||||
key!!
|
key = res.keys.get(0)
|
||||||
|
res.keys.get(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private data class KeysData(@JsonProperty("chillx") val keys: List<String>)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
|
@ -97,11 +103,4 @@ open class Chillx : ExtractorApi() {
|
||||||
it.groupValues[1].toInt(16).toChar().toString()
|
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"
|
override var mainUrl = "https://four.playru.net"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Pichive : ContentX() {
|
||||||
|
override var name = "Pichive"
|
||||||
|
override var mainUrl = "https://pichive.online"
|
||||||
|
}
|
||||||
|
|
||||||
class FourPichive : ContentX() {
|
class FourPichive : ContentX() {
|
||||||
override var name = "FourPichive"
|
override var name = "FourPichive"
|
||||||
override var mainUrl = "https://four.pichive.online"
|
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
|
callback: (ExtractorLink) -> Unit
|
||||||
) {
|
) {
|
||||||
val mediaId = app.get(url).document.selectFirst("ul.episodes li a")?.attr("data-id") ?: return
|
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
|
if (res.status != 200) return
|
||||||
res.result?.amap { source ->
|
res.result?.amap { source ->
|
||||||
try {
|
try {
|
||||||
|
@ -68,5 +74,11 @@ class VidSrcTo : ExtractorApi() {
|
||||||
@JsonProperty("result") val result: VidsrctoUrl
|
@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)
|
data class VidsrctoUrl(@JsonProperty("url") val encUrl: String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.lagradost.cloudstream3.extractors
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.lagradost.cloudstream3.ErrorLoadingException
|
||||||
import com.lagradost.cloudstream3.SubtitleFile
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
import com.lagradost.cloudstream3.app
|
import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.base64Encode
|
import com.lagradost.cloudstream3.base64Encode
|
||||||
|
@ -9,6 +10,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
import kotlin.run
|
||||||
|
|
||||||
// Code found in https://github.com/KillerDogeEmpire/vidplay-keys
|
// Code found in https://github.com/KillerDogeEmpire/vidplay-keys
|
||||||
// special credits to @KillerDogeEmpire for providing key
|
// special credits to @KillerDogeEmpire for providing key
|
||||||
|
@ -35,8 +37,25 @@ open class Vidplay : ExtractorApi() {
|
||||||
override val name = "Vidplay"
|
override val name = "Vidplay"
|
||||||
override val mainUrl = "https://vidplay.site"
|
override val mainUrl = "https://vidplay.site"
|
||||||
override val requiresReferer = true
|
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(
|
override suspend fun getUrl(
|
||||||
url: String,
|
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? {
|
private suspend fun callFutoken(id: String, url: String): String? {
|
||||||
val script = app.get("$mainUrl/futoken", referer = url).text
|
val script = app.get("$mainUrl/futoken", referer = url).text
|
||||||
val k = "k='(\\S+)'".toRegex().find(script)?.groupValues?.get(1) ?: return null
|
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.FourCX
|
||||||
import com.lagradost.cloudstream3.extractors.PlayRu
|
import com.lagradost.cloudstream3.extractors.PlayRu
|
||||||
import com.lagradost.cloudstream3.extractors.FourPlayRu
|
import com.lagradost.cloudstream3.extractors.FourPlayRu
|
||||||
|
import com.lagradost.cloudstream3.extractors.Pichive
|
||||||
import com.lagradost.cloudstream3.extractors.FourPichive
|
import com.lagradost.cloudstream3.extractors.FourPichive
|
||||||
import com.lagradost.cloudstream3.extractors.HDMomPlayer
|
import com.lagradost.cloudstream3.extractors.HDMomPlayer
|
||||||
import com.lagradost.cloudstream3.extractors.HDPlayerSystem
|
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.RapidVid
|
||||||
import com.lagradost.cloudstream3.extractors.TRsTX
|
import com.lagradost.cloudstream3.extractors.TRsTX
|
||||||
import com.lagradost.cloudstream3.extractors.VidMoxy
|
import com.lagradost.cloudstream3.extractors.VidMoxy
|
||||||
|
import com.lagradost.cloudstream3.extractors.Sobreatsesuyp
|
||||||
import com.lagradost.cloudstream3.extractors.PixelDrain
|
import com.lagradost.cloudstream3.extractors.PixelDrain
|
||||||
import com.lagradost.cloudstream3.extractors.MailRu
|
import com.lagradost.cloudstream3.extractors.MailRu
|
||||||
import com.lagradost.cloudstream3.extractors.Mediafire
|
import com.lagradost.cloudstream3.extractors.Mediafire
|
||||||
|
@ -734,6 +736,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
|
||||||
FourCX(),
|
FourCX(),
|
||||||
PlayRu(),
|
PlayRu(),
|
||||||
FourPlayRu(),
|
FourPlayRu(),
|
||||||
|
Pichive(),
|
||||||
FourPichive(),
|
FourPichive(),
|
||||||
HDMomPlayer(),
|
HDMomPlayer(),
|
||||||
HDPlayerSystem(),
|
HDPlayerSystem(),
|
||||||
|
@ -743,6 +746,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
|
||||||
RapidVid(),
|
RapidVid(),
|
||||||
TRsTX(),
|
TRsTX(),
|
||||||
VidMoxy(),
|
VidMoxy(),
|
||||||
|
Sobreatsesuyp(),
|
||||||
PixelDrain(),
|
PixelDrain(),
|
||||||
MailRu(),
|
MailRu(),
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue