batch download plugins

This commit is contained in:
reduplicated 2022-08-08 01:03:54 +02:00
parent 4e6bbf3908
commit 32d9aea02c
10 changed files with 150 additions and 29 deletions

View file

@ -6,6 +6,7 @@ import android.content.Context
import android.content.pm.PackageManager
import android.content.res.Resources
import android.os.Build
import android.os.Looper
import android.util.Log
import android.view.*
import android.widget.TextView
@ -17,11 +18,13 @@ import androidx.preference.PreferenceManager
import com.google.android.gms.cast.framework.CastSession
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.player.PlayerEventType
import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.hasPIPPermission
import com.lagradost.cloudstream3.utils.UIHelper.shouldShowPIPMode
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.coroutines.currentCoroutineContext
import org.schabi.newpipe.extractor.NewPipe
import java.util.*
@ -43,6 +46,13 @@ object CommonActivity {
var currentToast: Toast? = null
fun showToast(act: Activity?, text: UiText, duration: Int) {
if (act == null) return
text.asStringNull(act)?.let {
showToast(act, it, duration)
}
}
fun showToast(act: Activity?, @StringRes message: Int, duration: Int) {
if (act == null) return
showToast(act, act.getString(message), duration)

View file

@ -10,7 +10,7 @@ abstract class Plugin {
* @param context Context
*/
@Throws(Throwable::class)
open fun load(context: Context?) {
open fun load(context: Context) {
}
class Manifest {

View file

@ -126,10 +126,10 @@ class GeneratorPlayer : FullScreenPlayer() {
private fun loadExtractorJob(extractorLink: ExtractorLink?) {
currentVerifyLink?.cancel()
extractorLink?.let {
extractorLink?.let { link ->
currentVerifyLink = ioSafe {
if (it.extractorData != null) {
getApiFromNameNull(it.source)?.extractorVerifierJob(it.extractorData)
if (link.extractorData != null) {
getApiFromNameNull(link.source)?.extractorVerifierJob(link.extractorData)
}
}
}

View file

@ -41,6 +41,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.isConnectedToChromecast
import com.lagradost.cloudstream3.utils.CastHelper.startCast
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDub
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultEpisode
@ -689,7 +690,7 @@ class ResultViewModel2 : ViewModel() {
})
if (currentLinks.isEmpty()) {
Coroutines.main {
main {
showToast(
activity,
R.string.no_links_found_toast,
@ -698,7 +699,7 @@ class ResultViewModel2 : ViewModel() {
}
return@ioSafe
} else {
Coroutines.main {
main {
showToast(
activity,
R.string.download_started,

View file

@ -1,21 +1,14 @@
package com.lagradost.cloudstream3.ui.settings.extensions
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import android.view.*
import androidx.appcompat.view.menu.MenuBuilder
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import kotlinx.android.synthetic.main.fragment_extensions.*
import kotlinx.android.synthetic.main.fragment_plugins.*
const val PLUGINS_BUNDLE_NAME = "name"
const val PLUGINS_BUNDLE_URL = "url"
@ -26,7 +19,7 @@ class PluginsFragment : Fragment() {
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
return inflater.inflate(R.layout.fragment_extensions, container, false)
return inflater.inflate(R.layout.fragment_plugins, container, false)
}
private val pluginViewModel: PluginsViewModel by activityViewModels()
@ -37,20 +30,30 @@ class PluginsFragment : Fragment() {
val name = arguments?.getString(PLUGINS_BUNDLE_NAME)
val url = arguments?.getString(PLUGINS_BUNDLE_URL)
if (url == null) {
if (url == null || name == null) {
activity?.onBackPressed()
return
}
setUpToolbar(name ?: "Unknown")
setUpToolbar(name)
repo_recycler_view?.adapter =
settings_toolbar?.setOnMenuItemClickListener { menuItem ->
when (menuItem?.itemId) {
R.id.download_all -> {
pluginViewModel.downloadAll(activity, url)
}
else -> {}
}
return@setOnMenuItemClickListener true
}
plugin_recycler_view?.adapter =
PluginAdapter {
pluginViewModel.handlePluginAction(activity, url, it)
}
observe(pluginViewModel.plugins) {
(repo_recycler_view?.adapter as? PluginAdapter?)?.updateList(it)
(plugin_recycler_view?.adapter as? PluginAdapter?)?.updateList(it)
}
pluginViewModel.updatePluginList(url)

View file

@ -9,11 +9,14 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.plugins.PluginData
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.plugins.SitePlugin
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread
import kotlinx.coroutines.launch
@ -23,9 +26,8 @@ class PluginsViewModel : ViewModel() {
private val _plugins = MutableLiveData<List<PluginViewData>>()
val plugins: LiveData<List<PluginViewData>> = _plugins
private val repositoryCache: MutableMap<String, List<Plugin>> = mutableMapOf()
companion object {
private val repositoryCache: MutableMap<String, List<Plugin>> = mutableMapOf()
const val TAG = "PLG"
}
@ -55,6 +57,51 @@ class PluginsViewModel : ViewModel() {
return (data ?: getDownloads()).contains(plugin.second.internalName)
}
fun downloadAll(activity: Activity?, repositoryUrl: String) = ioSafe {
if (activity == null) return@ioSafe
val stored = getDownloads()
val plugins = getPlugins(repositoryUrl)
plugins.filter { plugin -> !isDownloaded(plugin, stored) }.also { list ->
main {
showToast(
activity,
if (list.isEmpty()) {
txt(
R.string.batch_download_nothing_to_download_format,
txt(R.string.plugin)
)
} else {
txt(R.string.batch_download_start_format, list.size, txt(if(list.size == 1) R.string.plugin_singular else R.string.plugin))
},
Toast.LENGTH_SHORT
)
}
}.apmap { (repo, metadata) ->
PluginManager.downloadAndLoadPlugin(
activity,
metadata.url,
metadata.name,
repo
)
}.main { list ->
if (list.any { it }) {
showToast(
activity,
txt(
R.string.batch_download_finish_format,
list.count { it },
txt(if(list.size == 1) R.string.plugin_singular else R.string.plugin)
),
Toast.LENGTH_SHORT
)
updatePluginListPrivate(repositoryUrl)
} else if (list.isNotEmpty()) {
showToast(activity, R.string.download_failed, Toast.LENGTH_SHORT)
}
}
}
fun handlePluginAction(activity: Activity?, repositoryUrl: String, plugin: Plugin) = ioSafe {
Log.i(TAG, "handlePluginAction = $repositoryUrl, $plugin")

View file

@ -3,28 +3,34 @@ package com.lagradost.cloudstream3.utils
import android.os.Handler
import android.os.Looper
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import kotlinx.coroutines.*
object Coroutines {
fun main(work: suspend (() -> Unit)): Job {
fun <T> T.main(work: suspend ((T) -> Unit)): Job {
val value = this
return CoroutineScope(Dispatchers.Main).launch {
work()
work(value)
}
}
fun ioSafe(work: suspend (CoroutineScope.() -> Unit)): Job {
fun <T> T.ioSafe(work: suspend (CoroutineScope.(T) -> Unit)): Job {
val value = this
return CoroutineScope(Dispatchers.IO).launch {
try {
work()
work(value)
} catch (e: Exception) {
logError(e)
}
}
}
suspend fun <T> ioWork(work: suspend (CoroutineScope.() -> T)): T {
suspend fun <T, V> V.ioWork(work: suspend (CoroutineScope.(V) -> T)): T {
val value = this
return withContext(Dispatchers.IO) {
work()
work(value)
}
}

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/extensions_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
app:menu="@menu/repository"
android:id="@+id/settings_toolbar"
android:paddingTop="@dimen/navbar_height"
tools:title="Overlord"
android:background="?attr/primaryGrayBackground"
app:navigationIconTint="?attr/iconColor"
app:titleTextColor="?attr/textColor"
app:layout_scrollFlags="scroll|enterAlways"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:listitem="@layout/repository_item"
android:id="@+id/plugin_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
app:showAsAction="always"
android:id="@+id/download_all"
android:icon="@drawable/netflix_download"
android:title="@string/batch_download" />
</menu>

View file

@ -577,4 +577,10 @@
<string name="plugin_deleted">Plugin Deleted</string>
<string name="plugin_load_fail" formatted="true">Failed to load %s</string>
<string name="is_adult">18+</string>
<string name="batch_download_start_format" formatted="true">Started downloading %d %s</string>
<string name="batch_download_finish_format" formatted="true">Downloaded %d %s successfully</string>
<string name="batch_download_nothing_to_download_format" formatted="true">All %s already downloaded</string>
<string name="batch_download">Batch download</string>
<string name="plugin_singular" formatted="true">plugin</string>
<string name="plugin" formatted="true">plugins</string>
</resources>