finish sample

pull/1112/head
deniscerri 5 months ago
parent 32a1c85d02
commit 388498adb2
No known key found for this signature in database
GPG Key ID: 95C43D517D830350

@ -9,7 +9,8 @@ permissions:
on:
push:
branches: [ "main" ]
branches:
- "**"
jobs:
build:

@ -109,7 +109,7 @@ object RuntimeManager {
}
ENV_PYTHONHOME = if (pythonLocation.isDownloaded) {
pythonLocation.ldDir.parent
pythonLocation.ldDir.absolutePath + "/usr"
} else {
pythonLocation.ldDir.absolutePath + "/usr"
}

@ -5,6 +5,6 @@ object Aria2c : PluginBase() {
override val pluginFolderName: String get() = "aria2c"
override val bundledZipName: String get() = "libaria2c.zip.so"
override val bundledVersion: String get() = "v1.37.0"
override val packageGithubRepo: String get() = ""
override val githubPackageName: String get() = ""
override val githubRepo: String get() = "deniscerri/ytdlnis-plugins"
override val githubPackageName: String get() = "aria2c"
}

@ -5,6 +5,6 @@ object FFmpeg : PluginBase() {
override val pluginFolderName: String get() = "ffmpeg"
override val bundledZipName: String get() = "libffmpeg.zip.so"
override val bundledVersion: String get() = "v7.0.1"
override val packageGithubRepo: String get() = "deniscerri/ytdlnis-packages"
override val githubRepo: String get() = "deniscerri/ytdlnis-plugins"
override val githubPackageName: String get() = "ffmpeg"
}

@ -5,6 +5,6 @@ object NodeJS : PluginBase() {
override val pluginFolderName: String get() = "node"
override val bundledZipName: String get() = "libnode.zip.so"
override val bundledVersion: String get() = ""
override val packageGithubRepo: String get() = "deniscerri/ytdlnis-plugins"
override val githubRepo: String get() = "deniscerri/ytdlnis-plugins"
override val githubPackageName: String get() = "nodejs"
}

