This commit is contained in:
deniscerri 2025-03-23 16:34:30 +01:00
parent e1db3fa5e5
commit fd151105ef
No known key found for this signature in database
GPG key ID: 95C43D517D830350
6 changed files with 78 additions and 77 deletions

View file

@ -26,7 +26,7 @@ interface DownloadDao {
@Query("SELECT * FROM downloads WHERE status='Active'")
fun getActiveDownloads() : Flow<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Active' or status = 'Paused' ORDER BY id")
@Query("SELECT * FROM downloads WHERE status='Active' or status = 'Paused' ORDER BY id, CASE WHEN status='Active' THEN 0 ELSE 1 END")
fun getActiveAndPausedDownloads() : Flow<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Paused'")
@ -115,7 +115,7 @@ interface DownloadDao {
@Query("""
SELECT * FROM downloads
WHERE status in ('Active','Queued', 'Scheduled') AND downloadStartTime <= :currentTime
WHERE status in ('Active', 'Queued', 'Scheduled') AND downloadStartTime <= :currentTime
ORDER BY
CASE
WHEN id in (:priorityItems) THEN 0

View file

@ -1314,8 +1314,12 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
suspend fun pauseAllDownloads() {
pausedAllDownloads.value = PausedAllDownloadsState.PROCESSING
isPausingResuming = true
withContext(Dispatchers.IO){
downloadManager.cancelAll()
}
WorkManager.getInstance(application).cancelAllWorkByTag("download")
downloadManager.cancelAll()
val activeDownloadsList = withContext(Dispatchers.IO){
getActiveDownloads()
}
@ -1333,7 +1337,11 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
pausedAllDownloads.value = PausedAllDownloadsState.PROCESSING
isPausingResuming = true
WorkManager.getInstance(application).cancelAllWorkByTag("download")
downloadManager.cancelAll()
withContext(Dispatchers.IO){
downloadManager.cancelAll()
}
val paused = withContext(Dispatchers.IO) {
dao.getPausedDownloadsList()
}
@ -1359,15 +1367,11 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
}
private suspend fun cancelAllDownloadsImpl() {
WorkManager.getInstance(application).cancelAllWorkByTag("download")
val activeAndQueued = withContext(Dispatchers.IO){
repository.getActiveAndQueuedDownloadIDs()
}
activeAndQueued.forEach { id ->
YoutubeDL.getInstance().destroyProcessById(id.toString())
notificationUtil.cancelDownloadNotification(id.toInt())
}
cancelActiveQueued()
withContext(Dispatchers.IO) {
downloadManager.cancelAll()
}
WorkManager.getInstance(application).cancelAllWorkByTag("download")
}
fun resumeDownload(itemID: Long) = viewModelScope.launch {

View file

@ -201,6 +201,7 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
override fun onResumeClick(itemID: Long) {
downloadViewModel.resumeDownload(itemID)
//activeRecyclerView.smoothScrollToPosition(0)
}
}

View file

@ -55,9 +55,9 @@ import java.util.Locale
class DownloadManager private constructor() {
private var notificationUtil: NotificationUtil = NotificationUtil(App.instance)
private var sharedPreferences : SharedPreferences = PreferenceManager.getDefaultSharedPreferences(App.instance)
private var dbManager: DBManager = DBManager.getInstance(App.instance)
private var dao : DownloadDao = dbManager.downloadDao
private lateinit var dbManager: DBManager
private lateinit var dao : DownloadDao
private lateinit var historyDao: HistoryDao
private lateinit var commandTemplateDao: CommandTemplateDao
private lateinit var logRepo: LogRepository
@ -75,37 +75,18 @@ class DownloadManager private constructor() {
val isRunning get() = observeJob?.isActive == true
private fun getActiveDownloadsStore() : Set<String> {
return sharedPreferences.getStringSet("ytdlp_active_downloads", setOf())!!
}
private fun addIDToActiveDownloadsStore(id: Long) {
val new = mutableSetOf<String>()
new.addAll(getActiveDownloadsStore())
new.add(id.toString())
sharedPreferences.edit().putStringSet("ytdlp_active_downloads", new).apply()
}
private fun removeIDFromActiveDownloadsStore(id: Long) {
val new = mutableSetOf<String>()
new.addAll(getActiveDownloadsStore())
new.remove(id.toString())
sharedPreferences.edit().putStringSet("ytdlp_active_downloads", new).apply()
}
fun cancelDownload(id: Long) {
YoutubeDL.getInstance().destroyProcessById(id.toString())
notificationUtil.cancelDownloadNotification(id.toInt())
removeIDFromActiveDownloadsStore(id)
}
fun cancelAll() {
getActiveDownloadsStore().map { it.toLong() }.forEach {
cancelDownload(it)
}
observeJob?.cancel()
observeJob = null
val activeDownloads = dao.getActiveDownloadsList()
activeDownloads.forEach {
cancelDownload(it.id)
}
}
//only call from download worker
@ -163,53 +144,68 @@ class DownloadManager private constructor() {
}
observeJob = CoroutineScope(SupervisorJob() + Dispatchers.IO).launch {
queueState.distinctUntilChanged().collectLatest { queue ->
val runningCount = queue.asSequence()
.filter { it.status == DownloadRepository.Status.Active.toString() }
.count()
supervisorScope {
queueState.distinctUntilChanged().collectLatest { queue ->
val activeDownloads = queue
.asSequence()
.filter { it.status == DownloadRepository.Status.Active.toString() }
.map { it.id }
val queueItems = queue.asSequence()
.filter { it.status != DownloadRepository.Status.Active.toString() }
val activeDownloadsCount = activeDownloads.count()
val queueCount = queueItems.count()
if (queueCount == 0 && runningCount == 0) {
observeJob?.cancel()
return@collectLatest
}
val queueItems = queue
.asSequence()
.filter { it.status != DownloadRepository.Status.Active.toString() }
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
if (useScheduler){
if (queueItems.none{ it.downloadStartTime > 0L } && runningCount == 0 && !alarmScheduler.isDuringTheScheduledTime()) {
val queueCount = queueItems.count()
if (queueCount == 0 && activeDownloadsCount == 0) {
observeJob?.cancel()
return@collectLatest
}
}
if (priorityItemIDs.isEmpty() && !continueAfterPriorityIds) {
observeJob?.cancel()
return@collectLatest
}
if (!isRunning) {
observeJob?.cancel()
return@collectLatest
}
val concurrentDownloads = sharedPreferences.getInt("concurrent_downloads", 1) - runningCount
if (concurrentDownloads > 0) {
//free spots are open
// Use supervisorScope to cancel child jobs when the downloader job is cancelled
supervisorScope {
val queuedDownloads = queueItems
.toList()
.take(concurrentDownloads)
priorityItemIDs.removeAll(queuedDownloads.map { it.id })
val downloadsToStart = queuedDownloads.filter { it.id.toString() !in getActiveDownloadsStore() }
downloadsToStart.forEach { downloadItem ->
val notification = notificationUtil.createDownloadServiceNotification(openDownloadQueue, downloadItem.title.ifEmpty { downloadItem.url })
notificationUtil.notify(downloadItem.id.toInt(), notification)
launchDownloadJob(context, downloadItem)
addIDToActiveDownloadsStore(downloadItem.id)
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
if (useScheduler){
if (queueItems.none{ it.downloadStartTime > 0L } && activeDownloadsCount == 0 && !alarmScheduler.isDuringTheScheduledTime()) {
observeJob?.cancel()
return@collectLatest
}
}
}
val concurrentDownloads = sharedPreferences.getInt("concurrent_downloads", 1) - activeDownloadsCount
if (concurrentDownloads > 0) {
supervisorScope {
//free spots are open
val queuedDownloads = if (priorityItemIDs.isNotEmpty() && !continueAfterPriorityIds) {
queueItems
.filter { priorityItemIDs.contains(it.id) }
.toList()
}else{
queueItems.toList()
}.take(concurrentDownloads)
priorityItemIDs.removeAll(queuedDownloads.map { it.id })
val downloadsToStart = queuedDownloads.filter { it.id !in activeDownloads }
downloadsToStart.forEach { downloadItem ->
val notification = notificationUtil.createDownloadServiceNotification(openDownloadQueue, downloadItem.title.ifEmpty { downloadItem.url })
notificationUtil.notify(downloadItem.id.toInt(), notification)
launchDownloadJob(context, downloadItem)
}
}
}
if (priorityItemIDs.isEmpty() && !continueAfterPriorityIds) {
observeJob?.cancel()
return@collectLatest
}
}
}
}
}
@ -288,7 +284,6 @@ class DownloadManager private constructor() {
resultRepo.updateDownloadItem(downloadItem)?.apply {
dao.updateWithoutUpsert(this)
}
removeIDFromActiveDownloadsStore(downloadItem.id)
//val wasQuickDownloaded = resultDao.getCountInt() == 0
runBlocking {
var finalPaths = mutableListOf<String>()
@ -414,7 +409,6 @@ class DownloadManager private constructor() {
}.onFailure {
FileUtil.deleteConfigFiles(request)
removeIDFromActiveDownloadsStore(downloadItem.id)
withContext(Dispatchers.Main){
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
}

View file

@ -10,6 +10,7 @@
android:layout_height="wrap_content"
android:scrollbars="none"
android:clipToPadding="false"
android:paddingBottom="100dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton

View file

@ -33,7 +33,8 @@
android:id="@+id/download_tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_marginHorizontal="10dp"
android:layout_marginBottom="10dp"
android:backgroundTint="@android:color/transparent"
app:tabGravity="start"
app:tabMinWidth="0dp"