forked from recloudstream/cloudstream
batch download plugins
This commit is contained in:
parent
4e6bbf3908
commit
32d9aea02c
10 changed files with 150 additions and 29 deletions
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
39
app/src/main/res/layout/fragment_plugins.xml
Normal file
39
app/src/main/res/layout/fragment_plugins.xml
Normal 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>
|
||||
|
9
app/src/main/res/menu/repository.xml
Normal file
9
app/src/main/res/menu/repository.xml
Normal 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>
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue