mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-06 16:19:28 +00:00
Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay
This commit is contained in:
commit
7c1d0a7f88
11 changed files with 92 additions and 18 deletions
|
@ -16,6 +16,7 @@ class AddSourceOptionsActivity : AppCompatActivity() {
|
||||||
lateinit var _buttonBack: ImageButton;
|
lateinit var _buttonBack: ImageButton;
|
||||||
|
|
||||||
lateinit var _buttonQR: BigButton;
|
lateinit var _buttonQR: BigButton;
|
||||||
|
lateinit var _buttonBrowse: BigButton;
|
||||||
lateinit var _buttonURL: BigButton;
|
lateinit var _buttonURL: BigButton;
|
||||||
lateinit var _buttonPlugins: BigButton;
|
lateinit var _buttonPlugins: BigButton;
|
||||||
|
|
||||||
|
@ -56,6 +57,7 @@ class AddSourceOptionsActivity : AppCompatActivity() {
|
||||||
_buttonBack = findViewById(R.id.button_back);
|
_buttonBack = findViewById(R.id.button_back);
|
||||||
|
|
||||||
_buttonQR = findViewById(R.id.option_qr);
|
_buttonQR = findViewById(R.id.option_qr);
|
||||||
|
_buttonBrowse = findViewById(R.id.option_browse);
|
||||||
_buttonURL = findViewById(R.id.option_url);
|
_buttonURL = findViewById(R.id.option_url);
|
||||||
_buttonPlugins = findViewById(R.id.option_plugins);
|
_buttonPlugins = findViewById(R.id.option_plugins);
|
||||||
|
|
||||||
|
@ -74,6 +76,9 @@ class AddSourceOptionsActivity : AppCompatActivity() {
|
||||||
integrator.setCaptureActivity(QRCaptureActivity::class.java);
|
integrator.setCaptureActivity(QRCaptureActivity::class.java);
|
||||||
_qrCodeResultLauncher.launch(integrator.createScanIntent())
|
_qrCodeResultLauncher.launch(integrator.createScanIntent())
|
||||||
}
|
}
|
||||||
|
_buttonBrowse.onClick.subscribe {
|
||||||
|
startActivity(MainActivity.getTabIntent(this, "BROWSE_PLUGINS"));
|
||||||
|
}
|
||||||
|
|
||||||
_buttonURL.onClick.subscribe {
|
_buttonURL.onClick.subscribe {
|
||||||
UIDialogs.toast(this, getString(R.string.not_implemented_yet));
|
UIDialogs.toast(this, getString(R.string.not_implemented_yet));
|
||||||
|
|
|
@ -535,6 +535,9 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
navigate(_fragMainSources);
|
navigate(_fragMainSources);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
"BROWSE_PLUGINS" -> {
|
||||||
|
navigate(_fragBrowser, "https://plugins.grayjay.app");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@ package com.futo.platformplayer.api.media.platforms.js
|
||||||
|
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.constructs.Event0
|
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.DropdownFieldOptions
|
||||||
import com.futo.platformplayer.views.fields.FieldForm
|
import com.futo.platformplayer.views.fields.FieldForm
|
||||||
import com.futo.platformplayer.views.fields.FormField
|
import com.futo.platformplayer.views.fields.FormField
|
||||||
|
@ -55,7 +58,16 @@ class SourcePluginDescriptor {
|
||||||
onCaptchaChanged.emit();
|
onCaptchaChanged.emit();
|
||||||
}
|
}
|
||||||
fun getCaptchaData(): SourceCaptchaData? {
|
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?) {
|
fun updateAuth(str: SourceAuth?) {
|
||||||
|
@ -63,7 +75,16 @@ class SourcePluginDescriptor {
|
||||||
onAuthChanged.emit();
|
onAuthChanged.emit();
|
||||||
}
|
}
|
||||||
fun getAuth(): SourceAuth? {
|
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
|
@Serializable
|
||||||
|
|
|
@ -154,8 +154,14 @@ class ContentSearchResultsFragment : MainFragment() {
|
||||||
};
|
};
|
||||||
|
|
||||||
onSearch.subscribe(this) {
|
onSearch.subscribe(this) {
|
||||||
if(it.isHttpUrl())
|
if(it.isHttpUrl()) {
|
||||||
navigate<VideoDetailFragment>(it);
|
if(StatePlatform.instance.hasEnabledPlaylistClient(it))
|
||||||
|
navigate<PlaylistFragment>(it);
|
||||||
|
else if(StatePlatform.instance.hasEnabledChannelClient(it))
|
||||||
|
navigate<ChannelFragment>(it);
|
||||||
|
else
|
||||||
|
navigate<VideoDetailFragment>(it);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
setQuery(it, true);
|
setQuery(it, true);
|
||||||
};
|
};
|
||||||
|
|
|
@ -157,9 +157,12 @@ class HomeFragment : MainFragment() {
|
||||||
val dp10 = 10.dp(resources);
|
val dp10 = 10.dp(resources);
|
||||||
val dp30 = 30.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
|
//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) {
|
listOf(BigButton(context, "Browse Online Sources", "View official sources online", R.drawable.ic_explore) {
|
||||||
fragment.navigate<BrowserFragment>(BrowserFragment.NavigateOptions("https://plugins.grayjay.app/", mapOf(
|
fragment.navigate<BrowserFragment>(BrowserFragment.NavigateOptions("https://plugins.grayjay.app/", mapOf(
|
||||||
Pair("grayjay") { req ->
|
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
|
else
|
||||||
return NoResultsView(context, "Nothing to see here", "The enabled sources do not have any results.", R.drawable.ic_help,
|
return NoResultsView(context, "Nothing to see here", "The enabled sources do not have any results.", R.drawable.ic_help,
|
||||||
|
|
|
@ -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)
|
val clientIfExists = if(config.id != StateDeveloper.DEV_ID)
|
||||||
StatePlugins.instance.getPlugin(config.id);
|
StatePlugins.instance.getPlugin(config.id);
|
||||||
else null;
|
else null;
|
||||||
groups.add(
|
groups.add(
|
||||||
BigButtonGroup(c, context.getString(R.string.management),
|
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();
|
uninstallSource();
|
||||||
}.withBackground(R.drawable.background_big_button_red).apply {
|
}.withBackground(R.drawable.background_big_button_red).apply {
|
||||||
this.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).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);
|
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)
|
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) {
|
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);
|
_sourceButtons.addView(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
val isEmbedded = StatePlugins.instance.getEmbeddedSources(context).any { it.key == config.id };
|
|
||||||
val advancedButtons = BigButtonGroup(c, "Advanced",
|
val advancedButtons = BigButtonGroup(c, "Advanced",
|
||||||
BigButton(c, "Edit Code", "Modify the source of this plugin", R.drawable.ic_code) {
|
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;
|
this.alpha = 0.5f;
|
||||||
},
|
},
|
||||||
if(isEmbedded) BigButton(c, "Reinstall", "Modify the source of this plugin", R.drawable.ic_refresh) {
|
if(isEmbedded) BigButton(c, "Reinstall", "Modify the source of this plugin", R.drawable.ic_refresh) {
|
||||||
StatePlugins.instance.updateEmbeddedPlugins(context, listOf(config.id), true);
|
val embeddedConfig = StatePlugins.instance.getEmbeddedPluginConfigFromID(context, config.id);
|
||||||
reloadSource(config.id);
|
|
||||||
UIDialogs.toast(context, "Embedded plugin reinstalled, may require refresh");
|
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 {
|
}.apply {
|
||||||
this.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).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);
|
setMargins(0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt(), 0, 0);
|
||||||
|
|
|
@ -424,6 +424,7 @@ class VideoDetailFragment : MainFragment {
|
||||||
changeOrientation(OrientationManager.Orientation.PORTRAIT);
|
changeOrientation(OrientationManager.Orientation.PORTRAIT);
|
||||||
}
|
}
|
||||||
isFullscreen = fullscreen;
|
isFullscreen = fullscreen;
|
||||||
|
_view?.allowMotion = !fullscreen;
|
||||||
}
|
}
|
||||||
private fun changeOrientation(orientation: OrientationManager.Orientation) {
|
private fun changeOrientation(orientation: OrientationManager.Orientation) {
|
||||||
Logger.i(TAG, "Orientation Change:" + orientation.name);
|
Logger.i(TAG, "Orientation Change:" + orientation.name);
|
||||||
|
|
|
@ -160,6 +160,13 @@ class StatePlugins {
|
||||||
val configJson = StateAssets.readAsset(context, assetConfigPath) ?: return null;
|
val configJson = StateAssets.readAsset(context, assetConfigPath) ?: return null;
|
||||||
return SourcePluginConfig.fromJson(configJson, "");
|
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 {
|
fun installEmbeddedPlugin(context: Context, assetConfigPath: String, id: String? = null): Boolean {
|
||||||
try {
|
try {
|
||||||
val configJson = StateAssets.readAsset(context, assetConfigPath) ?:
|
val configJson = StateAssets.readAsset(context, assetConfigPath) ?:
|
||||||
|
|
|
@ -691,7 +691,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
|
|
||||||
val viewWidth = Math.min(metrics.widthPixels, metrics.heightPixels); //TODO: Get parent width. was this.width
|
val viewWidth = Math.min(metrics.widthPixels, metrics.heightPixels); //TODO: Get parent width. was this.width
|
||||||
val deviceHeight = Math.max(metrics.widthPixels, metrics.heightPixels);
|
val deviceHeight = Math.max(metrics.widthPixels, metrics.heightPixels);
|
||||||
val maxHeight = deviceHeight * 0.6;
|
val maxHeight = deviceHeight * 0.4;
|
||||||
|
|
||||||
val determinedHeight = if(w > h)
|
val determinedHeight = if(w > h)
|
||||||
((h * (viewWidth.toDouble() / w)).toInt())
|
((h * (viewWidth.toDouble() / w)).toInt())
|
||||||
|
|
|
@ -55,6 +55,15 @@
|
||||||
app:buttonText="@string/install_by_qr"
|
app:buttonText="@string/install_by_qr"
|
||||||
app:buttonSubText="@string/install_a_plugin_by_scanning_a_qr_code"
|
app:buttonSubText="@string/install_a_plugin_by_scanning_a_qr_code"
|
||||||
app:buttonIcon="@drawable/ic_qr" />
|
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
|
<com.futo.platformplayer.views.buttons.BigButton
|
||||||
android:id="@+id/option_url"
|
android:id="@+id/option_url"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -128,13 +128,17 @@
|
||||||
tools:text="(7 playlists, 85 videos)"
|
tools:text="(7 playlists, 85 videos)"
|
||||||
/>
|
/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<LinearLayout
|
<HorizontalScrollView
|
||||||
android:orientation="horizontal"
|
|
||||||
android:id="@+id/downloads_playlist_list"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
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>
|
</LinearLayout>
|
||||||
|
|
||||||
<!--Videos-->
|
<!--Videos-->
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue