mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-03 14:50:49 +00:00
Privacy mode, Handle 403s
This commit is contained in:
parent
f7581f8a65
commit
4e88a63809
16 changed files with 203 additions and 43 deletions
|
@ -13,6 +13,7 @@ import android.util.Log
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.ImageView
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
@ -29,7 +30,6 @@ import androidx.fragment.app.FragmentContainerView
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.futo.platformplayer.*
|
import com.futo.platformplayer.*
|
||||||
import com.futo.platformplayer.api.http.ManagedHttpClient
|
|
||||||
import com.futo.platformplayer.casting.StateCasting
|
import com.futo.platformplayer.casting.StateCasting
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.fragment.mainactivity.bottombar.MenuBottomBarFragment
|
import com.futo.platformplayer.fragment.mainactivity.bottombar.MenuBottomBarFragment
|
||||||
|
@ -42,7 +42,6 @@ import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment
|
||||||
import com.futo.platformplayer.listeners.OrientationManager
|
import com.futo.platformplayer.listeners.OrientationManager
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.models.ImportCache
|
import com.futo.platformplayer.models.ImportCache
|
||||||
import com.futo.platformplayer.models.UrlVideoWithTime
|
|
||||||
import com.futo.platformplayer.states.*
|
import com.futo.platformplayer.states.*
|
||||||
import com.futo.platformplayer.stores.FragmentedStorage
|
import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
import com.futo.platformplayer.stores.SubscriptionStorage
|
import com.futo.platformplayer.stores.SubscriptionStorage
|
||||||
|
@ -79,6 +78,9 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
private lateinit var _fragContainerVideoDetail: FragmentContainerView;
|
private lateinit var _fragContainerVideoDetail: FragmentContainerView;
|
||||||
private lateinit var _fragContainerOverlay: FrameLayout;
|
private lateinit var _fragContainerOverlay: FrameLayout;
|
||||||
|
|
||||||
|
//Views
|
||||||
|
private lateinit var _buttonIncognito: ImageView;
|
||||||
|
|
||||||
//Frags TopBar
|
//Frags TopBar
|
||||||
lateinit var _fragTopBarGeneral: GeneralTopBarFragment;
|
lateinit var _fragTopBarGeneral: GeneralTopBarFragment;
|
||||||
lateinit var _fragTopBarSearch: SearchTopBarFragment;
|
lateinit var _fragTopBarSearch: SearchTopBarFragment;
|
||||||
|
@ -204,6 +206,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
setNavigationBarColorAndIcons();
|
setNavigationBarColorAndIcons();
|
||||||
|
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
StatePlatform.instance.updateAvailableClients(this@MainActivity);
|
StatePlatform.instance.updateAvailableClients(this@MainActivity);
|
||||||
}
|
}
|
||||||
|
@ -290,6 +293,52 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
updateSegmentPaddings();
|
updateSegmentPaddings();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
_buttonIncognito = findViewById(R.id.incognito_button);
|
||||||
|
_buttonIncognito.elevation = -99f;
|
||||||
|
_buttonIncognito.alpha = 0f;
|
||||||
|
StateApp.instance.privateModeChanged.subscribe {
|
||||||
|
//Messing with visibility causes some issues with layout ordering?
|
||||||
|
if(it) {
|
||||||
|
_buttonIncognito.elevation = 99f;
|
||||||
|
_buttonIncognito.alpha = 1f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_buttonIncognito.elevation = -99f;
|
||||||
|
_buttonIncognito.alpha = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_buttonIncognito.setOnClickListener {
|
||||||
|
if(!StateApp.instance.privateMode)
|
||||||
|
return@setOnClickListener;
|
||||||
|
UIDialogs.showDialog(this, R.drawable.ic_disabled_visible_purple, "Disable Privacy Mode",
|
||||||
|
"Do you want to disable privacy mode? New videos will be tracked again.", null, 0,
|
||||||
|
UIDialogs.Action("Cancel", {
|
||||||
|
StateApp.instance.setPrivacyMode(true);
|
||||||
|
}, UIDialogs.ActionStyle.NONE),
|
||||||
|
UIDialogs.Action("Disable", {
|
||||||
|
StateApp.instance.setPrivacyMode(false);
|
||||||
|
}, UIDialogs.ActionStyle.DANGEROUS));
|
||||||
|
};
|
||||||
|
_fragVideoDetail.onFullscreenChanged.subscribe {
|
||||||
|
Logger.i(TAG, "onFullscreenChanged ${it}");
|
||||||
|
|
||||||
|
if(it) {
|
||||||
|
_buttonIncognito.elevation = -99f;
|
||||||
|
_buttonIncognito.alpha = 0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(StateApp.instance.privateMode) {
|
||||||
|
_buttonIncognito.elevation = 99f;
|
||||||
|
_buttonIncognito.alpha = 1f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_buttonIncognito.elevation = -99f;
|
||||||
|
_buttonIncognito.alpha = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StatePlayer.instance.also {
|
StatePlayer.instance.also {
|
||||||
it.onQueueChanged.subscribe { shouldSwapCurrentItem ->
|
it.onQueueChanged.subscribe { shouldSwapCurrentItem ->
|
||||||
if (!shouldSwapCurrentItem) {
|
if (!shouldSwapCurrentItem) {
|
||||||
|
|
|
@ -13,13 +13,15 @@ class PlatformClientPool {
|
||||||
private val _pool: HashMap<JSClient, Int> = hashMapOf();
|
private val _pool: HashMap<JSClient, Int> = hashMapOf();
|
||||||
private var _poolCounter = 0;
|
private var _poolCounter = 0;
|
||||||
private val _poolName: String?;
|
private val _poolName: String?;
|
||||||
|
private val _privatePool: Boolean;
|
||||||
|
|
||||||
var isDead: Boolean = false
|
var isDead: Boolean = false
|
||||||
private set;
|
private set;
|
||||||
val onDead = Event2<JSClient, PlatformClientPool>();
|
val onDead = Event2<JSClient, PlatformClientPool>();
|
||||||
|
|
||||||
constructor(parentClient: IPlatformClient, name: String? = null) {
|
constructor(parentClient: IPlatformClient, name: String? = null, privatePool: Boolean = false) {
|
||||||
_poolName = name;
|
_poolName = name;
|
||||||
|
_privatePool = privatePool;
|
||||||
if(parentClient !is JSClient)
|
if(parentClient !is JSClient)
|
||||||
throw IllegalArgumentException("Pooling only supported for JSClients right now");
|
throw IllegalArgumentException("Pooling only supported for JSClients right now");
|
||||||
Logger.i(TAG, "Pool for ${parentClient.name} was started");
|
Logger.i(TAG, "Pool for ${parentClient.name} was started");
|
||||||
|
@ -51,7 +53,7 @@ class PlatformClientPool {
|
||||||
reserved = _pool.keys.find { !it.isBusy };
|
reserved = _pool.keys.find { !it.isBusy };
|
||||||
if(reserved == null && _pool.size < capacity) {
|
if(reserved == null && _pool.size < capacity) {
|
||||||
Logger.i(TAG, "Started additional [${_parent.name}] client in pool [${_poolName}] (${_pool.size + 1}/${capacity})");
|
Logger.i(TAG, "Started additional [${_parent.name}] client in pool [${_poolName}] (${_pool.size + 1}/${capacity})");
|
||||||
reserved = _parent.getCopy();
|
reserved = _parent.getCopy(_privatePool);
|
||||||
|
|
||||||
reserved?.onCaptchaException?.subscribe { client, ex ->
|
reserved?.onCaptchaException?.subscribe { client, ex ->
|
||||||
StateApp.instance.handleCaptchaException(client, ex);
|
StateApp.instance.handleCaptchaException(client, ex);
|
||||||
|
|
|
@ -6,12 +6,14 @@ class PlatformMultiClientPool {
|
||||||
private val _clientPools: HashMap<IPlatformClient, PlatformClientPool> = hashMapOf();
|
private val _clientPools: HashMap<IPlatformClient, PlatformClientPool> = hashMapOf();
|
||||||
|
|
||||||
private var _isFake = false;
|
private var _isFake = false;
|
||||||
|
private var _privatePool = false;
|
||||||
|
|
||||||
constructor(name: String, maxCap: Int = -1) {
|
constructor(name: String, maxCap: Int = -1, isPrivatePool: Boolean = false) {
|
||||||
_name = name;
|
_name = name;
|
||||||
_maxCap = if(maxCap > 0)
|
_maxCap = if(maxCap > 0)
|
||||||
maxCap
|
maxCap
|
||||||
else 99;
|
else 99;
|
||||||
|
_privatePool = isPrivatePool;
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getClientPooled(parentClient: IPlatformClient, capacity: Int = _maxCap): IPlatformClient {
|
fun getClientPooled(parentClient: IPlatformClient, capacity: Int = _maxCap): IPlatformClient {
|
||||||
|
@ -19,7 +21,7 @@ class PlatformMultiClientPool {
|
||||||
return parentClient;
|
return parentClient;
|
||||||
val pool = synchronized(_clientPools) {
|
val pool = synchronized(_clientPools) {
|
||||||
if(!_clientPools.containsKey(parentClient))
|
if(!_clientPools.containsKey(parentClient))
|
||||||
_clientPools[parentClient] = PlatformClientPool(parentClient, _name).apply {
|
_clientPools[parentClient] = PlatformClientPool(parentClient, _name, _privatePool).apply {
|
||||||
this.onDead.subscribe { _, pool ->
|
this.onDead.subscribe { _, pool ->
|
||||||
synchronized(_clientPools) {
|
synchronized(_clientPools) {
|
||||||
if(_clientPools[parentClient] == pool)
|
if(_clientPools[parentClient] == pool)
|
||||||
|
|
|
@ -54,8 +54,8 @@ class DevJSClient : JSClient {
|
||||||
return DevJSClient(context, config, _devScript, _auth, _captcha, devID, descriptor.settings);
|
return DevJSClient(context, config, _devScript, _auth, _captcha, devID, descriptor.settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCopy(): JSClient {
|
override fun getCopy(privateCopy: Boolean): JSClient {
|
||||||
return DevJSClient(_context, descriptor, _script, _auth, _captcha, saveState(), devID);
|
return DevJSClient(_context, descriptor, _script, if(!privateCopy) _auth else null, _captcha, saveState(), devID);
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initialize() {
|
override fun initialize() {
|
||||||
|
|
|
@ -164,13 +164,16 @@ open class JSClient : IPlatformClient {
|
||||||
|
|
||||||
_plugin.changeAllowDevSubmit(descriptor.appSettings.allowDeveloperSubmit);
|
_plugin.changeAllowDevSubmit(descriptor.appSettings.allowDeveloperSubmit);
|
||||||
}
|
}
|
||||||
constructor(context: Context, descriptor: SourcePluginDescriptor, saveState: String?, script: String) {
|
constructor(context: Context, descriptor: SourcePluginDescriptor, saveState: String?, script: String, withoutCredentials: Boolean = false) {
|
||||||
this._context = context;
|
this._context = context;
|
||||||
this.config = descriptor.config;
|
this.config = descriptor.config;
|
||||||
icon = StatePlatform.instance.getPlatformIcon(config.id) ?: ImageVariable(config.absoluteIconUrl, null, null);
|
icon = StatePlatform.instance.getPlatformIcon(config.id) ?: ImageVariable(config.absoluteIconUrl, null, null);
|
||||||
this.descriptor = descriptor;
|
this.descriptor = descriptor;
|
||||||
_injectedSaveState = saveState;
|
_injectedSaveState = saveState;
|
||||||
|
if(!withoutCredentials)
|
||||||
_auth = descriptor.getAuth();
|
_auth = descriptor.getAuth();
|
||||||
|
else
|
||||||
|
_auth = null;
|
||||||
_captcha = descriptor.getCaptchaData();
|
_captcha = descriptor.getCaptchaData();
|
||||||
flags = descriptor.flags.toTypedArray();
|
flags = descriptor.flags.toTypedArray();
|
||||||
|
|
||||||
|
@ -190,8 +193,8 @@ open class JSClient : IPlatformClient {
|
||||||
_plugin.changeAllowDevSubmit(descriptor.appSettings.allowDeveloperSubmit);
|
_plugin.changeAllowDevSubmit(descriptor.appSettings.allowDeveloperSubmit);
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getCopy(): JSClient {
|
open fun getCopy(withoutCredentials: Boolean = false): JSClient {
|
||||||
return JSClient(_context, descriptor, saveState(), _script);
|
return JSClient(_context, descriptor, saveState(), _script, withoutCredentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUnderlyingPlugin(): V8Plugin {
|
fun getUnderlyingPlugin(): V8Plugin {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import androidx.core.animation.doOnEnd
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.Settings
|
import com.futo.platformplayer.Settings
|
||||||
|
import com.futo.platformplayer.UIDialogs
|
||||||
import com.futo.platformplayer.activities.MainActivity
|
import com.futo.platformplayer.activities.MainActivity
|
||||||
import com.futo.platformplayer.activities.SettingsActivity
|
import com.futo.platformplayer.activities.SettingsActivity
|
||||||
import com.futo.platformplayer.dp
|
import com.futo.platformplayer.dp
|
||||||
|
@ -222,6 +223,13 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
||||||
buttons.removeAt(faqIndex)
|
buttons.removeAt(faqIndex)
|
||||||
buttons.add(if (buttons.size == 1) 1 else 0, button)
|
buttons.add(if (buttons.size == 1) 1 else 0, button)
|
||||||
}
|
}
|
||||||
|
//Force privacy to be third
|
||||||
|
val privacyIndex = buttons.indexOfFirst { b -> b.id == 96 };
|
||||||
|
if (privacyIndex != -1) {
|
||||||
|
val button = buttons[privacyIndex]
|
||||||
|
buttons.removeAt(privacyIndex)
|
||||||
|
buttons.add(if (buttons.size == 2) 2 else 1, button)
|
||||||
|
}
|
||||||
|
|
||||||
for (data in buttons) {
|
for (data in buttons) {
|
||||||
val button = MenuButton(context, data, _fragment, true);
|
val button = MenuButton(context, data, _fragment, true);
|
||||||
|
@ -305,6 +313,16 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
||||||
newCurrentButtonDefinitions.add(ButtonDefinition(97, R.drawable.ic_quiz, R.drawable.ic_quiz_fill, R.string.faq, canToggle = false, { false }, {
|
newCurrentButtonDefinitions.add(ButtonDefinition(97, R.drawable.ic_quiz, R.drawable.ic_quiz_fill, R.string.faq, canToggle = false, { false }, {
|
||||||
it.navigate<BrowserFragment>(Settings.URL_FAQ);
|
it.navigate<BrowserFragment>(Settings.URL_FAQ);
|
||||||
}))
|
}))
|
||||||
|
newCurrentButtonDefinitions.add(ButtonDefinition(96, R.drawable.ic_disabled_visible, R.drawable.ic_disabled_visible, R.string.privacy_mode, canToggle = false, { false }, {
|
||||||
|
UIDialogs.showDialog(context, R.drawable.ic_disabled_visible_purple, "Privacy Mode",
|
||||||
|
"All requests will be processed anonymously (unauthenticated), playback and history tracking will be disabled.\n\nTap the icon to disable.", null, 0,
|
||||||
|
UIDialogs.Action("Cancel", {
|
||||||
|
StateApp.instance.setPrivacyMode(false);
|
||||||
|
}, UIDialogs.ActionStyle.NONE),
|
||||||
|
UIDialogs.Action("Enable", {
|
||||||
|
StateApp.instance.setPrivacyMode(true);
|
||||||
|
}, UIDialogs.ActionStyle.PRIMARY));
|
||||||
|
}))
|
||||||
|
|
||||||
//Add conditional buttons here, when you add a conditional button, be sure to add the register and unregister events for when the button needs to be updated
|
//Add conditional buttons here, when you add a conditional button, be sure to add the register and unregister events for when the button needs to be updated
|
||||||
|
|
||||||
|
@ -370,7 +388,8 @@ class MenuBottomBarFragment : MainActivityFragment() {
|
||||||
c.overridePendingTransition(R.anim.slide_in_up, R.anim.slide_darken);
|
c.overridePendingTransition(R.anim.slide_in_up, R.anim.slide_darken);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
//98 is reversed for buy button
|
//96 is reserved for privacy button
|
||||||
|
//98 is reserved for buy button
|
||||||
//99 is reserved for more button
|
//99 is reserved for more button
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ class VideoDetailFragment : MainFragment {
|
||||||
private var _view : SingleViewTouchableMotionLayout? = null;
|
private var _view : SingleViewTouchableMotionLayout? = null;
|
||||||
|
|
||||||
var isFullscreen : Boolean = false;
|
var isFullscreen : Boolean = false;
|
||||||
|
val onFullscreenChanged = Event1<Boolean>();
|
||||||
var isTransitioning : Boolean = false
|
var isTransitioning : Boolean = false
|
||||||
private set;
|
private set;
|
||||||
var isInPictureInPicture : Boolean = false
|
var isInPictureInPicture : Boolean = false
|
||||||
|
@ -424,6 +425,7 @@ class VideoDetailFragment : MainFragment {
|
||||||
changeOrientation(OrientationManager.Orientation.PORTRAIT);
|
changeOrientation(OrientationManager.Orientation.PORTRAIT);
|
||||||
}
|
}
|
||||||
isFullscreen = fullscreen;
|
isFullscreen = fullscreen;
|
||||||
|
onFullscreenChanged.emit(isFullscreen);
|
||||||
_view?.allowMotion = !fullscreen;
|
_view?.allowMotion = !fullscreen;
|
||||||
}
|
}
|
||||||
private fun changeOrientation(orientation: OrientationManager.Orientation) {
|
private fun changeOrientation(orientation: OrientationManager.Orientation) {
|
||||||
|
|
|
@ -1239,6 +1239,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
if(StateApp.instance.privateMode) {
|
||||||
val stopwatch = com.futo.platformplayer.debug.Stopwatch()
|
val stopwatch = com.futo.platformplayer.debug.Stopwatch()
|
||||||
var tracker = video.getPlaybackTracker()
|
var tracker = video.getPlaybackTracker()
|
||||||
Logger.i(TAG, "video.getPlaybackTracker took ${stopwatch.elapsedMs}ms")
|
Logger.i(TAG, "video.getPlaybackTracker took ${stopwatch.elapsedMs}ms")
|
||||||
|
@ -1246,12 +1247,18 @@ class VideoDetailView : ConstraintLayout {
|
||||||
if (tracker == null) {
|
if (tracker == null) {
|
||||||
stopwatch.reset()
|
stopwatch.reset()
|
||||||
tracker = StatePlatform.instance.getPlaybackTracker(video.url);
|
tracker = StatePlatform.instance.getPlaybackTracker(video.url);
|
||||||
Logger.i(TAG, "StatePlatform.instance.getPlaybackTracker took ${stopwatch.elapsedMs}ms")
|
Logger.i(
|
||||||
|
TAG,
|
||||||
|
"StatePlatform.instance.getPlaybackTracker took ${stopwatch.elapsedMs}ms"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (me.video == video)
|
if (me.video == video)
|
||||||
me._playbackTracker = tracker;
|
me._playbackTracker = tracker;
|
||||||
}
|
}
|
||||||
|
else if(me.video == video)
|
||||||
|
me._playbackTracker = null;
|
||||||
|
}
|
||||||
catch(ex: Throwable) {
|
catch(ex: Throwable) {
|
||||||
Logger.e(TAG, "Playback tracker failed", ex);
|
Logger.e(TAG, "Playback tracker failed", ex);
|
||||||
if(me.video?.isLive == true) withContext(Dispatchers.Main) {
|
if(me.video?.isLive == true) withContext(Dispatchers.Main) {
|
||||||
|
@ -1454,6 +1461,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
StatePlayer.instance.startOrUpdateMediaSession(context, video);
|
StatePlayer.instance.startOrUpdateMediaSession(context, video);
|
||||||
StatePlayer.instance.setCurrentlyPlaying(video);
|
StatePlayer.instance.setCurrentlyPlaying(video);
|
||||||
|
|
||||||
|
_liveChat?.stop();
|
||||||
|
_liveChat = null;
|
||||||
if(video.isLive && video.live != null) {
|
if(video.isLive && video.live != null) {
|
||||||
loadLiveChat(video);
|
loadLiveChat(video);
|
||||||
}
|
}
|
||||||
|
@ -1663,6 +1672,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
if(_didTriggerDatasourceErrroCount <= 3) {
|
if(_didTriggerDatasourceErrroCount <= 3) {
|
||||||
_didTriggerDatasourceError = true;
|
_didTriggerDatasourceError = true;
|
||||||
_didTriggerDatasourceErrroCount++;
|
_didTriggerDatasourceErrroCount++;
|
||||||
|
|
||||||
UIDialogs.toast("Block detected, attempting bypass");
|
UIDialogs.toast("Block detected, attempting bypass");
|
||||||
|
|
||||||
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
@ -1683,7 +1693,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
}
|
||||||
|
else if(_didTriggerDatasourceErrroCount > 3) {
|
||||||
UIDialogs.showDialog(context, R.drawable.ic_error_pred,
|
UIDialogs.showDialog(context, R.drawable.ic_error_pred,
|
||||||
context.getString(R.string.media_error),
|
context.getString(R.string.media_error),
|
||||||
context.getString(R.string.the_media_source_encountered_an_unauthorized_error_this_might_be_solved_by_a_plugin_reload_would_you_like_to_reload_experimental),
|
context.getString(R.string.the_media_source_encountered_an_unauthorized_error_this_might_be_solved_by_a_plugin_reload_would_you_like_to_reload_experimental),
|
||||||
|
@ -1701,7 +1712,6 @@ class VideoDetailView : ConstraintLayout {
|
||||||
}
|
}
|
||||||
}, UIDialogs.ActionStyle.PRIMARY)
|
}, UIDialogs.ActionStyle.PRIMARY)
|
||||||
);
|
);
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import com.futo.platformplayer.api.media.platforms.js.JSClient
|
||||||
import com.futo.platformplayer.background.BackgroundWorker
|
import com.futo.platformplayer.background.BackgroundWorker
|
||||||
import com.futo.platformplayer.casting.StateCasting
|
import com.futo.platformplayer.casting.StateCasting
|
||||||
import com.futo.platformplayer.constructs.Event0
|
import com.futo.platformplayer.constructs.Event0
|
||||||
|
import com.futo.platformplayer.constructs.Event1
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
|
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
|
||||||
import com.futo.platformplayer.fragment.mainactivity.main.HomeFragment
|
import com.futo.platformplayer.fragment.mainactivity.main.HomeFragment
|
||||||
import com.futo.platformplayer.fragment.mainactivity.main.SourceDetailFragment
|
import com.futo.platformplayer.fragment.mainactivity.main.SourceDetailFragment
|
||||||
|
@ -56,6 +57,18 @@ class StateApp {
|
||||||
|
|
||||||
val sessionId = UUID.randomUUID().toString();
|
val sessionId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
var privateMode: Boolean = false
|
||||||
|
get(){
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
private set(value) {
|
||||||
|
field = value;
|
||||||
|
}
|
||||||
|
val privateModeChanged = Event1<Boolean>();
|
||||||
|
fun setPrivacyMode(value: Boolean) {
|
||||||
|
privateMode = value;
|
||||||
|
privateModeChanged.emit(privateMode);
|
||||||
|
}
|
||||||
|
|
||||||
fun getExternalGeneralDirectory(context: Context): DocumentFile? {
|
fun getExternalGeneralDirectory(context: Context): DocumentFile? {
|
||||||
val generalUri = Settings.instance.storage.getStorageGeneralUri();
|
val generalUri = Settings.instance.storage.getStorageGeneralUri();
|
||||||
|
|
|
@ -96,6 +96,8 @@ class StateHistory {
|
||||||
return historyIndex[url];
|
return historyIndex[url];
|
||||||
}
|
}
|
||||||
fun getHistoryByVideo(video: IPlatformVideo, create: Boolean = false, watchDate: OffsetDateTime? = null): DBHistory.Index? {
|
fun getHistoryByVideo(video: IPlatformVideo, create: Boolean = false, watchDate: OffsetDateTime? = null): DBHistory.Index? {
|
||||||
|
if(StateApp.instance.privateMode)
|
||||||
|
return null;
|
||||||
val existing = historyIndex[video.url];
|
val existing = historyIndex[video.url];
|
||||||
var result: DBHistory.Index? = null;
|
var result: DBHistory.Index? = null;
|
||||||
if(existing != null) {
|
if(existing != null) {
|
||||||
|
|
|
@ -93,6 +93,7 @@ class StatePlatform {
|
||||||
private val _channelClientPool = PlatformMultiClientPool("Channels", 15); //Used primarily for subscription/background channel fetches
|
private val _channelClientPool = PlatformMultiClientPool("Channels", 15); //Used primarily for subscription/background channel fetches
|
||||||
private val _trackerClientPool = PlatformMultiClientPool("Trackers", 1); //Used exclusively for playback trackers
|
private val _trackerClientPool = PlatformMultiClientPool("Trackers", 1); //Used exclusively for playback trackers
|
||||||
private val _liveEventClientPool = PlatformMultiClientPool("LiveEvents", 1); //Used exclusively for live events
|
private val _liveEventClientPool = PlatformMultiClientPool("LiveEvents", 1); //Used exclusively for live events
|
||||||
|
private val _privateClientPool = PlatformMultiClientPool("Private", 2); //Used primarily for calls if in incognito mode
|
||||||
|
|
||||||
|
|
||||||
private val _icons : HashMap<String, ImageVariable> = HashMap();
|
private val _icons : HashMap<String, ImageVariable> = HashMap();
|
||||||
|
@ -109,13 +110,22 @@ class StatePlatform {
|
||||||
//Batched Requests
|
//Batched Requests
|
||||||
private val _batchTaskGetVideoDetails: BatchedTaskHandler<String, IPlatformContentDetails> = BatchedTaskHandler<String, IPlatformContentDetails>(_scope,
|
private val _batchTaskGetVideoDetails: BatchedTaskHandler<String, IPlatformContentDetails> = BatchedTaskHandler<String, IPlatformContentDetails>(_scope,
|
||||||
{ url ->
|
{ url ->
|
||||||
|
|
||||||
Logger.i(StatePlatform::class.java.name, "Fetching video details [${url}]");
|
Logger.i(StatePlatform::class.java.name, "Fetching video details [${url}]");
|
||||||
|
if(!StateApp.instance.privateMode) {
|
||||||
_enabledClients.find { it.isContentDetailsUrl(url) }?.let {
|
_enabledClients.find { it.isContentDetailsUrl(url) }?.let {
|
||||||
_mainClientPool.getClientPooled(it).getContentDetails(url)
|
_mainClientPool.getClientPooled(it).getContentDetails(url)
|
||||||
} ?: throw NoPlatformClientException("No client enabled that supports this url ($url)");
|
}
|
||||||
|
?: throw NoPlatformClientException("No client enabled that supports this url ($url)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_enabledClients.find { it.isContentDetailsUrl(url) }?.let {
|
||||||
|
_privateClientPool.getClientPooled(it).getContentDetails(url)
|
||||||
|
}
|
||||||
|
?: throw NoPlatformClientException("No client enabled that supports this url ($url)");
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
if(!Settings.instance.browsing.videoCache)
|
if(!Settings.instance.browsing.videoCache || StateApp.instance.privateMode)
|
||||||
return@BatchedTaskHandler null;
|
return@BatchedTaskHandler null;
|
||||||
else {
|
else {
|
||||||
val cached = synchronized(_cache) { _cache.get(it); } ?: return@BatchedTaskHandler null;
|
val cached = synchronized(_cache) { _cache.get(it); } ?: return@BatchedTaskHandler null;
|
||||||
|
@ -131,7 +141,7 @@ class StatePlatform {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ para, result ->
|
{ para, result ->
|
||||||
if(!Settings.instance.browsing.videoCache || (result is IPlatformVideo && result.isLive))
|
if(!Settings.instance.browsing.videoCache || (result is IPlatformVideo && result.isLive) || StateApp.instance.privateMode)
|
||||||
return@BatchedTaskHandler
|
return@BatchedTaskHandler
|
||||||
else {
|
else {
|
||||||
Logger.i(TAG, "Caching [${para}]");
|
Logger.i(TAG, "Caching [${para}]");
|
||||||
|
@ -871,7 +881,10 @@ class StatePlatform {
|
||||||
if(!client.capabilities.hasGetComments)
|
if(!client.capabilities.hasGetComments)
|
||||||
return EmptyPager();
|
return EmptyPager();
|
||||||
|
|
||||||
|
if(!StateApp.instance.privateMode)
|
||||||
return client.fromPool(_mainClientPool).getComments(url);
|
return client.fromPool(_mainClientPool).getComments(url);
|
||||||
|
else
|
||||||
|
return client.fromPool(_privateClientPool).getComments(url);
|
||||||
}
|
}
|
||||||
fun getSubComments(comment: IPlatformComment): IPager<IPlatformComment> {
|
fun getSubComments(comment: IPlatformComment): IPager<IPlatformComment> {
|
||||||
Logger.i(TAG, "Platform - getSubComments");
|
Logger.i(TAG, "Platform - getSubComments");
|
||||||
|
@ -882,7 +895,11 @@ class StatePlatform {
|
||||||
fun getLiveEvents(url: String): IPager<IPlatformLiveEvent>? {
|
fun getLiveEvents(url: String): IPager<IPlatformLiveEvent>? {
|
||||||
Logger.i(TAG, "Platform - getLiveChat");
|
Logger.i(TAG, "Platform - getLiveChat");
|
||||||
var client = getContentClient(url);
|
var client = getContentClient(url);
|
||||||
|
|
||||||
|
if(!StateApp.instance.privateMode)
|
||||||
return client.fromPool(_liveEventClientPool).getLiveEvents(url);
|
return client.fromPool(_liveEventClientPool).getLiveEvents(url);
|
||||||
|
else
|
||||||
|
return client.fromPool(_privateClientPool).getLiveEvents(url);
|
||||||
}
|
}
|
||||||
fun getLiveChatWindow(url: String): ILiveChatWindowDescriptor? {
|
fun getLiveChatWindow(url: String): ILiveChatWindowDescriptor? {
|
||||||
Logger.i(TAG, "Platform - getLiveChat");
|
Logger.i(TAG, "Platform - getLiveChat");
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#CC111111" />
|
||||||
|
<corners android:radius="100dp" />
|
||||||
|
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||||
|
</shape>
|
9
app/src/main/res/drawable/ic_disabled_visible.xml
Normal file
9
app/src/main/res/drawable/ic_disabled_visible.xml
Normal file
|
@ -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="@android:color/white"
|
||||||
|
android:pathData="M450,879Q372,873 304.5,840Q237,807 187,753.5Q137,700 108.5,629.5Q80,559 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,485 880,489.5Q880,494 880,499Q863,488 840.5,477.5Q818,467 799,460Q791,334 699.5,247Q608,160 480,160Q424,160 374.5,178Q325,196 284,228L529,473Q510,481 492.5,491.5Q475,502 458,514L228,284Q196,325 178,374.5Q160,424 160,480Q160,579 213.5,657.5Q267,736 352,773Q370,801 397,830Q424,859 450,879ZM680,800Q739,800 789.5,773Q840,746 870,700Q840,654 789.5,627Q739,600 680,600Q621,600 570.5,627Q520,654 490,700Q520,746 570.5,773Q621,800 680,800ZM680,880Q584,880 508.5,829.5Q433,779 400,700Q433,621 508.5,570.5Q584,520 680,520Q776,520 851.5,570.5Q927,621 960,700Q927,779 851.5,829.5Q776,880 680,880ZM680,760Q655,760 637.5,742.5Q620,725 620,700Q620,675 637.5,657.5Q655,640 680,640Q705,640 722.5,657.5Q740,675 740,700Q740,725 722.5,742.5Q705,760 680,760Z"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_disabled_visible_purple.xml
Normal file
9
app/src/main/res/drawable/ic_disabled_visible_purple.xml
Normal file
|
@ -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="#635DAC"
|
||||||
|
android:pathData="M450,879Q372,873 304.5,840Q237,807 187,753.5Q137,700 108.5,629.5Q80,559 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,485 880,489.5Q880,494 880,499Q863,488 840.5,477.5Q818,467 799,460Q791,334 699.5,247Q608,160 480,160Q424,160 374.5,178Q325,196 284,228L529,473Q510,481 492.5,491.5Q475,502 458,514L228,284Q196,325 178,374.5Q160,424 160,480Q160,579 213.5,657.5Q267,736 352,773Q370,801 397,830Q424,859 450,879ZM680,800Q739,800 789.5,773Q840,746 870,700Q840,654 789.5,627Q739,600 680,600Q621,600 570.5,627Q520,654 490,700Q520,746 570.5,773Q621,800 680,800ZM680,880Q584,880 508.5,829.5Q433,779 400,700Q433,621 508.5,570.5Q584,520 680,520Q776,520 851.5,570.5Q927,621 960,700Q927,779 851.5,829.5Q776,880 680,880ZM680,760Q655,760 637.5,742.5Q620,725 620,700Q620,675 637.5,657.5Q655,640 680,640Q705,640 722.5,657.5Q740,675 740,700Q740,725 722.5,742.5Q705,760 680,760Z"/>
|
||||||
|
</vector>
|
|
@ -70,6 +70,21 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:elevation="15dp">
|
android:elevation="15dp">
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/incognito_button"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:src="@drawable/ic_disabled_visible_purple"
|
||||||
|
android:background="@drawable/background_button_round_black"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:elevation="50dp"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/toast_view" />
|
||||||
|
|
||||||
<com.futo.platformplayer.views.ToastView
|
<com.futo.platformplayer.views.ToastView
|
||||||
android:id="@+id/toast_view"
|
android:id="@+id/toast_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -79,4 +94,5 @@
|
||||||
app:layout_constraintLeft_toLeftOf="@id/fragment_main"
|
app:layout_constraintLeft_toLeftOf="@id/fragment_main"
|
||||||
app:layout_constraintRight_toRightOf="@id/fragment_main"
|
app:layout_constraintRight_toRightOf="@id/fragment_main"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/fragment_main" />
|
app:layout_constraintBottom_toBottomOf="@id/fragment_main" />
|
||||||
|
|
||||||
</androidx.constraintlayout.motion.widget.MotionLayout>
|
</androidx.constraintlayout.motion.widget.MotionLayout>
|
|
@ -25,6 +25,7 @@
|
||||||
<string name="sources">Sources</string>
|
<string name="sources">Sources</string>
|
||||||
<string name="buy">Buy</string>
|
<string name="buy">Buy</string>
|
||||||
<string name="faq">FAQ</string>
|
<string name="faq">FAQ</string>
|
||||||
|
<string name="privacy_mode">Privacy Mode</string>
|
||||||
<string name="the_top_source_will_be_considered_primary">The top source will be considered primary</string>
|
<string name="the_top_source_will_be_considered_primary">The top source will be considered primary</string>
|
||||||
<string name="defaults">Defaults</string>
|
<string name="defaults">Defaults</string>
|
||||||
<string name="home_screen">Home Screen</string>
|
<string name="home_screen">Home Screen</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue