Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay

This commit is contained in:
Koen 2024-01-17 11:02:13 +01:00
commit 7c1d0a7f88
11 changed files with 92 additions and 18 deletions

View file

@ -16,6 +16,7 @@ class AddSourceOptionsActivity : AppCompatActivity() {
lateinit var _buttonBack: ImageButton;
lateinit var _buttonQR: BigButton;
lateinit var _buttonBrowse: BigButton;
lateinit var _buttonURL: BigButton;
lateinit var _buttonPlugins: BigButton;
@ -56,6 +57,7 @@ class AddSourceOptionsActivity : AppCompatActivity() {
_buttonBack = findViewById(R.id.button_back);
_buttonQR = findViewById(R.id.option_qr);
_buttonBrowse = findViewById(R.id.option_browse);
_buttonURL = findViewById(R.id.option_url);
_buttonPlugins = findViewById(R.id.option_plugins);
@ -74,6 +76,9 @@ class AddSourceOptionsActivity : AppCompatActivity() {
integrator.setCaptureActivity(QRCaptureActivity::class.java);
_qrCodeResultLauncher.launch(integrator.createScanIntent())
}
_buttonBrowse.onClick.subscribe {
startActivity(MainActivity.getTabIntent(this, "BROWSE_PLUGINS"));
}
_buttonURL.onClick.subscribe {
UIDialogs.toast(this, getString(R.string.not_implemented_yet));

View file

@ -535,6 +535,9 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
navigate(_fragMainSources);
}
};
"BROWSE_PLUGINS" -> {
navigate(_fragBrowser, "https://plugins.grayjay.app");
}
}
}
}

View file