@ -5,9 +5,10 @@ import android.os.Build
import androidx.core.content.edit
import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceManager
import com.anggrayudi.storage.file.toRawFile
import com.deniscerri.ytdl.core.RuntimeManager
import com.deniscerri.ytdl.core.ZipUtils
import com.deniscerri.ytdl.database.models.GithubReleaseAsset
import com.google.gson.annotations.SerializedName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
@ -17,15 +18,15 @@ import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import okhttp3.Request
import org.apache.commons.io.FileUtils
import org.json.JSONObject
import java.io.File
import java.time.LocalDate
import kotlin.coroutines.cancellation.CancellationException
abstract class PluginBase {
protected abstract val executableName: String // e.g., "ffmpeg"
protected abstract val pluginFolderName: String // e.g., "ffmpeg"
protected abstract val bundledZipName: String // e.g., "libffmpeg.zip.so"
protected abstract val packageGithubRepo: String // e.g deniscerri/ytdlnis-plugins
protected abstract val githubRepo: String // e.g deniscerri/ytdlnis-plugins
protected abstract val githubPackageName: String // e.g ffmpeg
fun getInstance(): PluginBase = this
@ -34,11 +35,16 @@ abstract class PluginBase {
@Serializable
data class PluginRelease(
@SerialName("name")
var version: String,
@SerialName("created_at")
val createdAt: String,
var downloadUrl: String = "",
@SerializedName(value = "tag_name")
var tag_name: String,
@SerialName("published_at")
val published_at: String,
@SerializedName(value = "assets")
var assets: List<GithubReleaseAsset>,
@SerializedName(value = "body")
val body: String,
var version: String = "",
var downloadSize: Long = 0,
var isInstalled: Boolean = false,
var isBundled: Boolean = false,
var isDownloading: Boolean = false,
@ -143,7 +149,7 @@ abstract class PluginBase {
//download
val request = Request.Builder()
.url(release.downloadUrl)
.url(release.assets.first().browser_download_url)
.build()
val response = sharedClient.newCall(request).execute()
@ -242,12 +248,11 @@ abstract class PluginBase {
}
}
suspend fun getReleases() : List<PluginRelease> {
if (packageGithubRepo.isEmpty()) return listOf()
suspend fun getReleases() : Result<List<PluginRelease>> {
if (githubRepo.isEmpty()) return Result.success(listOf())
val request = Request.Builder()
.url("https://api.github.com/users/${packageGithubRepo.split("/").first()}/packages/maven/${githubPackageName}/versions")
.header("Accept", "application/vnd.github+json")
.url("https://api.github.com/repos/${githubRepo}/releases")
.build()
return withContext(Dispatchers.IO) {
@ -258,18 +263,25 @@ abstract class PluginBase {
if (response.isSuccessful && jsonString.isNotEmpty()) {
val supportedArch = getArchSuffix()
json.decodeFromString<List<PluginRelease>>(jsonString)
val releases = json.decodeFromString<List<PluginRelease>>(jsonString)
.filter {
it.tag_name.contains(githubPackageName)
}
.onEach {
it.version = it.version
// nodejs-1.0.0-arm64-v8a
it.version = it.tag_name.split("-")[1]
it.assets = it.assets.filter { a -> a.name.contains(supportedArch) }
it.downloadSize = it.assets.first().size
it.isInstalled = downloadedVersion == "v${it.version}"
it.isBundled = bundledVersion == "v${it.version}"
it.downloadUrl = "https://maven.pkg.github.com/${packageGithubRepo}/${githubPackageName}/${it.version}/${githubPackageName}-${it.version}-${supportedArch}.zip"
}
Result.success(releases)
} else {
emptyList()
Result.failure(Throwable(JSONObject(jsonString).getString("message")))
}
} catch (e: Exception) {
listOf()
Result.failure(e)
}
}
}

@ -5,6 +5,6 @@ object Python : PluginBase() {
override val pluginFolderName: String get() = "python"
override val bundledZipName: String get() = "libpython.zip.so"
override val bundledVersion: String get() = "v3.12"
override val packageGithubRepo: String get() = "deniscerri/ytdlnis-packages"
override val githubRepo: String get() = "deniscerri/ytdlnis-plugins"
override val githubPackageName: String get() = "python"
}

@ -5,6 +5,6 @@ object QuickJS : PluginBase() {
override val pluginFolderName: String get() = "quickjs"
override val bundledZipName: String get() = "libqjs.zip.so"
override val bundledVersion: String get() = "2025-04-26"
override val packageGithubRepo: String get() = ""
override val githubRepo: String get() = ""
override val githubPackageName: String get() = ""
}

@ -1,6 +1,7 @@
package com.deniscerri.ytdl.database.models
import com.google.gson.annotations.SerializedName
import kotlinx.serialization.Serializable
import java.util.Date
data class GithubRelease(
@ -17,9 +18,12 @@ data class GithubRelease(
)
@Serializable
data class GithubReleaseAsset(
@SerializedName(value = "name")
var name: String,
@SerializedName(value = "browser_download_url")
var browser_download_url: String
var browser_download_url: String,
@SerializedName(value = "size")
var size: Long
)

@ -52,11 +52,12 @@ class PluginReleaseAdapter(onItemClickListener: OnItemClickListener, activity: A
val item = getItem(position) ?: return
val card = holder.itemView
card.findViewById<TextView>(R.id.title).text = "v${item.version}"
val version = "v${item.version}"
card.findViewById<TextView>(R.id.title).text = version
val parser = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault())
parser.timeZone = TimeZone.getTimeZone("UTC")
val date = parser.parse(item.createdAt)
val date = parser.parse(item.published_at)
val parser2 = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault())
card.findViewById<TextView>(R.id.createdAt).text = parser2.format(date.time)

@ -2,11 +2,20 @@ package com.deniscerri.ytdl.ui.more.settings.updating
import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.text.method.LinkMovementMethod
import android.util.DisplayMetrics
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -34,15 +43,20 @@ import com.deniscerri.ytdl.ui.adapter.PluginReleaseAdapter
import com.deniscerri.ytdl.ui.adapter.PluginsAdapter
import com.deniscerri.ytdl.ui.more.settings.SettingsActivity
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
import com.deniscerri.ytdl.util.FileUtil
import com.deniscerri.ytdl.util.UiUtil
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.progressindicator.CircularProgressIndicator
import com.google.android.material.snackbar.Snackbar
import junit.runner.Version
import io.noties.markwon.AbstractMarkwonPlugin
import io.noties.markwon.Markwon
import io.noties.markwon.MarkwonConfiguration
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
class PluginsFragment : Fragment(), PluginsAdapter.OnItemClickListener, PluginReleaseAdapter.OnItemClickListener {
@ -119,7 +133,15 @@ class PluginsFragment : Fragment(), PluginsAdapter.OnItemClickListener, PluginRe
lifecycleScope.launch {
val instance = tmpItem!!.getInstance()
instance.getReleases().apply {
pluginReleases = this.toMutableList()
this.onFailure {
lifecycleScope.launch {
withContext(Dispatchers.Main) {
Toast.makeText(requireContext(), it.message ?: getString(R.string.errored), Toast.LENGTH_SHORT).show()
}
}
}
pluginReleases = this.getOrElse { listOf() }
releaseAdapter.submitList(pluginReleases)
releaseRecyclerView.isVisible = pluginReleases.isNotEmpty()
loader?.isVisible = false
@ -204,52 +226,75 @@ class PluginsFragment : Fragment(), PluginsAdapter.OnItemClickListener, PluginRe
}
override fun onDownloadReleaseClick(item: PluginBase.PluginRelease) {
val instance = tmpItem!!.getInstance()
val idx = pluginReleases.indexOf(item)
val list = pluginReleases.toMutableList()
list[idx] = list[idx].copy(isDownloading = true)
releaseAdapter.submitList(list.toList())
var positiveButton: Button? = null
val updateDialog = MaterialAlertDialogBuilder(requireContext())
.setTitle("${item.tag_name} (${FileUtil.convertFileSize(item.downloadSize)})")
.setMessage(item.body)
.setIcon(R.drawable.ic_update_app)
.setNegativeButton(requireContext().getString(R.string.cancel)) { _: DialogInterface?, _: Int ->
tmpDownloadJob?.cancel()
}
.setPositiveButton(requireContext().getString(R.string.download), null)
val view = updateDialog.show()
val textView = view.findViewById<TextView>(android.R.id.message)
textView!!.movementMethod = LinkMovementMethod.getInstance()
val mw = Markwon.builder(requireContext()).usePlugin(object: AbstractMarkwonPlugin() {
tmpDownloadJob = lifecycleScope.launch {
val fileResp = instance.downloadRelease(requireContext(), item) { progress ->
lifecycleScope.launch {
withContext(Dispatchers.Main) {
list[idx] = list[idx].copy(downloadProgress = progress)
releaseAdapter.submitList(list.toList())
//releaseAdapter.notifyItemChanged(idx, progress.toString())
}
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
builder.linkResolver { view, link ->
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
requireContext().startActivity(browserIntent)
}
}
}).build()
mw.setMarkdown(textView, item.body)
positiveButton = view.getButton(AlertDialog.BUTTON_POSITIVE)
positiveButton?.setOnClickListener {
positiveButton.isEnabled = false
positiveButton.text = "0%"
fileResp.onFailure {
lifecycleScope.launch {
withContext(Dispatchers.Main) {
bottomSheet?.dismiss()
Snackbar.make(requireView(), it.message ?: getString(R.string.errored), Snackbar.LENGTH_LONG).show()
tmpDownloadJob = lifecycleScope.launch {
val instance = tmpItem!!.getInstance()
val fileResp = instance.downloadRelease(requireContext(), item) { progress ->
lifecycleScope.launch {
withContext(Dispatchers.Main) {
positiveButton?.text = "$progress%"
}
}
}
}
fileResp.onSuccess { file ->
val resp = instance.installFromZip(requireContext(), file, "v${item.version}")
resp.onFailure {
fileResp.onFailure {
lifecycleScope.launch {
withContext(Dispatchers.Main) {
bottomSheet?.dismiss()
view.dismiss()
Snackbar.make(requireView(), it.message ?: getString(R.string.errored), Snackbar.LENGTH_LONG).show()
}
}
}
resp.onSuccess {
lifecycleScope.launch {
withContext(Dispatchers.Main) {
bottomSheet?.dismiss()
listAdapter.notifyDataSetChanged()
RuntimeManager.reInit(requireContext())
fileResp.onSuccess { file ->
val resp = instance.installFromZip(requireContext(), file, "v${item.version}")
resp.onFailure {
lifecycleScope.launch {
withContext(Dispatchers.Main) {
bottomSheet?.dismiss()
view.dismiss()
Snackbar.make(requireView(), it.message ?: getString(R.string.errored), Snackbar.LENGTH_LONG).show()
}
}
}
resp.onSuccess {
lifecycleScope.launch {
withContext(Dispatchers.Main) {
bottomSheet?.dismiss()
view.dismiss()
listAdapter.notifyDataSetChanged()
RuntimeManager.reInit(requireContext())
}
}
}
}
}
}
}
}

@ -92,9 +92,13 @@ class UpdateSettingsFragment : BaseSettingsFragment() {
false
}
findPreference<Preference>("plugins")?.setOnPreferenceClickListener {
findNavController().navigate(R.id.pluginsFragment)
false
findPreference<Preference>("plugins")?.apply {
summary = "Python, FFmpeg, Aria2c, NodeJS"
setOnPreferenceClickListener {
findNavController().navigate(R.id.pluginsFragment)
false
}
}

@ -70,15 +70,16 @@
app:summary="@string/update_app_beta_summary"
app:title="@string/update_app_beta" />
<Preference
app:icon="@drawable/ic_code"
app:key="plugins"
app:title="@string/plugins" />
<Preference
app:icon="@drawable/ic_chapters"
app:key="changelog"
app:title="@string/changelog" />
<Preference
app:icon="@drawable/ic_code"
app:key="plugins"
app:title="@string/plugins" />
</PreferenceCategory>

Loading…
Cancel
Save