fix downloads not continuing in the queue
This commit is contained in:
parent
78fd2427eb
commit
3b40ba7e02
19 changed files with 446 additions and 415 deletions
|
|
@ -503,7 +503,7 @@
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="androidx.work.impl.foreground.SystemForegroundService"
|
android:name="androidx.work.impl.foreground.SystemForegroundService"
|
||||||
android:foregroundServiceType="specialUse"/>
|
android:foregroundServiceType="dataSync"/>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
@ -107,19 +107,24 @@ interface DownloadDao {
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT * FROM downloads
|
SELECT * FROM downloads
|
||||||
WHERE status in ('Queued', 'Scheduled') AND downloadStartTime <= :currentTime
|
WHERE status in ('Active', 'Queued', 'Scheduled') AND downloadStartTime <= :currentTime
|
||||||
ORDER BY downloadStartTime, id
|
ORDER BY downloadStartTime, id
|
||||||
LIMIT 20
|
LIMIT 10
|
||||||
""")
|
""")
|
||||||
fun getQueuedScheduledDownloadsUntil(currentTime: Long) : Flow<List<DownloadItem>>
|
fun getActiveQueuedScheduledDownloadsUntil(currentTime: Long) : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT * FROM downloads
|
SELECT * FROM downloads
|
||||||
WHERE id in (:priorityItems) AND status in ('Queued', 'Scheduled') AND downloadStartTime <= :currentTime
|
WHERE status in ('Active','Queued', 'Scheduled') AND downloadStartTime <= :currentTime
|
||||||
ORDER BY downloadStartTime, id
|
ORDER BY
|
||||||
LIMIT 20
|
CASE
|
||||||
|
WHEN id in (:priorityItems) THEN 0
|
||||||
|
ELSE 1
|
||||||
|
END,
|
||||||
|
downloadStartTime, id
|
||||||
|
LIMIT 10
|
||||||
""")
|
""")
|
||||||
fun getQueuedScheduledDownloadsUntilWithPriority(currentTime: Long, priorityItems: List<Long>) : Flow<List<DownloadItem>>
|
fun getActiveQueuedScheduledDownloadsUntilWithPriority(currentTime: Long, priorityItems: List<Long>) : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
|
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
|
||||||
fun getQueuedDownloadsList() : List<DownloadItem>
|
fun getQueuedDownloadsList() : List<DownloadItem>
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,7 @@ package com.deniscerri.ytdl.database.repository
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.os.postDelayed
|
|
||||||
import androidx.paging.Pager
|
import androidx.paging.Pager
|
||||||
import androidx.paging.PagingConfig
|
import androidx.paging.PagingConfig
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
|
@ -16,9 +12,7 @@ import androidx.work.Data
|
||||||
import androidx.work.ExistingWorkPolicy
|
import androidx.work.ExistingWorkPolicy
|
||||||
import androidx.work.NetworkType
|
import androidx.work.NetworkType
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import androidx.work.WorkInfo
|
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.await
|
|
||||||
import com.deniscerri.ytdl.App
|
import com.deniscerri.ytdl.App
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.dao.DownloadDao
|
import com.deniscerri.ytdl.database.dao.DownloadDao
|
||||||
|
|
@ -31,7 +25,6 @@ import com.deniscerri.ytdl.work.AlarmScheduler
|
||||||
import com.deniscerri.ytdl.work.DownloadWorker
|
import com.deniscerri.ytdl.work.DownloadWorker
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,11 @@ package com.deniscerri.ytdl.receiver
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import androidx.work.Constraints
|
import androidx.work.Constraints
|
||||||
import androidx.work.ExistingWorkPolicy
|
import androidx.work.ExistingWorkPolicy
|
||||||
import androidx.work.NetworkType
|
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.deniscerri.ytdl.work.CancelScheduledDownloadWorker
|
import com.deniscerri.ytdl.work.CancelScheduledDownloadWorker
|
||||||
import com.deniscerri.ytdl.work.DownloadWorker
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class CancelScheduleAlarmReceiver : BroadcastReceiver() {
|
class CancelScheduleAlarmReceiver : BroadcastReceiver() {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
|
@ -20,8 +19,6 @@ import android.view.ViewGroup
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,42 @@
|
||||||
package com.deniscerri.ytdl.ui.downloads
|
package com.deniscerri.ytdl.ui.downloads
|
||||||
|
|
||||||
import android.animation.AnimatorSet
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.animation.AccelerateDecelerateInterpolator
|
|
||||||
import android.widget.LinearLayout
|
|
||||||
import android.widget.ListView
|
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.appcompat.view.ActionMode
|
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.res.ResourcesCompat
|
|
||||||
import androidx.core.os.bundleOf
|
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.marginBottom
|
|
||||||
import androidx.core.view.setMargins
|
|
||||||
import androidx.core.view.updateLayoutParams
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.room.util.getColumnIndexOrThrow
|
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
import com.deniscerri.ytdl.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.ui.adapter.ActiveDownloadAdapter
|
import com.deniscerri.ytdl.ui.adapter.ActiveDownloadAdapter
|
||||||
import com.deniscerri.ytdl.ui.adapter.QueuedDownloadAdapter
|
|
||||||
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
|
||||||
import com.deniscerri.ytdl.util.Extensions.forceFastScrollMode
|
import com.deniscerri.ytdl.util.Extensions.forceFastScrollMode
|
||||||
import com.deniscerri.ytdl.util.Extensions.toListString
|
|
||||||
import com.deniscerri.ytdl.util.FileUtil
|
|
||||||
import com.deniscerri.ytdl.util.NotificationUtil
|
import com.deniscerri.ytdl.util.NotificationUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
|
||||||
import com.deniscerri.ytdl.work.DownloadWorker
|
import com.deniscerri.ytdl.work.DownloadWorker
|
||||||
import com.google.android.material.badge.BadgeDrawable
|
|
||||||
import com.google.android.material.badge.BadgeUtils
|
|
||||||
import com.google.android.material.badge.ExperimentalBadgeUtils
|
import com.google.android.material.badge.ExperimentalBadgeUtils
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
|
||||||
import com.google.android.material.button.MaterialButton
|
|
||||||
import com.google.android.material.card.MaterialCardView
|
|
||||||
import com.google.android.material.color.MaterialColors
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.interaction.DragInteraction
|
|
||||||
import androidx.core.content.edit
|
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.EditTextPreference
|
import androidx.preference.EditTextPreference
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.work.WorkManager
|
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.models.TerminalItem
|
import com.deniscerri.ytdl.database.models.TerminalItem
|
||||||
import com.deniscerri.ytdl.database.viewmodel.TerminalViewModel
|
import com.deniscerri.ytdl.database.viewmodel.TerminalViewModel
|
||||||
|
|
|
||||||
|
|
@ -40,20 +40,15 @@ import com.deniscerri.ytdl.util.Extensions.setCustomTextSize
|
||||||
import com.deniscerri.ytdl.util.FileUtil
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
import com.deniscerri.ytdl.util.NotificationUtil
|
import com.deniscerri.ytdl.util.NotificationUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.deniscerri.ytdl.work.DownloadWorker
|
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.google.android.material.bottomappbar.BottomAppBar
|
import com.google.android.material.bottomappbar.BottomAppBar
|
||||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
|
||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import org.greenrobot.eventbus.Subscribe
|
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1280,6 +1280,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
||||||
val poTokens = mutableListOf<String>()
|
val poTokens = mutableListOf<String>()
|
||||||
|
|
||||||
val configuredPlayerClientsRaw = sharedPreferences.getString("youtube_player_clients", "[]")!!
|
val configuredPlayerClientsRaw = sharedPreferences.getString("youtube_player_clients", "[]")!!
|
||||||
|
kotlin.runCatching {
|
||||||
val configuredPlayerClients = Gson().fromJson(configuredPlayerClientsRaw, Array<YoutubePlayerClientItem>::class.java).toMutableList()
|
val configuredPlayerClients = Gson().fromJson(configuredPlayerClientsRaw, Array<YoutubePlayerClientItem>::class.java).toMutableList()
|
||||||
|
|
||||||
for (value in configuredPlayerClients) {
|
for (value in configuredPlayerClients) {
|
||||||
|
|
@ -1300,6 +1301,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val dataSyncID = sharedPreferences.getString("youtube_data_sync_id", "")!!
|
val dataSyncID = sharedPreferences.getString("youtube_data_sync_id", "")!!
|
||||||
if (dataSyncID.isNotBlank()) {
|
if (dataSyncID.isNotBlank()) {
|
||||||
|
|
@ -1308,6 +1310,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
||||||
}
|
}
|
||||||
|
|
||||||
val generatedPoTokensRaw = sharedPreferences.getString("youtube_generated_po_tokens", "[]")
|
val generatedPoTokensRaw = sharedPreferences.getString("youtube_generated_po_tokens", "[]")
|
||||||
|
kotlin.runCatching {
|
||||||
val generatedPoTokens = Gson().fromJson(generatedPoTokensRaw,Array<YoutubeGeneratePoTokenItem>::class.java).toMutableList()
|
val generatedPoTokens = Gson().fromJson(generatedPoTokensRaw,Array<YoutubeGeneratePoTokenItem>::class.java).toMutableList()
|
||||||
if (generatedPoTokens.isNotEmpty()) {
|
if (generatedPoTokens.isNotEmpty()) {
|
||||||
for (value in generatedPoTokens) {
|
for (value in generatedPoTokens) {
|
||||||
|
|
@ -1329,6 +1332,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (playerClients.isNotEmpty()){
|
if (playerClients.isNotEmpty()){
|
||||||
extractorArgs.add("player_client=${playerClients.joinToString(",")}")
|
extractorArgs.add("player_client=${playerClients.joinToString(",")}")
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package com.deniscerri.ytdl.work
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.deniscerri.ytdl.work
|
package com.deniscerri.ytdl.work
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.ForegroundInfo
|
import androidx.work.ForegroundInfo
|
||||||
|
|
@ -26,7 +26,7 @@ class CleanUpLeftoverDownloads(
|
||||||
|
|
||||||
val notification = notificationUtil.createDeletingLeftoverDownloadsNotification()
|
val notification = notificationUtil.createDeletingLeftoverDownloadsNotification()
|
||||||
if (Build.VERSION.SDK_INT > 33) {
|
if (Build.VERSION.SDK_INT > 33) {
|
||||||
setForegroundAsync(ForegroundInfo(id, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE))
|
setForegroundAsync(ForegroundInfo(id, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC))
|
||||||
}else{
|
}else{
|
||||||
setForegroundAsync(ForegroundInfo(id, notification))
|
setForegroundAsync(ForegroundInfo(id, notification))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ import android.annotation.SuppressLint
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
import android.content.SharedPreferences
|
||||||
|
import android.content.pm.ServiceInfo
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
|
@ -16,7 +17,6 @@ import android.widget.Toast
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.ForegroundInfo
|
import androidx.work.ForegroundInfo
|
||||||
import androidx.work.WorkInfo
|
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
||||||
|
|
@ -24,6 +24,10 @@ import com.deniscerri.ytdl.App
|
||||||
import com.deniscerri.ytdl.MainActivity
|
import com.deniscerri.ytdl.MainActivity
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.DBManager
|
import com.deniscerri.ytdl.database.DBManager
|
||||||
|
import com.deniscerri.ytdl.database.dao.CommandTemplateDao
|
||||||
|
import com.deniscerri.ytdl.database.dao.DownloadDao
|
||||||
|
import com.deniscerri.ytdl.database.dao.HistoryDao
|
||||||
|
import com.deniscerri.ytdl.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdl.database.models.HistoryItem
|
import com.deniscerri.ytdl.database.models.HistoryItem
|
||||||
import com.deniscerri.ytdl.database.models.LogItem
|
import com.deniscerri.ytdl.database.models.LogItem
|
||||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||||
|
|
@ -36,11 +40,23 @@ import com.deniscerri.ytdl.util.NotificationUtil
|
||||||
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.CoroutineStart
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
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.delay
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
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.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.supervisorScope
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
@ -48,45 +64,68 @@ import java.security.MessageDigest
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
|
||||||
class DownloadWorker(
|
class DownloadWorker(private val context: Context,workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) {
|
||||||
private val context: Context,
|
private lateinit var dao : DownloadDao
|
||||||
workerParams: WorkerParameters
|
private lateinit var notificationUtil: NotificationUtil
|
||||||
) : CoroutineWorker(context, workerParams) {
|
private lateinit var dbManager: DBManager
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
private lateinit var historyDao: HistoryDao
|
||||||
|
private lateinit var commandTemplateDao: CommandTemplateDao
|
||||||
|
private lateinit var logRepo: LogRepository
|
||||||
|
private lateinit var resultRepo: ResultRepository
|
||||||
|
private lateinit var ytdlpUtil: YTDLPUtil
|
||||||
|
private lateinit var alarmScheduler : AlarmScheduler
|
||||||
|
private lateinit var sharedPreferences : SharedPreferences
|
||||||
|
private lateinit var resources: Resources
|
||||||
|
private lateinit var workManager: WorkManager
|
||||||
|
private lateinit var eventBus: EventBus
|
||||||
|
|
||||||
|
private lateinit var queueState : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
|
private val openDownloadQueue: PendingIntent = PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
1000000000,
|
||||||
|
Intent(context, MainActivity::class.java).run {
|
||||||
|
action = Intent.ACTION_VIEW
|
||||||
|
putExtra("destination", "Queue")
|
||||||
|
},
|
||||||
|
PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
|
||||||
|
override suspend fun getForegroundInfo(): ForegroundInfo {
|
||||||
|
val workNotif = NotificationUtil(App.instance).createDefaultWorkerNotification()
|
||||||
|
|
||||||
|
return ForegroundInfo(
|
||||||
|
1000000000,
|
||||||
|
workNotif,
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancelSelf() {
|
||||||
|
workManager.cancelWorkById(this@DownloadWorker.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
if (isStopped) return Result.success()
|
//init
|
||||||
val workManager = WorkManager.getInstance(context)
|
notificationUtil = NotificationUtil(App.instance)
|
||||||
val currentWork = withContext(Dispatchers.IO) {
|
dbManager = DBManager.getInstance(context)
|
||||||
workManager.getWorkInfosByTag("download").get()
|
dao = dbManager.downloadDao
|
||||||
}
|
historyDao = dbManager.historyDao
|
||||||
if (currentWork.count{it.state == WorkInfo.State.RUNNING} > 1) {
|
commandTemplateDao = dbManager.commandTemplateDao
|
||||||
return Result.success()
|
logRepo = LogRepository(dbManager.logDao)
|
||||||
}
|
resultRepo = ResultRepository(dbManager.resultDao, commandTemplateDao, context)
|
||||||
|
ytdlpUtil = YTDLPUtil(context, commandTemplateDao)
|
||||||
val notificationUtil = NotificationUtil(App.instance)
|
alarmScheduler = AlarmScheduler(context)
|
||||||
val dbManager = DBManager.getInstance(context)
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
val dao = dbManager.downloadDao
|
eventBus = EventBus.getDefault()
|
||||||
val historyDao = dbManager.historyDao
|
|
||||||
val commandTemplateDao = dbManager.commandTemplateDao
|
|
||||||
val logRepo = LogRepository(dbManager.logDao)
|
|
||||||
val resultRepo = ResultRepository(dbManager.resultDao, commandTemplateDao, context)
|
|
||||||
val ytdlpUtil = YTDLPUtil(context, commandTemplateDao)
|
|
||||||
val handler = Handler(Looper.getMainLooper())
|
|
||||||
val alarmScheduler = AlarmScheduler(context)
|
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
val time = System.currentTimeMillis() + 6000
|
|
||||||
val priorityItemIDs = (inputData.getLongArray("priority_item_ids") ?: longArrayOf()).toMutableList()
|
|
||||||
val continueAfterPriorityIds = inputData.getBoolean("continue_after_priority_ids", true)
|
|
||||||
val queuedItems = if (priorityItemIDs.isEmpty()) {
|
|
||||||
dao.getQueuedScheduledDownloadsUntil(time)
|
|
||||||
}else {
|
|
||||||
dao.getQueuedScheduledDownloadsUntilWithPriority(time, priorityItemIDs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is needed for observe sources call, so it wont create result items
|
|
||||||
// [removed]
|
|
||||||
//val createResultItem = inputData.getBoolean("createResultItem", true)
|
|
||||||
|
|
||||||
val confTmp = Configuration(context.resources.configuration)
|
val confTmp = Configuration(context.resources.configuration)
|
||||||
val locale = if (Build.VERSION.SDK_INT < 33) {
|
val locale = if (Build.VERSION.SDK_INT < 33) {
|
||||||
|
|
@ -100,82 +139,97 @@ class DownloadWorker(
|
||||||
}
|
}
|
||||||
confTmp.setLocale(locale)
|
confTmp.setLocale(locale)
|
||||||
val metrics = DisplayMetrics()
|
val metrics = DisplayMetrics()
|
||||||
val resources = Resources(context.assets, metrics, confTmp)
|
resources = Resources(context.assets, metrics, confTmp)
|
||||||
|
|
||||||
val openQueueIntent = Intent(context, MainActivity::class.java)
|
workManager = WorkManager.getInstance(context)
|
||||||
openQueueIntent.setAction(Intent.ACTION_VIEW)
|
if (workManager.isRunning("download")) return Result.Failure()
|
||||||
openQueueIntent.putExtra("destination", "Queue")
|
|
||||||
val openDownloadQueue = PendingIntent.getActivity(
|
|
||||||
context,
|
|
||||||
1000000000,
|
|
||||||
openQueueIntent,
|
|
||||||
PendingIntent.FLAG_IMMUTABLE
|
|
||||||
)
|
|
||||||
|
|
||||||
val workNotif = notificationUtil.createDefaultWorkerNotification()
|
setForegroundSafely()
|
||||||
val notificationID = 1000000000
|
|
||||||
if (Build.VERSION.SDK_INT > 33) {
|
val priorityItemIDs = (inputData.getLongArray("priority_item_ids") ?: longArrayOf()).toMutableList()
|
||||||
setForegroundAsync(ForegroundInfo(notificationID, workNotif, FOREGROUND_SERVICE_TYPE_SPECIAL_USE))
|
val continueAfterPriorityIds = inputData.getBoolean("continue_after_priority_ids", true)
|
||||||
|
val time = System.currentTimeMillis() + 6000
|
||||||
|
queueState = if (priorityItemIDs.isEmpty()) {
|
||||||
|
dao.getActiveQueuedScheduledDownloadsUntil(time)
|
||||||
}else {
|
}else {
|
||||||
setForegroundAsync(ForegroundInfo(notificationID, workNotif))
|
dao.getActiveQueuedScheduledDownloadsUntilWithPriority(time, priorityItemIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
queuedItems.collectLatest { items ->
|
val downloadJobs = mutableMapOf<Long, Job>()
|
||||||
if (this@DownloadWorker.isStopped) return@collectLatest
|
|
||||||
|
|
||||||
runningYTDLInstances.clear()
|
queueState.collectLatest { queue ->
|
||||||
val activeDownloads = dao.getActiveDownloadsList()
|
val runningCount = queue.asSequence()
|
||||||
activeDownloads.forEach {
|
.filter { it.status == DownloadRepository.Status.Active.toString() }
|
||||||
runningYTDLInstances.add(it.id)
|
.count()
|
||||||
|
|
||||||
|
val queueItems = queue.asSequence()
|
||||||
|
.filter { it.status != DownloadRepository.Status.Active.toString() }
|
||||||
|
|
||||||
|
val queueCount = queueItems.count()
|
||||||
|
if (queueCount == 0 && runningCount == 0) {
|
||||||
|
cancelSelf()
|
||||||
|
return@collectLatest
|
||||||
}
|
}
|
||||||
|
|
||||||
val running = ArrayList(runningYTDLInstances)
|
|
||||||
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
|
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
|
||||||
if (items.isEmpty() && running.isEmpty()) {
|
|
||||||
WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id)
|
|
||||||
return@collectLatest
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useScheduler){
|
if (useScheduler){
|
||||||
if (items.none{it.downloadStartTime > 0L} && running.isEmpty() && !alarmScheduler.isDuringTheScheduledTime()) {
|
if (queueItems.none{ it.downloadStartTime > 0L } && runningCount == 0 && !alarmScheduler.isDuringTheScheduledTime()) {
|
||||||
WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id)
|
cancelSelf()
|
||||||
return@collectLatest
|
return@collectLatest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priorityItemIDs.isEmpty() && !continueAfterPriorityIds) {
|
val concurrentDownloads = sharedPreferences.getInt("concurrent_downloads", 1) - runningCount
|
||||||
WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id)
|
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()
|
||||||
return@collectLatest
|
return@collectLatest
|
||||||
}
|
}
|
||||||
|
|
||||||
val concurrentDownloads = sharedPreferences.getInt("concurrent_downloads", 1) - running.size
|
|
||||||
val eligibleDownloads = if (priorityItemIDs.isNotEmpty()) {
|
|
||||||
val tmp = priorityItemIDs.take(concurrentDownloads)
|
|
||||||
items.filter { it.id !in running && tmp.contains(it.id) }
|
|
||||||
}else{
|
|
||||||
items.take(concurrentDownloads).filter { it.id !in running }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eligibleDownloads.forEach{downloadItem ->
|
// 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 { download ->
|
||||||
|
downloadJobs[download.id] = launchDownloadJob(download)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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 })
|
val notification = notificationUtil.createDownloadServiceNotification(openDownloadQueue, downloadItem.title.ifEmpty { downloadItem.url })
|
||||||
notificationUtil.notify(downloadItem.id.toInt(), notification)
|
notificationUtil.notify(downloadItem.id.toInt(), notification)
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
val writtenPath = downloadItem.format.format_note.contains("-P ")
|
val writtenPath = downloadItem.format.format_note.contains("-P ")
|
||||||
val noCache = writtenPath || (!sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(downloadItem.downloadPath)).canWrite())
|
val noCache = writtenPath || (!sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(downloadItem.downloadPath)).canWrite())
|
||||||
|
|
||||||
val request = ytdlpUtil.buildYoutubeDLRequest(downloadItem)
|
val request = ytdlpUtil.buildYoutubeDLRequest(downloadItem)
|
||||||
downloadItem.status = DownloadRepository.Status.Active.toString()
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
delay(1500)
|
|
||||||
//update item if its incomplete
|
|
||||||
resultRepo.updateDownloadItem(downloadItem)?.apply {
|
|
||||||
val status = dao.checkStatus(this.id)
|
|
||||||
if (status == DownloadRepository.Status.Active){
|
|
||||||
dao.updateWithoutUpsert(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val cacheDir = FileUtil.getCachePath(context)
|
val cacheDir = FileUtil.getCachePath(context)
|
||||||
val tempFileDir = File(cacheDir, downloadItem.id.toString())
|
val tempFileDir = File(cacheDir, downloadItem.id.toString())
|
||||||
|
|
@ -186,7 +240,6 @@ class DownloadWorker(
|
||||||
val keepCache = sharedPreferences.getBoolean("keep_cache", false)
|
val keepCache = sharedPreferences.getBoolean("keep_cache", false)
|
||||||
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !downloadItem.incognito
|
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !downloadItem.incognito
|
||||||
|
|
||||||
|
|
||||||
val commandString = ytdlpUtil.parseYTDLRequestString(request)
|
val commandString = ytdlpUtil.parseYTDLRequestString(request)
|
||||||
val initialLogDetails = "Downloading:\n" +
|
val initialLogDetails = "Downloading:\n" +
|
||||||
"Title: ${downloadItem.title}\n" +
|
"Title: ${downloadItem.title}\n" +
|
||||||
|
|
@ -207,10 +260,20 @@ class DownloadWorker(
|
||||||
runBlocking {
|
runBlocking {
|
||||||
if (logDownloads) logItem.id = logRepo.insert(logItem)
|
if (logDownloads) logItem.id = logRepo.insert(logItem)
|
||||||
downloadItem.logID = logItem.id
|
downloadItem.logID = logItem.id
|
||||||
|
downloadItem.status = DownloadRepository.Status.Active.toString()
|
||||||
dao.update(downloadItem)
|
dao.update(downloadItem)
|
||||||
}
|
|
||||||
|
|
||||||
val eventBus = EventBus.getDefault()
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
delay(1500)
|
||||||
|
//update item if its incomplete
|
||||||
|
resultRepo.updateDownloadItem(downloadItem)?.apply {
|
||||||
|
val status = dao.checkStatus(id)
|
||||||
|
if (status == DownloadRepository.Status.Active){
|
||||||
|
dao.updateWithoutUpsert(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
YoutubeDL.getInstance().destroyProcessById(downloadItem.id.toString())
|
YoutubeDL.getInstance().destroyProcessById(downloadItem.id.toString())
|
||||||
|
|
@ -263,7 +326,8 @@ class DownloadWorker(
|
||||||
eventBus.post(WorkerProgress(100, "Moving file to ${FileUtil.formatPath(downloadLocation)}", downloadItem.id, downloadItem.logID))
|
eventBus.post(WorkerProgress(100, "Moving file to ${FileUtil.formatPath(downloadLocation)}", downloadItem.id, downloadItem.logID))
|
||||||
try {
|
try {
|
||||||
finalPaths = withContext(Dispatchers.IO){
|
finalPaths = withContext(Dispatchers.IO){
|
||||||
FileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation, keepCache){ p ->
|
FileUtil.moveFile(tempFileDir.absoluteFile,
|
||||||
|
context, downloadLocation, keepCache){ p ->
|
||||||
eventBus.post(WorkerProgress(p, "Moving file to ${FileUtil.formatPath(downloadLocation)}", downloadItem.id, downloadItem.logID))
|
eventBus.post(WorkerProgress(p, "Moving file to ${FileUtil.formatPath(downloadLocation)}", downloadItem.id, downloadItem.logID))
|
||||||
}
|
}
|
||||||
}.filter { !it.matches("\\.(description)|(txt)\$".toRegex()) }.toMutableList()
|
}.filter { !it.matches("\\.(description)|(txt)\$".toRegex()) }.toMutableList()
|
||||||
|
|
@ -360,7 +424,7 @@ class DownloadWorker(
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
||||||
}
|
}
|
||||||
if (this@DownloadWorker.isStopped) return@onFailure
|
if (isStopped) return@onFailure
|
||||||
if (it is YoutubeDL.CanceledException) return@onFailure
|
if (it is YoutubeDL.CanceledException) return@onFailure
|
||||||
if (it.message?.contains("JSONDecodeError") == true) {
|
if (it.message?.contains("JSONDecodeError") == true) {
|
||||||
val cachePath = "${FileUtil.getCachePath(context)}infojsons"
|
val cachePath = "${FileUtil.getCachePath(context)}infojsons"
|
||||||
|
|
@ -403,25 +467,8 @@ class DownloadWorker(
|
||||||
eventBus.post(WorkerProgress(100, it.toString(), downloadItem.id, downloadItem.logID))
|
eventBus.post(WorkerProgress(100, it.toString(), downloadItem.id, downloadItem.logID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (eligibleDownloads.isNotEmpty()){
|
|
||||||
eligibleDownloads.forEach {
|
|
||||||
it.status = DownloadRepository.Status.Active.toString()
|
|
||||||
priorityItemIDs.remove(it.id)
|
|
||||||
}
|
|
||||||
dao.updateMultiple(eligibleDownloads)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return Result.success()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val runningYTDLInstances: MutableList<Long> = mutableListOf()
|
|
||||||
const val TAG = "DownloadWorker"
|
const val TAG = "DownloadWorker"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package com.deniscerri.ytdl.work
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
|
@ -42,7 +42,7 @@ class MoveCacheFilesWorker(
|
||||||
val notification = notificationUtil.createMoveCacheFilesNotification(pendingIntent, NotificationUtil.DOWNLOAD_MISC_CHANNEL_ID)
|
val notification = notificationUtil.createMoveCacheFilesNotification(pendingIntent, NotificationUtil.DOWNLOAD_MISC_CHANNEL_ID)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT > 33) {
|
if (Build.VERSION.SDK_INT > 33) {
|
||||||
setForegroundAsync(ForegroundInfo(id, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE))
|
setForegroundAsync(ForegroundInfo(id, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC))
|
||||||
}else{
|
}else{
|
||||||
setForegroundAsync(ForegroundInfo(id, notification))
|
setForegroundAsync(ForegroundInfo(id, notification))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.deniscerri.ytdl.work
|
package com.deniscerri.ytdl.work
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
|
@ -57,7 +57,7 @@ class ObserveSourceWorker(
|
||||||
val workerID = System.currentTimeMillis().toInt()
|
val workerID = System.currentTimeMillis().toInt()
|
||||||
val notification = notificationUtil.createObserveSourcesNotification(item.name)
|
val notification = notificationUtil.createObserveSourcesNotification(item.name)
|
||||||
if (Build.VERSION.SDK_INT > 33) {
|
if (Build.VERSION.SDK_INT > 33) {
|
||||||
setForegroundAsync(ForegroundInfo(workerID, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE))
|
setForegroundAsync(ForegroundInfo(workerID, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC))
|
||||||
}else{
|
}else{
|
||||||
setForegroundAsync(ForegroundInfo(workerID, notification))
|
setForegroundAsync(ForegroundInfo(workerID, notification))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package com.deniscerri.ytdl.work
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
|
@ -53,7 +53,7 @@ class TerminalDownloadWorker(
|
||||||
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
val notification = notificationUtil.createDownloadServiceNotification(pendingIntent, command.take(65), NotificationUtil.DOWNLOAD_TERMINAL_RUNNING_NOTIFICATION_ID)
|
val notification = notificationUtil.createDownloadServiceNotification(pendingIntent, command.take(65), NotificationUtil.DOWNLOAD_TERMINAL_RUNNING_NOTIFICATION_ID)
|
||||||
if (Build.VERSION.SDK_INT > 33) {
|
if (Build.VERSION.SDK_INT > 33) {
|
||||||
setForegroundAsync(ForegroundInfo(itemId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE))
|
setForegroundAsync(ForegroundInfo(itemId, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC))
|
||||||
}else{
|
}else{
|
||||||
setForegroundAsync(ForegroundInfo(itemId, notification))
|
setForegroundAsync(ForegroundInfo(itemId, notification))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.deniscerri.ytdl.work
|
package com.deniscerri.ytdl.work
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.work.ForegroundInfo
|
import androidx.work.ForegroundInfo
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
|
|
@ -32,7 +32,7 @@ class UpdateMultipleDownloadsDataWorker(
|
||||||
val notification = notificationUtil.createDataUpdateNotification()
|
val notification = notificationUtil.createDataUpdateNotification()
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT > 33) {
|
if (Build.VERSION.SDK_INT > 33) {
|
||||||
setForegroundAsync(ForegroundInfo(workID, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE))
|
setForegroundAsync(ForegroundInfo(workID, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC))
|
||||||
}else{
|
}else{
|
||||||
setForegroundAsync(ForegroundInfo(workID, notification))
|
setForegroundAsync(ForegroundInfo(workID, notification))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.deniscerri.ytdl.work
|
package com.deniscerri.ytdl.work
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.work.ForegroundInfo
|
import androidx.work.ForegroundInfo
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
|
|
@ -33,7 +33,7 @@ class UpdateMultipleDownloadsFormatsWorker(
|
||||||
val notification = notificationUtil.createFormatsUpdateNotification()
|
val notification = notificationUtil.createFormatsUpdateNotification()
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT > 33) {
|
if (Build.VERSION.SDK_INT > 33) {
|
||||||
setForegroundAsync(ForegroundInfo(workID, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE))
|
setForegroundAsync(ForegroundInfo(workID, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC))
|
||||||
}else{
|
}else{
|
||||||
setForegroundAsync(ForegroundInfo(workID, notification))
|
setForegroundAsync(ForegroundInfo(workID, notification))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.deniscerri.ytdl.work
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.WorkInfo
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
val Context.workManager: WorkManager
|
||||||
|
get() = WorkManager.getInstance(this)
|
||||||
|
|
||||||
|
fun WorkManager.isRunning(tag: String): Boolean {
|
||||||
|
val list = this.getWorkInfosByTag(tag).get()
|
||||||
|
return list.count { it.state == WorkInfo.State.RUNNING } > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes this worker run in the context of a foreground service.
|
||||||
|
*
|
||||||
|
* Note that this function is a no-op if the process is subject to foreground
|
||||||
|
* service restrictions.
|
||||||
|
*
|
||||||
|
* Moving to foreground service context requires the worker to run a bit longer,
|
||||||
|
* allowing Service.startForeground() to be called and avoiding system crash.
|
||||||
|
*/
|
||||||
|
suspend fun CoroutineWorker.setForegroundSafely() {
|
||||||
|
try {
|
||||||
|
setForeground(getForegroundInfo())
|
||||||
|
delay(500)
|
||||||
|
} catch (e: IllegalStateException) {
|
||||||
|
Log.e("ERROR", "Not allowed to set foreground job")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue