Compare commits
6 Commits
b01cb76847
...
acfb0b9422
Author | SHA1 | Date |
---|---|---|
IndusAryan | acfb0b9422 | |
KingLucius | 004c481a5e | |
b4byhuey | e2946cad6b | |
IndusAryan | f89aad3e95 | |
IndusAryan | aff24843a0 | |
IndusAryan | 71b61733e4 |
|
@ -134,7 +134,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||||
import com.lagradost.cloudstream3.utils.BackupUtils.backup
|
import com.lagradost.cloudstream3.utils.BackupUtils.backup
|
||||||
import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup
|
import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.BiometricCallback
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.biometricPrompt
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.biometricPrompt
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.deviceHasPasswordPinLock
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.deviceHasPasswordPinLock
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.isAuthEnabled
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.isAuthEnabled
|
||||||
|
@ -294,8 +294,7 @@ var app = Requests(responseParser = object : ResponseParser {
|
||||||
defaultHeaders = mapOf("user-agent" to USER_AGENT)
|
defaultHeaders = mapOf("user-agent" to USER_AGENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), ColorPickerDialogListener,
|
class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCallback {
|
||||||
BiometricAuthenticator.BiometricAuthCallback {
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "MAINACT"
|
const val TAG = "MAINACT"
|
||||||
const val ANIMATED_OUTLINE: Boolean = false
|
const val ANIMATED_OUTLINE: Boolean = false
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
|
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.INFER_TYPE
|
||||||
|
import com.lagradost.cloudstream3.utils.Qualities
|
||||||
|
import org.mozilla.javascript.Context
|
||||||
|
import org.mozilla.javascript.NativeJSON
|
||||||
|
import org.mozilla.javascript.NativeObject
|
||||||
|
import org.mozilla.javascript.Scriptable
|
||||||
|
import java.util.Base64
|
||||||
|
|
||||||
|
open class Vidguardto : ExtractorApi() {
|
||||||
|
override val name = "Vidguard"
|
||||||
|
override val mainUrl = "https://vidguard.to"
|
||||||
|
override val requiresReferer = false
|
||||||
|
|
||||||
|
override suspend fun getUrl(
|
||||||
|
url: String,
|
||||||
|
referer: String?,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
val res = app.get(url)
|
||||||
|
val resc = res.document.select("script:containsData(eval)").firstOrNull()?.data()
|
||||||
|
resc?.let {
|
||||||
|
val jsonStr2 = AppUtils.parseJson<SvgObject>(runJS2(it))
|
||||||
|
val watchlink = sigDecode(jsonStr2.stream)
|
||||||
|
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
this.name,
|
||||||
|
name,
|
||||||
|
watchlink,
|
||||||
|
this.mainUrl,
|
||||||
|
Qualities.Unknown.value,
|
||||||
|
INFER_TYPE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sigDecode(url: String): String {
|
||||||
|
val sig = url.split("sig=")[1].split("&")[0]
|
||||||
|
var t = ""
|
||||||
|
for (v in sig.chunked(2)) {
|
||||||
|
val byteValue = Integer.parseInt(v, 16) xor 2
|
||||||
|
t += byteValue.toChar()
|
||||||
|
}
|
||||||
|
val padding = when (t.length % 4) {
|
||||||
|
2 -> "=="
|
||||||
|
3 -> "="
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
val decoded = Base64.getDecoder().decode((t + padding).toByteArray(Charsets.UTF_8))
|
||||||
|
t = String(decoded).dropLast(5).reversed()
|
||||||
|
val charArray = t.toCharArray()
|
||||||
|
for (i in 0 until charArray.size - 1 step 2) {
|
||||||
|
val temp = charArray[i]
|
||||||
|
charArray[i] = charArray[i + 1]
|
||||||
|
charArray[i + 1] = temp
|
||||||
|
}
|
||||||
|
val modifiedSig = String(charArray).dropLast(5)
|
||||||
|
return url.replace(sig, modifiedSig)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runJS2(hideMyHtmlContent: String): String {
|
||||||
|
Log.d("runJS", "start")
|
||||||
|
val rhino = Context.enter()
|
||||||
|
rhino.initSafeStandardObjects()
|
||||||
|
rhino.optimizationLevel = -1
|
||||||
|
val scope: Scriptable = rhino.initSafeStandardObjects()
|
||||||
|
scope.put("window", scope, scope)
|
||||||
|
var result = ""
|
||||||
|
try {
|
||||||
|
Log.d("runJS", "Executing JavaScript: $hideMyHtmlContent")
|
||||||
|
rhino.evaluateString(scope, hideMyHtmlContent, "JavaScript", 1, null)
|
||||||
|
val svgObject = scope.get("svg", scope)
|
||||||
|
result = if (svgObject is NativeObject) {
|
||||||
|
NativeJSON.stringify(Context.getCurrentContext(), scope, svgObject, null, null).toString()
|
||||||
|
} else {
|
||||||
|
Context.toString(svgObject)
|
||||||
|
}
|
||||||
|
Log.d("runJS", "Result: $result")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("runJS", "Error executing JavaScript", e)
|
||||||
|
} finally {
|
||||||
|
Context.exit()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
data class SvgObject(
|
||||||
|
val stream: String,
|
||||||
|
val hash: String
|
||||||
|
)
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals.TV
|
import com.lagradost.cloudstream3.ui.settings.Globals.TV
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
|
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator
|
||||||
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.BiometricCallback
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.biometricPrompt
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.biometricPrompt
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.deviceHasPasswordPinLock
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.deviceHasPasswordPinLock
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.isAuthEnabled
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.isAuthEnabled
|
||||||
|
@ -33,7 +34,7 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.selectedKeyIndex
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setAccount
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setAccount
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
|
|
||||||
class AccountSelectActivity : AppCompatActivity(), BiometricAuthenticator.BiometricAuthCallback {
|
class AccountSelectActivity : AppCompatActivity(), BiometricCallback {
|
||||||
|
|
||||||
lateinit var viewModel: AccountViewModel
|
lateinit var viewModel: AccountViewModel
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,11 @@ import androidx.core.view.isVisible
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.ResultEpisodeBinding
|
import com.lagradost.cloudstream3.databinding.ResultEpisodeBinding
|
||||||
import com.lagradost.cloudstream3.databinding.ResultEpisodeLargeBinding
|
import com.lagradost.cloudstream3.databinding.ResultEpisodeLargeBinding
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.secondsToReadable
|
||||||
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
|
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
|
||||||
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_LONG_CLICK
|
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_LONG_CLICK
|
||||||
import com.lagradost.cloudstream3.ui.download.DownloadClickEvent
|
import com.lagradost.cloudstream3.ui.download.DownloadClickEvent
|
||||||
|
@ -23,6 +25,8 @@ import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
const val ACTION_PLAY_EPISODE_IN_PLAYER = 1
|
const val ACTION_PLAY_EPISODE_IN_PLAYER = 1
|
||||||
|
@ -104,7 +108,7 @@ class EpisodeAdapter(
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
val item = getItem(position)
|
val item = getItem(position)
|
||||||
return if (item.poster.isNullOrBlank()) 0 else 1
|
return if (item.poster.isNullOrBlank() && item.description.isNullOrBlank()) 0 else 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -260,6 +264,33 @@ class EpisodeAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (card.airDate != null) {
|
||||||
|
val isUpcoming = unixTimeMS < card.airDate
|
||||||
|
|
||||||
|
if (isUpcoming) {
|
||||||
|
episodePlayIcon.isVisible = false
|
||||||
|
episodeUpcomingIcon.isVisible = !episodePoster.isVisible
|
||||||
|
episodeDate.setText(
|
||||||
|
txt(
|
||||||
|
R.string.episode_upcoming_format,
|
||||||
|
secondsToReadable(card.airDate.minus(unixTimeMS).div(1000).toInt(), "")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
episodeUpcomingIcon.isVisible = false
|
||||||
|
|
||||||
|
val formattedAirDate = SimpleDateFormat.getDateInstance(
|
||||||
|
DateFormat.LONG,
|
||||||
|
Locale.getDefault()
|
||||||
|
).apply {
|
||||||
|
}.format(Date(card.airDate))
|
||||||
|
|
||||||
|
episodeDate.setText(txt(formattedAirDate))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
episodeDate.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
if (isLayout(EMULATOR or PHONE)) {
|
if (isLayout(EMULATOR or PHONE)) {
|
||||||
episodePoster.setOnClickListener {
|
episodePoster.setOnClickListener {
|
||||||
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
||||||
|
@ -271,6 +302,7 @@ class EpisodeAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
itemView.setOnClickListener {
|
itemView.setOnClickListener {
|
||||||
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ data class ResultEpisode(
|
||||||
val videoWatchState: VideoWatchState,
|
val videoWatchState: VideoWatchState,
|
||||||
/** Sum of all previous season episode counts + episode */
|
/** Sum of all previous season episode counts + episode */
|
||||||
val totalEpisodeIndex: Int? = null,
|
val totalEpisodeIndex: Int? = null,
|
||||||
|
val airDate: Long? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ResultEpisode.getRealPosition(): Long {
|
fun ResultEpisode.getRealPosition(): Long {
|
||||||
|
@ -85,6 +86,7 @@ fun buildResultEpisode(
|
||||||
tvType: TvType,
|
tvType: TvType,
|
||||||
parentId: Int,
|
parentId: Int,
|
||||||
totalEpisodeIndex: Int? = null,
|
totalEpisodeIndex: Int? = null,
|
||||||
|
airDate: Long? = null,
|
||||||
): ResultEpisode {
|
): ResultEpisode {
|
||||||
val posDur = getViewPos(id)
|
val posDur = getViewPos(id)
|
||||||
val videoWatchState = getVideoWatchState(id) ?: VideoWatchState.None
|
val videoWatchState = getVideoWatchState(id) ?: VideoWatchState.None
|
||||||
|
@ -107,7 +109,8 @@ fun buildResultEpisode(
|
||||||
tvType,
|
tvType,
|
||||||
parentId,
|
parentId,
|
||||||
videoWatchState,
|
videoWatchState,
|
||||||
totalEpisodeIndex
|
totalEpisodeIndex,
|
||||||
|
airDate,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2277,7 +2277,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
fillers.getOrDefault(episode, false),
|
fillers.getOrDefault(episode, false),
|
||||||
loadResponse.type,
|
loadResponse.type,
|
||||||
mainId,
|
mainId,
|
||||||
totalIndex
|
totalIndex,
|
||||||
|
airDate = i.date
|
||||||
)
|
)
|
||||||
|
|
||||||
val season = eps.seasonIndex ?: 0
|
val season = eps.seasonIndex ?: 0
|
||||||
|
@ -2326,7 +2327,8 @@ class ResultViewModel2 : ViewModel() {
|
||||||
null,
|
null,
|
||||||
loadResponse.type,
|
loadResponse.type,
|
||||||
mainId,
|
mainId,
|
||||||
totalIndex
|
totalIndex,
|
||||||
|
airDate = episode.date
|
||||||
)
|
)
|
||||||
|
|
||||||
val season = ep.seasonIndex ?: 0
|
val season = ep.seasonIndex ?: 0
|
||||||
|
|
|
@ -40,7 +40,7 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setTool
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.html
|
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||||
import com.lagradost.cloudstream3.utils.BackupUtils
|
import com.lagradost.cloudstream3.utils.BackupUtils
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.BiometricCallback
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.authCallback
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.authCallback
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.biometricPrompt
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.biometricPrompt
|
||||||
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.deviceHasPasswordPinLock
|
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.deviceHasPasswordPinLock
|
||||||
|
@ -53,7 +53,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
|
||||||
class SettingsAccount : PreferenceFragmentCompat(), BiometricAuthenticator.BiometricAuthCallback {
|
class SettingsAccount : PreferenceFragmentCompat(), BiometricCallback {
|
||||||
companion object {
|
companion object {
|
||||||
/** Used by nginx plugin too */
|
/** Used by nginx plugin too */
|
||||||
fun showLoginInfo(
|
fun showLoginInfo(
|
||||||
|
|
|
@ -33,7 +33,7 @@ import com.lagradost.cloudstream3.ui.settings.Globals.TV
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
|
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.downloadAllPluginsDialog
|
import com.lagradost.cloudstream3.utils.AppUtils.addRepositoryDialog
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
|
@ -273,10 +273,8 @@ class ExtensionsFragment : Fragment() {
|
||||||
if (plugins.isNullOrEmpty()) {
|
if (plugins.isNullOrEmpty()) {
|
||||||
showToast(R.string.no_plugins_found_error, Toast.LENGTH_LONG)
|
showToast(R.string.no_plugins_found_error, Toast.LENGTH_LONG)
|
||||||
} else {
|
} else {
|
||||||
this@ExtensionsFragment.activity?.downloadAllPluginsDialog(
|
this@ExtensionsFragment.activity?.addRepositoryDialog(
|
||||||
url,
|
fixedName, true)
|
||||||
fixedName
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
||||||
import com.lagradost.cloudstream3.AllLanguagesName
|
import com.lagradost.cloudstream3.AllLanguagesName
|
||||||
|
import com.lagradost.cloudstream3.BuildConfig
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.TvType
|
import com.lagradost.cloudstream3.TvType
|
||||||
import com.lagradost.cloudstream3.databinding.FragmentPluginsBinding
|
import com.lagradost.cloudstream3.databinding.FragmentPluginsBinding
|
||||||
|
@ -70,6 +71,8 @@ class PluginsFragment : Fragment() {
|
||||||
val name = arguments?.getString(PLUGINS_BUNDLE_NAME)
|
val name = arguments?.getString(PLUGINS_BUNDLE_NAME)
|
||||||
val url = arguments?.getString(PLUGINS_BUNDLE_URL)
|
val url = arguments?.getString(PLUGINS_BUNDLE_URL)
|
||||||
val isLocal = arguments?.getBoolean(PLUGINS_BUNDLE_LOCAL) == true
|
val isLocal = arguments?.getBoolean(PLUGINS_BUNDLE_LOCAL) == true
|
||||||
|
// download all extensions button
|
||||||
|
val downloadAllButton = binding?.settingsToolbar?.menu?.findItem(R.id.download_all)
|
||||||
|
|
||||||
if (url == null || name == null) {
|
if (url == null || name == null) {
|
||||||
activity?.onBackPressedDispatcher?.onBackPressed()
|
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||||
|
@ -171,7 +174,7 @@ class PluginsFragment : Fragment() {
|
||||||
|
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
// No download button and no categories on local
|
// No download button and no categories on local
|
||||||
binding?.settingsToolbar?.menu?.findItem(R.id.download_all)?.isVisible = false
|
downloadAllButton?.isVisible = false
|
||||||
binding?.settingsToolbar?.menu?.findItem(R.id.lang_filter)?.isVisible = false
|
binding?.settingsToolbar?.menu?.findItem(R.id.lang_filter)?.isVisible = false
|
||||||
pluginViewModel.updatePluginListLocal()
|
pluginViewModel.updatePluginListLocal()
|
||||||
|
|
||||||
|
@ -179,6 +182,10 @@ class PluginsFragment : Fragment() {
|
||||||
} else {
|
} else {
|
||||||
pluginViewModel.updatePluginList(context, url)
|
pluginViewModel.updatePluginList(context, url)
|
||||||
binding?.tvtypesChipsScroll?.root?.isVisible = true
|
binding?.tvtypesChipsScroll?.root?.isVisible = true
|
||||||
|
// not needed for users but may be useful for devs
|
||||||
|
downloadAllButton?.isVisible = BuildConfig.DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bindChips(
|
bindChips(
|
||||||
binding?.tvtypesChipsScroll?.tvtypesChips,
|
binding?.tvtypesChipsScroll?.tvtypesChips,
|
||||||
|
|
|
@ -62,6 +62,7 @@ import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
||||||
import com.lagradost.cloudstream3.ui.WebviewFragment
|
import com.lagradost.cloudstream3.ui.WebviewFragment
|
||||||
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals
|
import com.lagradost.cloudstream3.ui.settings.Globals
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.extensions.PluginsFragment
|
||||||
import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel.Companion.downloadAll
|
import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel.Companion.downloadAll
|
||||||
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
|
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
|
@ -386,7 +387,7 @@ object AppUtils {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
afterRepositoryLoadedEvent.invoke(true)
|
afterRepositoryLoadedEvent.invoke(true)
|
||||||
downloadAllPluginsDialog(url, repo.name)
|
addRepositoryDialog(repo.name, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,25 +430,37 @@ object AppUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Activity.addRepositoryDialog(repositoryName: String, isExtensionsFragment: Boolean) {
|
||||||
|
val message = String.format(resources.getString(
|
||||||
|
R.string.download_all_plugins_from_repo), repositoryName)
|
||||||
|
val repos = RepositoryManager.getRepositories()
|
||||||
|
|
||||||
fun Activity.downloadAllPluginsDialog(repositoryUrl: String, repositoryName: String) {
|
// navigate to newly added repository on pressing OK
|
||||||
runOnUiThread {
|
fun openAddedRepo() {
|
||||||
val context = this
|
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
|
// don't redirect if user is adding from add repo button
|
||||||
builder.setTitle(
|
if (!isExtensionsFragment && repos.isNotEmpty()) {
|
||||||
repositoryName
|
normalSafeApiCall { navigate(
|
||||||
)
|
R.id.navigation_home_to_navigation_settings_plugins,
|
||||||
builder.setMessage(
|
PluginsFragment.newInstance(
|
||||||
R.string.download_all_plugins_from_repo
|
repositoryName,
|
||||||
)
|
repos.last().url,
|
||||||
builder.apply {
|
false))
|
||||||
setPositiveButton(R.string.download) { _, _ ->
|
|
||||||
downloadAll(context, repositoryUrl, null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setNegativeButton(R.string.no) { _, _ -> }
|
|
||||||
}
|
}
|
||||||
builder.show().setDefaultFocus()
|
}
|
||||||
|
|
||||||
|
runOnUiThread {
|
||||||
|
val builder : AlertDialog.Builder = AlertDialog.Builder(this)
|
||||||
|
builder.apply {
|
||||||
|
setTitle(repositoryName)
|
||||||
|
setMessage(message)
|
||||||
|
setPositiveButton(R.string.ok) { _, _ ->
|
||||||
|
openAddedRepo()
|
||||||
|
}
|
||||||
|
setCancelable(false)
|
||||||
|
show().setDefaultFocus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ object BiometricAuthenticator {
|
||||||
private var biometricManager: BiometricManager? = null
|
private var biometricManager: BiometricManager? = null
|
||||||
var biometricPrompt: BiometricPrompt? = null
|
var biometricPrompt: BiometricPrompt? = null
|
||||||
var promptInfo: BiometricPrompt.PromptInfo? = null
|
var promptInfo: BiometricPrompt.PromptInfo? = null
|
||||||
var authCallback: BiometricAuthCallback? = null // listen to authentication success
|
var authCallback: BiometricCallback? = null // listen to authentication success
|
||||||
|
|
||||||
private fun initializeBiometrics(activity: Activity) {
|
private fun initializeBiometrics(activity: Activity) {
|
||||||
val executor = ContextCompat.getMainExecutor(activity)
|
val executor = ContextCompat.getMainExecutor(activity)
|
||||||
|
@ -141,14 +141,14 @@ object BiometricAuthenticator {
|
||||||
// function to start authentication in any fragment or activity
|
// function to start authentication in any fragment or activity
|
||||||
fun startBiometricAuthentication(activity: Activity, title: Int, setDeviceCred: Boolean) {
|
fun startBiometricAuthentication(activity: Activity, title: Int, setDeviceCred: Boolean) {
|
||||||
initializeBiometrics(activity)
|
initializeBiometrics(activity)
|
||||||
authCallback = activity as? BiometricAuthCallback
|
authCallback = activity as? BiometricCallback
|
||||||
if (isBiometricHardWareAvailable()) {
|
if (isBiometricHardWareAvailable()) {
|
||||||
authCallback = activity as? BiometricAuthCallback
|
authCallback = activity as? BiometricCallback
|
||||||
authenticationDialog(activity, title, setDeviceCred)
|
authenticationDialog(activity, title, setDeviceCred)
|
||||||
promptInfo?.let { biometricPrompt?.authenticate(it) }
|
promptInfo?.let { biometricPrompt?.authenticate(it) }
|
||||||
} else {
|
} else {
|
||||||
if (deviceHasPasswordPinLock(activity)) {
|
if (deviceHasPasswordPinLock(activity)) {
|
||||||
authCallback = activity as? BiometricAuthCallback
|
authCallback = activity as? BiometricCallback
|
||||||
authenticationDialog(activity, R.string.password_pin_authentication_title, true)
|
authenticationDialog(activity, R.string.password_pin_authentication_title, true)
|
||||||
promptInfo?.let { biometricPrompt?.authenticate(it) }
|
promptInfo?.let { biometricPrompt?.authenticate(it) }
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ object BiometricAuthenticator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BiometricAuthCallback {
|
interface BiometricCallback {
|
||||||
fun onAuthenticationSuccess()
|
fun onAuthenticationSuccess()
|
||||||
fun onAuthenticationError()
|
fun onAuthenticationError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,6 +186,7 @@ import com.lagradost.cloudstream3.extractors.VideoVard
|
||||||
import com.lagradost.cloudstream3.extractors.VideovardSX
|
import com.lagradost.cloudstream3.extractors.VideovardSX
|
||||||
import com.lagradost.cloudstream3.extractors.Vidgomunime
|
import com.lagradost.cloudstream3.extractors.Vidgomunime
|
||||||
import com.lagradost.cloudstream3.extractors.Vidgomunimesb
|
import com.lagradost.cloudstream3.extractors.Vidgomunimesb
|
||||||
|
import com.lagradost.cloudstream3.extractors.Vidguardto
|
||||||
import com.lagradost.cloudstream3.extractors.VidhideExtractor
|
import com.lagradost.cloudstream3.extractors.VidhideExtractor
|
||||||
import com.lagradost.cloudstream3.extractors.Vidmoly
|
import com.lagradost.cloudstream3.extractors.Vidmoly
|
||||||
import com.lagradost.cloudstream3.extractors.Vidmolyme
|
import com.lagradost.cloudstream3.extractors.Vidmolyme
|
||||||
|
@ -888,7 +889,8 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
|
||||||
StreamWishExtractor(),
|
StreamWishExtractor(),
|
||||||
EmturbovidExtractor(),
|
EmturbovidExtractor(),
|
||||||
Vtbe(),
|
Vtbe(),
|
||||||
EPlayExtractor()
|
EPlayExtractor(),
|
||||||
|
Vidguardto()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="#9BA0A4"
|
||||||
|
android:pathData="M320,800h320v-120q0,-66 -47,-113t-113,-47q-66,0 -113,47t-47,113v120ZM480,440q66,0 113,-47t47,-113v-120L320,160v120q0,66 47,113t113,47ZM160,880v-80h80v-120q0,-61 28.5,-114.5T348,480q-51,-32 -79.5,-85.5T240,280v-120h-80v-80h640v80h-80v120q0,61 -28.5,114.5T612,480q51,32 79.5,85.5T720,680v120h80v80L160,880ZM480,800ZM480,160Z"/>
|
||||||
|
</vector>
|
|
@ -43,14 +43,26 @@
|
||||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
android:nextFocusRight="@id/download_button"
|
android:nextFocusRight="@id/download_button"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
tools:src="@drawable/example_poster" />
|
tools:src="@drawable/example_poster"
|
||||||
|
tools:visibility="invisible"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
android:id="@+id/episode_play_icon"
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:contentDescription="@string/play_episode"
|
android:contentDescription="@string/play_episode"
|
||||||
android:src="@drawable/play_button" />
|
android:src="@drawable/play_button"
|
||||||
|
tools:visibility="invisible"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/episode_upcoming_icon"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/hourglass_24"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<androidx.core.widget.ContentLoadingProgressBar
|
<androidx.core.widget.ContentLoadingProgressBar
|
||||||
android:id="@+id/episode_progress"
|
android:id="@+id/episode_progress"
|
||||||
|
@ -100,6 +112,13 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textColor="?attr/grayTextColor"
|
android:textColor="?attr/grayTextColor"
|
||||||
tools:text="Rated: 8.8" />
|
tools:text="Rated: 8.8" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/episode_date"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="?attr/grayTextColor"
|
||||||
|
tools:text="15 Apr 2024" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.lagradost.cloudstream3.ui.download.button.PieFetchButton
|
<com.lagradost.cloudstream3.ui.download.button.PieFetchButton
|
||||||
|
|
|
@ -21,5 +21,6 @@
|
||||||
android:id="@+id/download_all"
|
android:id="@+id/download_all"
|
||||||
android:icon="@drawable/netflix_download"
|
android:icon="@drawable/netflix_download"
|
||||||
android:title="@string/batch_download"
|
android:title="@string/batch_download"
|
||||||
app:showAsAction="collapseActionView|ifRoom" />
|
app:showAsAction="collapseActionView|ifRoom"
|
||||||
|
android:visible="false"/>
|
||||||
</menu>
|
</menu>
|
|
@ -273,6 +273,27 @@
|
||||||
app:exitAnim="@anim/exit_anim"
|
app:exitAnim="@anim/exit_anim"
|
||||||
app:popEnterAnim="@anim/enter_anim"
|
app:popEnterAnim="@anim/enter_anim"
|
||||||
app:popExitAnim="@anim/exit_anim" />
|
app:popExitAnim="@anim/exit_anim" />
|
||||||
|
|
||||||
|
<action
|
||||||
|
android:id="@+id/navigation_home_to_navigation_settings_plugins"
|
||||||
|
app:destination="@id/navigation_settings_plugins"
|
||||||
|
app:enterAnim="@anim/enter_anim"
|
||||||
|
app:exitAnim="@anim/exit_anim"
|
||||||
|
app:popEnterAnim="@anim/enter_anim"
|
||||||
|
app:popExitAnim="@anim/exit_anim">
|
||||||
|
<argument
|
||||||
|
android:name="name"
|
||||||
|
android:defaultValue="@null"
|
||||||
|
app:argType="string" />
|
||||||
|
<argument
|
||||||
|
android:name="url"
|
||||||
|
android:defaultValue="@null"
|
||||||
|
app:argType="string" />
|
||||||
|
<argument
|
||||||
|
android:name="isLocal"
|
||||||
|
android:defaultValue="false"
|
||||||
|
app:argType="boolean" />
|
||||||
|
</action>
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
|
|
|
@ -292,6 +292,7 @@
|
||||||
<string name="episodes">Episodes</string>
|
<string name="episodes">Episodes</string>
|
||||||
<string name="episodes_range">%1$d-%2$d</string>
|
<string name="episodes_range">%1$d-%2$d</string>
|
||||||
<string name="episode_format" formatted="true">%1$d %2$s</string>
|
<string name="episode_format" formatted="true">%1$d %2$s</string>
|
||||||
|
<string name="episode_upcoming_format" formatted="true">Upcoming in %s</string>
|
||||||
<string name="season_short">S</string>
|
<string name="season_short">S</string>
|
||||||
<string name="episode_short">E</string>
|
<string name="episode_short">E</string>
|
||||||
<string name="no_episodes_found">No Episodes found</string>
|
<string name="no_episodes_found">No Episodes found</string>
|
||||||
|
@ -348,6 +349,9 @@
|
||||||
<string name="live_singular">Livestream</string>
|
<string name="live_singular">Livestream</string>
|
||||||
<string name="nsfw_singular">NSFW</string>
|
<string name="nsfw_singular">NSFW</string>
|
||||||
<string name="other_singular">Video</string>
|
<string name="other_singular">Video</string>
|
||||||
|
<string name="music_singlar">Music</string>
|
||||||
|
<string name="audio_book_singular">Audio Book</string>
|
||||||
|
<string name="custom_media_singluar">Media</string>
|
||||||
<string name="source_error">Source error</string>
|
<string name="source_error">Source error</string>
|
||||||
<string name="remote_error">Remote error</string>
|
<string name="remote_error">Remote error</string>
|
||||||
<string name="render_error">Renderer error</string>
|
<string name="render_error">Renderer error</string>
|
||||||
|
@ -606,7 +610,8 @@
|
||||||
<string name="view_public_repositories_button">View community repositories</string>
|
<string name="view_public_repositories_button">View community repositories</string>
|
||||||
<string name="view_public_repositories_button_short">Public list</string>
|
<string name="view_public_repositories_button_short">Public list</string>
|
||||||
<string name="uppercase_all_subtitles">Uppercase all subtitles</string>
|
<string name="uppercase_all_subtitles">Uppercase all subtitles</string>
|
||||||
<string name="download_all_plugins_from_repo">Download all plugins from this repository?</string>
|
<string name="download_all_plugins_from_repo">%1$s has been added. You can install your desired extensions from 𝗦𝗲𝘁𝘁𝗶𝗻𝗴𝘀 > 𝗘𝘅𝘁𝗲𝗻𝘀𝗶𝗼𝗻𝘀 > %1$s.
|
||||||
|
CloudStream 3 does not takes any responsibility for using third-party extensions neither provides support for them.</string>
|
||||||
<string name="single_plugin_disabled" formatted="true">%s (Disabled)</string>
|
<string name="single_plugin_disabled" formatted="true">%s (Disabled)</string>
|
||||||
<string name="tracks">Tracks</string>
|
<string name="tracks">Tracks</string>
|
||||||
<string name="audio_tracks">Audio tracks</string>
|
<string name="audio_tracks">Audio tracks</string>
|
||||||
|
@ -762,10 +767,7 @@
|
||||||
<string name="password_pin_authentication_title">Password/PIN Authentication</string>
|
<string name="password_pin_authentication_title">Password/PIN Authentication</string>
|
||||||
<string name="biometric_unsupported">Biometric authentication is not supported on this device</string>
|
<string name="biometric_unsupported">Biometric authentication is not supported on this device</string>
|
||||||
<string name="biometric_setting_summary">Unlock the app with Fingerprint, Face ID, PIN, Pattern and Password.</string>
|
<string name="biometric_setting_summary">Unlock the app with Fingerprint, Face ID, PIN, Pattern and Password.</string>
|
||||||
<string name="biometric_prompt_description">This screen was closed due to multiple failed attempts. Please restart the application.</string>
|
<string name="biometric_prompt_description">After a few failed attempts, the prompt will close. Simply restart the app to try again.</string>
|
||||||
<string name="biometric_warning">Your CloudStream data has been backed up now. Although the possibility of this is very low, all devices can behave differently. In the rare case, that you get locked out from accessing the app, clear the app data completely and restore from a backup. We are very sorry for any inconvenience arising from this.</string>
|
<string name="biometric_warning">Your CloudStream data has been backed up now. Although the possibility of this is very low, all devices can behave differently. In the rare case, that you get locked out from accessing the app, clear the app data completely and restore from a backup. We are very sorry for any inconvenience arising from this.</string>
|
||||||
<string name="music_singlar">Music</string>
|
|
||||||
<string name="audio_book_singular">Audio Book</string>
|
|
||||||
<string name="custom_media_singluar">Media</string>
|
|
||||||
<string name="reset_btn">Reset</string>
|
<string name="reset_btn">Reset</string>
|
||||||
</resources>
|
</resources>
|
|
@ -1,6 +1,6 @@
|
||||||
#Fri Apr 30 17:11:15 CEST 2021
|
#Fri Apr 30 17:11:15 CEST 2021
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
Loading…
Reference in New Issue