This commit is contained in:
deniscerri 2025-03-22 22:16:04 +01:00
parent 3b40ba7e02
commit 76394309fd
No known key found for this signature in database
GPG key ID: 95C43D517D830350
4 changed files with 82 additions and 91 deletions

View file

@ -1062,8 +1062,10 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
result.message = repository.startDownloadWorker(queued, context).getOrElse { "" }
val ids = queued.filter { it.needsDataUpdating() }.map { it.id }
continueUpdatingDataInBackground(ids)
val idsToUpdateDataInBackground = queued.filter { it.needsDataUpdating() && it.downloadStartTime > 0 }.map { it.id }
if (idsToUpdateDataInBackground.isNotEmpty()) {
continueUpdatingDataInBackground(idsToUpdateDataInBackground)
}
}
@ -1201,18 +1203,16 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
}
private fun continueUpdatingDataInBackground(ids: List<Long>){
val id = System.currentTimeMillis().toInt()
val workRequest = OneTimeWorkRequestBuilder<UpdateMultipleDownloadsDataWorker>()
.setInputData(
Data.Builder()
.putLongArray("ids", ids.toLongArray())
.putInt("id", id)
.build())
.addTag("updateData")
.build()
val context = App.instance
WorkManager.getInstance(context).enqueueUniqueWork(
id.toString(),
System.currentTimeMillis().toString(),
ExistingWorkPolicy.REPLACE,
workRequest
)

View file

@ -626,7 +626,7 @@ class NotificationUtil(var context: Context) {
return notificationBuilder
.setContentTitle(resources.getString(R.string.updating_download_data))
.setOngoing(true)
.setCategory(Notification.CATEGORY_PROGRESS)
.setCategory(Notification.CATEGORY_MESSAGE)
.setSmallIcon(R.drawable.ic_launcher_foreground_large)
.setLargeIcon(
BitmapFactory.decodeResource(
@ -637,7 +637,6 @@ class NotificationUtil(var context: Context) {
.setContentText("")
.setPriority(NotificationCompat.PRIORITY_LOW)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setProgress(PROGRESS_MAX, PROGRESS_CURR, false)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.clearActions()
.build()

View file

@ -43,17 +43,12 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.supervisorScope
@ -80,6 +75,8 @@ class DownloadWorker(private val context: Context,workerParams: WorkerParameters
private lateinit var eventBus: EventBus
private lateinit var queueState : Flow<List<DownloadItem>>
lateinit var observeJob : Job
private val handler = Handler(Looper.getMainLooper())
@ -107,11 +104,6 @@ class DownloadWorker(private val context: Context,workerParams: WorkerParameters
)
}
private fun cancelSelf() {
workManager.cancelWorkById(this@DownloadWorker.id)
}
@SuppressLint("RestrictedApi")
override suspend fun doWork(): Result {
//init
@ -157,75 +149,75 @@ class DownloadWorker(private val context: Context,workerParams: WorkerParameters
val downloadJobs = mutableMapOf<Long, Job>()
queueState.collectLatest { queue ->
val runningCount = queue.asSequence()
.filter { it.status == DownloadRepository.Status.Active.toString() }
.count()
observeJob = CoroutineScope(SupervisorJob()).launch {
queueState.collectLatest { queue ->
val runningCount = queue.asSequence()
.filter { it.status == DownloadRepository.Status.Active.toString() }
.count()
val queueItems = queue.asSequence()
.filter { it.status != DownloadRepository.Status.Active.toString() }
val queueItems = queue.asSequence()
.filter { it.status != DownloadRepository.Status.Active.toString() }
val queueCount = queueItems.count()
if (queueCount == 0 && runningCount == 0) {
cancelSelf()
return@collectLatest
}
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
if (useScheduler){
if (queueItems.none{ it.downloadStartTime > 0L } && runningCount == 0 && !alarmScheduler.isDuringTheScheduledTime()) {
cancelSelf()
val queueCount = queueItems.count()
if (queueCount == 0 && runningCount == 0) {
observeJob.cancel()
return@collectLatest
}
}
val concurrentDownloads = sharedPreferences.getInt("concurrent_downloads", 1) - runningCount
if (concurrentDownloads > 0) {
//free spots are open
if (priorityItemIDs.isNotEmpty()) {
if (!continueAfterPriorityIds && queueItems.none { priorityItemIDs.contains(it.id) }) {
//dont queue any more items if only required priority items
cancelSelf()
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
if (useScheduler){
if (queueItems.none{ it.downloadStartTime > 0L } && runningCount == 0 && !alarmScheduler.isDuringTheScheduledTime()) {
observeJob.cancel()
return@collectLatest
}
}
// Use supervisorScope to cancel child jobs when the downloader job is cancelled
supervisorScope {
val queuedDownloads = queueItems
.toList()
.take(concurrentDownloads)
val queuedIDs = queuedDownloads.map { it.id }
val downloadJobsToStop = downloadJobs.filter { it.key !in queuedIDs }
downloadJobsToStop.forEach { (download, job) ->
job.cancel()
downloadJobs.remove(download)
val concurrentDownloads = sharedPreferences.getInt("concurrent_downloads", 1) - runningCount
if (concurrentDownloads > 0) {
//free spots are open
if (priorityItemIDs.isNotEmpty()) {
if (!continueAfterPriorityIds && queueItems.none { priorityItemIDs.contains(it.id) }) {
//dont queue any more items if only required priority items
observeJob.cancel()
return@collectLatest
}
}
val downloadsToStart = queuedDownloads.filter { it.id !in downloadJobs }
downloadsToStart.forEach { download ->
downloadJobs[download.id] = launchDownloadJob(download)
// Use supervisorScope to cancel child jobs when the downloader job is cancelled
supervisorScope {
val queuedDownloads = queueItems
.toList()
.take(concurrentDownloads)
val queuedIDs = queuedDownloads.map { it.id }
val downloadJobsToStop = downloadJobs.filter { it.key !in queuedIDs }
downloadJobsToStop.forEach { (download, job) ->
job.cancel()
downloadJobs.remove(download)
}
val downloadsToStart = queuedDownloads.filter { it.id !in downloadJobs }
downloadsToStart.forEach { downloadItem ->
val notification = notificationUtil.createDownloadServiceNotification(openDownloadQueue, downloadItem.title.ifEmpty { downloadItem.url })
notificationUtil.notify(downloadItem.id.toInt(), notification)
downloadJobs[downloadItem.id] = launchDownloadJob(downloadItem)
}
}
}
}
}
}
while (observeJob.isActive) {
//keep alive
}
return Result.Success()
}
@OptIn(DelicateCoroutinesApi::class)
private fun launchIO(block: suspend CoroutineScope.() -> Unit): Job =
GlobalScope.launch(Dispatchers.IO, CoroutineStart.DEFAULT, block)
@OptIn(ExperimentalStdlibApi::class)
private fun launchDownloadJob(downloadItem: DownloadItem) = launchIO {
val notification = notificationUtil.createDownloadServiceNotification(openDownloadQueue, downloadItem.title.ifEmpty { downloadItem.url })
notificationUtil.notify(downloadItem.id.toInt(), notification)
private fun launchDownloadJob(downloadItem: DownloadItem) = CoroutineScope(Dispatchers.IO).launch {
val writtenPath = downloadItem.format.format_note.contains("-P ")
val noCache = writtenPath || (!sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(downloadItem.downloadPath)).canWrite())

View file

@ -1,8 +1,10 @@
package com.deniscerri.ytdl.work
import android.content.Context
import android.content.pm.ServiceInfo
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
import android.os.Build
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.Worker
import androidx.work.WorkerParameters
@ -14,37 +16,37 @@ import com.deniscerri.ytdl.util.NotificationUtil
import kotlinx.coroutines.runBlocking
class UpdateMultipleDownloadsDataWorker(
private val context: Context,
workerParams: WorkerParameters
) : Worker(context, workerParams) {
override fun doWork(): Result {
class UpdateMultipleDownloadsDataWorker(private val context: Context,workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) {
override suspend fun getForegroundInfo(): ForegroundInfo {
val workNotif = NotificationUtil(App.instance).createDataUpdateNotification()
return ForegroundInfo(
2000000000,
workNotif,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
FOREGROUND_SERVICE_TYPE_DATA_SYNC
} else {
0
},
)
}
override suspend fun doWork(): Result {
val dbManager = DBManager.getInstance(context)
val dao = dbManager.downloadDao
val resDao = dbManager.resultDao
val commandTemplateDao = dbManager.commandTemplateDao
val resultRepo = ResultRepository(resDao,commandTemplateDao, context)
val notificationUtil = NotificationUtil(context)
val ids = inputData.getLongArray("ids")!!.toMutableList()
val workID = inputData.getInt("id", 0)
if (workID == 0) return Result.failure()
val notification = notificationUtil.createDataUpdateNotification()
if (Build.VERSION.SDK_INT > 33) {
setForegroundAsync(ForegroundInfo(workID, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC))
}else{
setForegroundAsync(ForegroundInfo(workID, notification))
}
var count = 0
return try{
setForegroundSafely()
try{
ids.forEach {
if (!isStopped){
val d = dao.getDownloadById(it)
if (d.title.isNotBlank() && d.author.isNotBlank() && d.thumb.isNotBlank()) {
count++
return@forEach
}
@ -59,20 +61,18 @@ class UpdateMultipleDownloadsDataWorker(
}
}
}
count++
notificationUtil.updateDataUpdateNotification(workID, UpdateMultipleDownloadsDataWorker::class.java.name, count, ids.size)
}else{
throw Exception()
}
}
Result.success()
}catch (e: Exception){
notificationUtil.cancelDownloadNotification(workID)
ids.clear()
Result.failure()
return Result.failure()
}
return Result.success()
}
}