@ -2,6 +2,9 @@ package com.futo.platformplayer.api.media.platforms.js
import com.futo.platformplayer.R
import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.AnnouncementType
import com.futo.platformplayer.states.StateAnnouncement
import com.futo.platformplayer.views.fields.DropdownFieldOptions
import com.futo.platformplayer.views.fields.FieldForm
import com.futo.platformplayer.views.fields.FormField
@ -55,7 +58,16 @@ class SourcePluginDescriptor {
onCaptchaChanged.emit();
}
fun getCaptchaData(): SourceCaptchaData? {
return SourceCaptchaData.fromEncrypted(captchaEncrypted);
try {
return SourceCaptchaData.fromEncrypted(captchaEncrypted);
}
catch(ex: Throwable) {
Logger.e("SourcePluginDescriptor", "Captcha decode failed, disabling auth.", ex);
StateAnnouncement.instance.registerAnnouncement("CAP_BROKEN_" + config.id,
"Captcha corrupted for plugin [${config.name}]",
"Something went wrong in the stored captcha, you'll have to login again", AnnouncementType.SESSION);
return null;
}
}
fun updateAuth(str: SourceAuth?) {
@ -63,7 +75,16 @@ class SourcePluginDescriptor {
onAuthChanged.emit();
}
fun getAuth(): SourceAuth? {
return SourceAuth.fromEncrypted(authEncrypted);
try {
return SourceAuth.fromEncrypted(authEncrypted);
}
catch(ex: Throwable) {
Logger.e("SourcePluginDescriptor", "Authentication decode failed, disabling auth.", ex);
StateAnnouncement.instance.registerAnnouncement("AUTH_BROKEN_" + config.id,
"Authentication corrupted for plugin [${config.name}]",
"Something went wrong in the stored authentication, you'll have to login again", AnnouncementType.SESSION);
return null;
}
}
@Serializable

View file

@ -154,8 +154,14 @@ class ContentSearchResultsFragment : MainFragment() {
};
onSearch.subscribe(this) {
if(it.isHttpUrl())
navigate<VideoDetailFragment>(it);
if(it.isHttpUrl()) {
if(StatePlatform.instance.hasEnabledPlaylistClient(it))
navigate<PlaylistFragment>(it);
else if(StatePlatform.instance.hasEnabledChannelClient(it))
navigate<ChannelFragment>(it);
else
navigate<VideoDetailFragment>(it);
}
else
setQuery(it, true);
};

View file

@ -157,9 +157,12 @@ class HomeFragment : MainFragment() {
val dp10 = 10.dp(resources);
val dp30 = 30.dp(resources);
if(!StatePlatform.instance.getEnabledClients().isEmpty())
val pluginsExist = StatePlatform.instance.getAvailableClients().isNotEmpty();
if(StatePlatform.instance.getEnabledClients().isEmpty())
//Initial setup
return NoResultsView(context, "You have no Sources", "Enable or install some sources", R.drawable.ic_sources,
return NoResultsView(context, "No enabled Sources", if(pluginsExist)
"Enable or install some Sources"
else "This Grayjay version comes without any sources, install sources externally or using the button below.", R.drawable.ic_sources,
listOf(BigButton(context, "Browse Online Sources", "View official sources online", R.drawable.ic_explore) {
fragment.navigate<BrowserFragment>(BrowserFragment.NavigateOptions("https://plugins.grayjay.app/", mapOf(
Pair("grayjay") { req ->
@ -170,7 +173,10 @@ class HomeFragment : MainFragment() {
};
}
)));
}.withMargin(dp10, dp30).withBackground(R.drawable.background_big_primary))
}.withMargin(dp10, dp30),
if(pluginsExist) BigButton(context, "Sources", "Go to the sources tab", R.drawable.ic_creators) {
fragment.navigate<SourcesFragment>();
}.withMargin(dp10, dp30) else null).filterNotNull()
);
else
return NoResultsView(context, "Nothing to see here", "The enabled sources do not have any results.", R.drawable.ic_help,

View file

@ -295,17 +295,24 @@ class SourceDetailFragment : MainFragment() {
}
}
val isEmbedded = StatePlugins.instance.getEmbeddedSources(context).any { it.key == config.id };
val clientIfExists = if(config.id != StateDeveloper.DEV_ID)
StatePlugins.instance.getPlugin(config.id);
else null;
groups.add(
BigButtonGroup(c, context.getString(R.string.management),
BigButton(c, context.getString(R.string.uninstall), context.getString(R.string.removes_the_plugin_from_the_app), R.drawable.ic_block) {
if(!isEmbedded) BigButton(c, context.getString(R.string.uninstall), context.getString(R.string.removes_the_plugin_from_the_app), R.drawable.ic_block) {
uninstallSource();
}.withBackground(R.drawable.background_big_button_red).apply {
this.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
setMargins(0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt(), 0, 0);
};
} else BigButton(c, context.getString(R.string.uninstall), "Cannot uninstall embedded plugins", R.drawable.ic_block, {}).apply {
this.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
setMargins(0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt(), 0, 0);
};
this.alpha = 0.5f
},
if(clientIfExists?.captchaEncrypted != null)
BigButton(c, context.getString(R.string.delete_captcha), context.getString(R.string.deletes_stored_captcha_answer_for_this_plugin), R.drawable.ic_block) {
@ -325,7 +332,6 @@ class SourceDetailFragment : MainFragment() {
_sourceButtons.addView(group);
}
val isEmbedded = StatePlugins.instance.getEmbeddedSources(context).any { it.key == config.id };
val advancedButtons = BigButtonGroup(c, "Advanced",
BigButton(c, "Edit Code", "Modify the source of this plugin", R.drawable.ic_code) {
@ -333,9 +339,15 @@ class SourceDetailFragment : MainFragment() {
this.alpha = 0.5f;
},
if(isEmbedded) BigButton(c, "Reinstall", "Modify the source of this plugin", R.drawable.ic_refresh) {
StatePlugins.instance.updateEmbeddedPlugins(context, listOf(config.id), true);
reloadSource(config.id);
UIDialogs.toast(context, "Embedded plugin reinstalled, may require refresh");
val embeddedConfig = StatePlugins.instance.getEmbeddedPluginConfigFromID(context, config.id);
UIDialogs.showDialog(context, R.drawable.ic_warning_yellow, "Are you sure you want to downgrade (${config.version}=>${embeddedConfig?.version})?",
"This will revert the plugin back to the originally embedded version.\nVersion change: ${config.version}=>${embeddedConfig?.version}", null,
0, UIDialogs.Action("Cancel", {}), UIDialogs.Action("Reinstall", {
StatePlugins.instance.updateEmbeddedPlugins(context, listOf(config.id), true);
reloadSource(config.id);
UIDialogs.toast(context, "Embedded plugin reinstalled, may require refresh");
}, UIDialogs.ActionStyle.DANGEROUS));
}.apply {
this.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
setMargins(0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt(), 0, 0);

View file

@ -424,6 +424,7 @@ class VideoDetailFragment : MainFragment {
changeOrientation(OrientationManager.Orientation.PORTRAIT);
}
isFullscreen = fullscreen;
_view?.allowMotion = !fullscreen;
}
private fun changeOrientation(orientation: OrientationManager.Orientation) {
Logger.i(TAG, "Orientation Change:" + orientation.name);

View file

@ -160,6 +160,13 @@ class StatePlugins {
val configJson = StateAssets.readAsset(context, assetConfigPath) ?: return null;
return SourcePluginConfig.fromJson(configJson, "");
}
fun getEmbeddedPluginConfigFromID(context: Context, pluginId: String): SourcePluginConfig? {
val embedded = getEmbeddedSources(context);
if(!embedded.containsKey(pluginId))
return null;
return getEmbeddedPluginConfig(context, embedded[pluginId]!!);
}
fun installEmbeddedPlugin(context: Context, assetConfigPath: String, id: String? = null): Boolean {
try {
val configJson = StateAssets.readAsset(context, assetConfigPath) ?:

View file

@ -691,7 +691,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
val viewWidth = Math.min(metrics.widthPixels, metrics.heightPixels); //TODO: Get parent width. was this.width
val deviceHeight = Math.max(metrics.widthPixels, metrics.heightPixels);
val maxHeight = deviceHeight * 0.6;
val maxHeight = deviceHeight * 0.4;
val determinedHeight = if(w > h)
((h * (viewWidth.toDouble() / w)).toInt())

View file

@ -55,6 +55,15 @@
app:buttonText="@string/install_by_qr"
app:buttonSubText="@string/install_a_plugin_by_scanning_a_qr_code"
app:buttonIcon="@drawable/ic_qr" />
<com.futo.platformplayer.views.buttons.BigButton
android:id="@+id/option_browse"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
app:buttonText="Browse Online Sources"
app:buttonSubText="Install a plugin by browsing official plugins"
app:buttonIcon="@drawable/ic_explore" />
<com.futo.platformplayer.views.buttons.BigButton
android:id="@+id/option_url"
android:layout_width="match_parent"

View file

@ -128,13 +128,17 @@
tools:text="(7 playlists, 85 videos)"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:id="@+id/downloads_playlist_list"
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--Fill Programmatically-->
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:id="@+id/downloads_playlist_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!--Fill Programmatically-->
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
<!--Videos-->