slight fixes

This commit is contained in:
deniscerri 2024-12-29 22:42:34 +01:00
parent 3c3ef92cbd
commit 2e5fc3206e
No known key found for this signature in database
GPG key ID: 95C43D517D830350
29 changed files with 381 additions and 136 deletions

View file

@ -18,7 +18,7 @@
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- alarm scheduler--> <!-- alarm scheduler-->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<!-- queueing processing downloads in the background--> <!-- queueing processing downloads in the background-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

View file

@ -153,6 +153,9 @@ interface DownloadDao {
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1") @Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
fun getDownloadById(id: Long) : DownloadItem fun getDownloadById(id: Long) : DownloadItem
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
fun getNullableDownloadById(id: Long) : DownloadItem?
@Query("SELECT * FROM downloads WHERE id IN (:ids)") @Query("SELECT * FROM downloads WHERE id IN (:ids)")
fun getDownloadsByIds(ids: List<Long>) : List<DownloadItem> fun getDownloadsByIds(ids: List<Long>) : List<DownloadItem>

View file

@ -106,8 +106,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
} }
} }
suspend fun update(item: DownloadItem){ suspend fun update(item: DownloadItem) : Long {
downloadDao.update(item) return downloadDao.update(item)
} }
suspend fun updateAll(list: List<DownloadItem>) : List<DownloadItem> { suspend fun updateAll(list: List<DownloadItem>) : List<DownloadItem> {

View file

@ -106,25 +106,37 @@ class HistoryRepository(private val historyDao: HistoryDao) {
suspend fun deleteAllWithIDs(ids: List<Long>, deleteFile: Boolean = false){ suspend fun deleteAllWithIDs(ids: List<Long>, deleteFile: Boolean = false){
if (deleteFile){ if (deleteFile){
historyDao.getAllHistoryByIDs(ids).forEach { item -> ids.chunked(500).forEach { chunks ->
item.downloadPath.forEach { historyDao.getAllHistoryByIDs(chunks).forEach { item ->
FileUtil.deleteFile(it) item.downloadPath.forEach {
FileUtil.deleteFile(it)
}
} }
} }
} }
historyDao.deleteAllByIDs(ids) ids.chunked(500).forEach { chunks ->
historyDao.deleteAllByIDs(chunks)
}
} }
suspend fun deleteAllWithIDsCheckFiles(ids: List<Long>){ suspend fun deleteAllWithIDsCheckFiles(ids: List<Long>){
val idsToDelete = mutableListOf<Long>() val idsToDelete = mutableListOf<Long>()
historyDao.getAllHistoryByIDs(ids).forEach { item -> ids.chunked(500).forEach { chunks ->
val filesNotPresent = item.downloadPath.all { !File(it).exists() && it.isNotBlank()} historyDao.getAllHistoryByIDs(chunks).forEach { item ->
if (filesNotPresent) { val filesNotPresent = item.downloadPath.all { !File(it).exists() && it.isNotBlank()}
idsToDelete.add(item.id) if (filesNotPresent) {
idsToDelete.add(item.id)
}
} }
} }
if (idsToDelete.isNotEmpty()) { if (idsToDelete.isNotEmpty()) {
historyDao.deleteAllByIDs(idsToDelete) idsToDelete.chunked(500).forEach { chunked ->
historyDao.deleteAllByIDs(chunked)
}
} }
} }
@ -133,8 +145,12 @@ class HistoryRepository(private val historyDao: HistoryDao) {
) )
fun getDownloadPathsFromIDs(ids: List<Long>) : List<List<String>> { fun getDownloadPathsFromIDs(ids: List<Long>) : List<List<String>> {
val res = historyDao.getDownloadPathsFromIDs(ids) val res : MutableList<List<String>> = mutableListOf()
return res.map { it.downloadPath } ids.chunked(500).forEach { chunks ->
val tmp = historyDao.getDownloadPathsFromIDs(chunks)
res.addAll(tmp.map { it.downloadPath })
}
return res
} }
suspend fun deleteDuplicates(){ suspend fun deleteDuplicates(){

View file

@ -12,6 +12,7 @@ import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
import com.deniscerri.ytdl.util.Extensions.isYoutubeChannelURL import com.deniscerri.ytdl.util.Extensions.isYoutubeChannelURL
import com.deniscerri.ytdl.util.Extensions.isYoutubeURL import com.deniscerri.ytdl.util.Extensions.isYoutubeURL
import com.deniscerri.ytdl.util.Extensions.isYoutubeWatchVideosURL import com.deniscerri.ytdl.util.Extensions.isYoutubeWatchVideosURL
import com.deniscerri.ytdl.util.Extensions.needsDataUpdating
import com.deniscerri.ytdl.util.extractors.GoogleApiUtil import com.deniscerri.ytdl.util.extractors.GoogleApiUtil
import com.deniscerri.ytdl.util.extractors.newpipe.NewPipeUtil import com.deniscerri.ytdl.util.extractors.newpipe.NewPipeUtil
import com.deniscerri.ytdl.util.extractors.YTDLPUtil import com.deniscerri.ytdl.util.extractors.YTDLPUtil
@ -414,7 +415,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
suspend fun updateDownloadItem( suspend fun updateDownloadItem(
downloadItem: DownloadItem downloadItem: DownloadItem
) : DownloadItem? { ) : DownloadItem? {
if (downloadItem.title.isEmpty() || downloadItem.author.isEmpty() || downloadItem.thumb.isEmpty()){ if (downloadItem.needsDataUpdating()){
runCatching { runCatching {
val info = getResultsFromSource(downloadItem.url, resetResults = false, addToResults = false, singleItem = true).first() val info = getResultsFromSource(downloadItem.url, resetResults = false, addToResults = false, singleItem = true).first()
if (downloadItem.title.isEmpty()) downloadItem.title = info.title if (downloadItem.title.isEmpty()) downloadItem.title = info.title

View file

@ -39,12 +39,14 @@ import com.deniscerri.ytdl.database.repository.DownloadRepository
import com.deniscerri.ytdl.database.repository.HistoryRepository import com.deniscerri.ytdl.database.repository.HistoryRepository
import com.deniscerri.ytdl.database.repository.ResultRepository import com.deniscerri.ytdl.database.repository.ResultRepository
import com.deniscerri.ytdl.ui.downloadcard.MultipleItemFormatTuple import com.deniscerri.ytdl.ui.downloadcard.MultipleItemFormatTuple
import com.deniscerri.ytdl.util.Extensions.needsDataUpdating
import com.deniscerri.ytdl.util.Extensions.toListString import com.deniscerri.ytdl.util.Extensions.toListString
import com.deniscerri.ytdl.util.FileUtil import com.deniscerri.ytdl.util.FileUtil
import com.deniscerri.ytdl.util.FormatUtil import com.deniscerri.ytdl.util.FormatUtil
import com.deniscerri.ytdl.util.NotificationUtil import com.deniscerri.ytdl.util.NotificationUtil
import com.deniscerri.ytdl.util.extractors.YTDLPUtil import com.deniscerri.ytdl.util.extractors.YTDLPUtil
import com.deniscerri.ytdl.work.AlarmScheduler import com.deniscerri.ytdl.work.AlarmScheduler
import com.deniscerri.ytdl.work.UpdateMultipleDownloadsDataWorker
import com.deniscerri.ytdl.work.UpdateMultipleDownloadsFormatsWorker import com.deniscerri.ytdl.work.UpdateMultipleDownloadsFormatsWorker
import com.google.gson.Gson import com.google.gson.Gson
import com.yausername.youtubedl_android.YoutubeDL import com.yausername.youtubedl_android.YoutubeDL
@ -217,6 +219,14 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
repository.update(item) repository.update(item)
} }
suspend fun putToSaved(item: DownloadItem) {
item.status = DownloadRepository.Status.Saved.toString()
val id = repository.update(item)
if (item.needsDataUpdating()) {
continueUpdatingDataInBackground(listOf(id))
}
}
fun getItemByID(id: Long) : DownloadItem { fun getItemByID(id: Long) : DownloadItem {
return repository.getItemByID(id) return repository.getItemByID(id)
} }
@ -641,7 +651,10 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
//repository.insert(downloadItem) //repository.insert(downloadItem)
} }
} }
repository.insertAll(toInsert) toInsert.chunked(500).forEach { chunked ->
repository.insertAll(chunked)
}
processingItems.emit(false) processingItems.emit(false)
} catch (e: Exception) { } catch (e: Exception) {
deleteProcessing() deleteProcessing()
@ -678,7 +691,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
//repository.insert(downloadItem) //repository.insert(downloadItem)
} }
} }
repository.insertAll(toInsert) toInsert.chunked(500).forEach { chunked ->
repository.insertAll(chunked)
}
processingItems.emit(false) processingItems.emit(false)
}catch (e: Exception) { }catch (e: Exception) {
deleteProcessing() deleteProcessing()
@ -1031,27 +1046,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
result.message = repository.startDownloadWorker(queued, context).getOrElse { "" } result.message = repository.startDownloadWorker(queued, context).getOrElse { "" }
if(!useScheduler){ val ids = queued.filter { it.needsDataUpdating() }.map { it.id }
CoroutineScope(Dispatchers.IO).launch { continueUpdatingDataInBackground(ids)
queued.filter { it.downloadStartTime != 0L && (it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty()) }.forEach {
kotlin.runCatching {
resultRepository.updateDownloadItem(it)?.apply {
repository.updateWithoutUpsert(this)
}
}
}
}
}else{
CoroutineScope(Dispatchers.IO).launch {
queued.filter { it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty() }.forEach {
kotlin.runCatching {
resultRepository.updateDownloadItem(it)?.apply {
repository.updateWithoutUpsert(this)
}
}
}
}
}
} }
@ -1188,6 +1184,25 @@ 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(),
ExistingWorkPolicy.REPLACE,
workRequest
)
}
suspend fun updateProcessingType(newType: Type) { suspend fun updateProcessingType(newType: Type) {
val processing = repository.getProcessingDownloads() val processing = repository.getProcessingDownloads()
processing.apply { processing.apply {

View file

@ -15,6 +15,7 @@ import android.view.*
import android.view.View.* import android.view.View.*
import android.widget.* import android.widget.*
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
@ -41,8 +42,10 @@ import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
import com.deniscerri.ytdl.ui.adapter.HomeAdapter import com.deniscerri.ytdl.ui.adapter.HomeAdapter
import com.deniscerri.ytdl.ui.adapter.SearchSuggestionsAdapter import com.deniscerri.ytdl.ui.adapter.SearchSuggestionsAdapter
import com.deniscerri.ytdl.ui.more.WebViewActivity
import com.deniscerri.ytdl.util.Extensions.enableFastScroll import com.deniscerri.ytdl.util.Extensions.enableFastScroll
import com.deniscerri.ytdl.util.Extensions.isURL import com.deniscerri.ytdl.util.Extensions.isURL
import com.deniscerri.ytdl.util.FileUtil
import com.deniscerri.ytdl.util.NotificationUtil import com.deniscerri.ytdl.util.NotificationUtil
import com.deniscerri.ytdl.util.ThemeUtil import com.deniscerri.ytdl.util.ThemeUtil
import com.deniscerri.ytdl.util.UiUtil import com.deniscerri.ytdl.util.UiUtil
@ -64,6 +67,8 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File
import java.net.URL
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -263,25 +268,37 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
if (res.errorMessage != null){ if (res.errorMessage != null){
val isSingleQueryAndURL = queryList.size == 1 && Patterns.WEB_URL.matcher(queryList.first()).matches() val isSingleQueryAndURL = queryList.size == 1 && Patterns.WEB_URL.matcher(queryList.first()).matches()
kotlin.runCatching { UiUtil.handleNoResults(requireActivity(), res.errorMessage!!, continueAnyway = isSingleQueryAndURL, continued = { kotlin.runCatching {
lifecycleScope.launch { UiUtil.handleNoResults(requireActivity(), res.errorMessage!!,
if (sharedPreferences!!.getBoolean("download_card", true)) { url = if (isSingleQueryAndURL) queryList.first() else null,
withContext(Dispatchers.Main){ continueAnyway = isSingleQueryAndURL,
showSingleDownloadSheet( continued = {
resultItem = downloadViewModel.createEmptyResultItem(queryList.first()), lifecycleScope.launch {
type = DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!), if (sharedPreferences!!.getBoolean("download_card", true)) {
disableUpdateData = true withContext(Dispatchers.Main){
showSingleDownloadSheet(
resultItem = downloadViewModel.createEmptyResultItem(queryList.first()),
type = DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!),
disableUpdateData = true
)
}
} else {
val downloadItem = downloadViewModel.createDownloadItemFromResult(
result = downloadViewModel.createEmptyResultItem(queryList.first()),
givenType = DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!)
) )
downloadViewModel.queueDownloads(listOf(downloadItem))
} }
} else {
val downloadItem = downloadViewModel.createDownloadItemFromResult(
result = downloadViewModel.createEmptyResultItem(queryList.first()),
givenType = DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!)
)
downloadViewModel.queueDownloads(listOf(downloadItem))
} }
} },
}, closed = {}) } cookieFetch = {
val myIntent = Intent(requireContext(), WebViewActivity::class.java)
myIntent.putExtra("url", "https://${URL(queryList.first()).host}")
cookiesFetchedResultLauncher.launch(myIntent)
},
closed = {}
)
}
resultViewModel.uiState.update {it.copy(errorMessage = null) } resultViewModel.uiState.update {it.copy(errorMessage = null) }
} }
@ -324,6 +341,15 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
} }
private var cookiesFetchedResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
sharedPreferences?.edit()?.putBoolean("use_cookies", true)?.apply()
startSearch()
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if(arguments?.getString("url") == null){ if(arguments?.getString("url") == null){
@ -653,6 +679,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
resultViewModel.addSearchQueryToHistory(q) resultViewModel.addSearchQueryToHistory(q)
} }
} }
startSearch()
}
private fun startSearch() {
lifecycleScope.launch(Dispatchers.IO){ lifecycleScope.launch(Dispatchers.IO){
resultViewModel.deleteAll() resultViewModel.deleteAll()
if(sharedPreferences!!.getBoolean("quick_download", false) || sharedPreferences!!.getString("preferred_download_type", "video") == "command"){ if(sharedPreferences!!.getBoolean("quick_download", false) || sharedPreferences!!.getString("preferred_download_type", "video") == "command"){

View file

@ -161,7 +161,7 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
true true
} }
card.setOnClickListener { card.setOnClickListener {
if (checkedItems.size > 0) { if (checkedItems.size > 0 || inverted) {
checkCard(card, item.id, position) checkCard(card, item.id, position)
} else { } else {
onItemClickListener.onCardClick(item.id, finalFilePresent) onItemClickListener.onCardClick(item.id, finalFilePresent)

View file

@ -1,8 +1,10 @@
package com.deniscerri.ytdl.ui.downloadcard package com.deniscerri.ytdl.ui.downloadcard
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.app.Dialog import android.app.Dialog
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build import android.os.Build
@ -16,6 +18,7 @@ import android.widget.Button
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.edit import androidx.core.content.edit
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -39,6 +42,7 @@ import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
import com.deniscerri.ytdl.receiver.ShareActivity import com.deniscerri.ytdl.receiver.ShareActivity
import com.deniscerri.ytdl.ui.BaseActivity import com.deniscerri.ytdl.ui.BaseActivity
import com.deniscerri.ytdl.ui.more.WebViewActivity
import com.deniscerri.ytdl.util.UiUtil import com.deniscerri.ytdl.util.UiUtil
import com.facebook.shimmer.ShimmerFrameLayout import com.facebook.shimmer.ShimmerFrameLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
@ -58,6 +62,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.net.URL
class DownloadBottomSheetDialog : BottomSheetDialogFragment() { class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
@ -397,9 +402,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
dd.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() } dd.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
dd.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int -> dd.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
lifecycleScope.launch(Dispatchers.IO){ lifecycleScope.launch(Dispatchers.IO){
val item = getDownloadItem() downloadViewModel.putToSaved(getDownloadItem())
item.status = DownloadRepository.Status.Saved.toString()
downloadViewModel.updateDownload(item)
dismiss() dismiss()
} }
} }
@ -468,16 +471,25 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
} }
} }
lifecycleScope.launch { lifecycleScope.launch {
resultViewModel.uiState.collectLatest { res -> resultViewModel.uiState.collectLatest { res ->
if (res.errorMessage != null){ if (res.errorMessage != null){
kotlin.runCatching { UiUtil.handleNoResults(requireActivity(), res.errorMessage!!, true, continued = {}, closed = { kotlin.runCatching {
dismiss() UiUtil.handleNoResults(requireActivity(), res.errorMessage!!,
}) } url = result.url,
continueAnyway = true,
continued = {},
cookieFetch = {
val myIntent = Intent(requireContext(), WebViewActivity::class.java)
myIntent.putExtra("url", "https://${URL(result.url).host}")
cookiesFetchedResultLauncher.launch(myIntent)
},
closed = {
dismiss()
}
)
}
resultViewModel.uiState.update {it.copy(errorMessage = null) } resultViewModel.uiState.update {it.copy(errorMessage = null) }
} }
} }
@ -657,6 +669,16 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
} }
} }
private var cookiesFetchedResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
sharedPreferences.edit().putBoolean("use_cookies", true).apply()
updateItem.isVisible = true
initUpdateData()
}
}
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem { private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem {
return fragmentAdapter.getDownloadItem(selectedTabPosition) return fragmentAdapter.getDownloadItem(selectedTabPosition)
} }

View file

@ -236,7 +236,7 @@ class FormatSelectionBottomSheetDialog(
} }
}.onFailure { err -> }.onFailure { err ->
withContext(Dispatchers.Main){ withContext(Dispatchers.Main){
UiUtil.handleNoResults(requireActivity(), err.message.toString(), false, continued = {}, closed = {}) UiUtil.handleNoResults(requireActivity(), err.message.toString(), null, false, continued = {}, closed = {}, cookieFetch = {})
} }
} }

View file

@ -98,7 +98,8 @@ class WebViewActivity : BaseActivity() {
} }
cookieManager.removeAllCookies(null) cookieManager.removeAllCookies(null)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
onBackPressedDispatcher.onBackPressed() this@WebViewActivity.setResult(RESULT_OK)
this@WebViewActivity.finish()
} }
} }
} }

View file

@ -59,7 +59,8 @@ class ProcessingSettingsFragment : BaseSettingsFragment() {
setOnPreferenceClickListener { setOnPreferenceClickListener {
val pref = prefs.getString("format_importance_audio", itemValues.joinToString(","))!! val pref = prefs.getString("format_importance_audio", itemValues.joinToString(","))!!
val itms = pref.split(",").map { val prefArr = pref.split(",")
val itms = itemValues.sortedBy { prefArr.indexOf(it) }.map {
Pair(it, items[itemValues.indexOf(it)]) Pair(it, items[itemValues.indexOf(it)])
}.toMutableList() }.toMutableList()
@ -80,7 +81,8 @@ class ProcessingSettingsFragment : BaseSettingsFragment() {
setOnPreferenceClickListener { setOnPreferenceClickListener {
val pref = prefs.getString("format_importance_video", itemValues.joinToString(","))!! val pref = prefs.getString("format_importance_video", itemValues.joinToString(","))!!
val itms = pref.split(",").map { val prefArr = pref.split(",")
val itms = itemValues.sortedBy { prefArr.indexOf(it) }.map {
Pair(it, items[itemValues.indexOf(it)]) Pair(it, items[itemValues.indexOf(it)])
}.toMutableList() }.toMutableList()

View file

@ -558,4 +558,9 @@ object Extensions {
t4.second t4.second
) )
} }
fun DownloadItem.needsDataUpdating() : Boolean {
return this.title.isBlank() || this.author.isBlank() || this.thumb.isBlank()
}
} }

View file

@ -35,6 +35,7 @@ class FormatUtil(private var context: Context) {
val orderPreferences = sharedPreferences.getString("format_importance_audio", itemValues.joinToString(","))!!.split(",").toMutableSet() val orderPreferences = sharedPreferences.getString("format_importance_audio", itemValues.joinToString(","))!!.split(",").toMutableSet()
if (preferSmallerFormats) { if (preferSmallerFormats) {
orderPreferences.add("smallsize") orderPreferences.add("smallsize")
orderPreferences.remove("file_size")
} }
return orderPreferences return orderPreferences
@ -46,6 +47,7 @@ class FormatUtil(private var context: Context) {
val orderPreferences = sharedPreferences.getString("format_importance_video", itemValues.joinToString(","))!!.split(",").toMutableSet() val orderPreferences = sharedPreferences.getString("format_importance_video", itemValues.joinToString(","))!!.split(",").toMutableSet()
if (preferSmallerFormats) { if (preferSmallerFormats) {
orderPreferences.add("smallsize") orderPreferences.add("smallsize")
orderPreferences.remove("file_size")
} }
return orderPreferences return orderPreferences
@ -63,7 +65,10 @@ class FormatUtil(private var context: Context) {
"smallsize" -> { "smallsize" -> {
(a.filesize).compareTo(b.filesize) (a.filesize).compareTo(b.filesize)
} }
"file_size" -> {
val result = b.filesize.compareTo(a.filesize)
result
}
"id" -> { "id" -> {
(audioFormatIDPreference.contains(b.format_id)).compareTo( (audioFormatIDPreference.contains(b.format_id)).compareTo(
audioFormatIDPreference.contains(a.format_id) audioFormatIDPreference.contains(a.format_id)
@ -119,6 +124,10 @@ class FormatUtil(private var context: Context) {
val result = a.filesize.compareTo(b.filesize) val result = a.filesize.compareTo(b.filesize)
result result
} }
"file_size" -> {
val result = b.filesize.compareTo(a.filesize)
result
}
"id" -> { "id" -> {
videoFormatIDPreference.contains(b.format_id).compareTo(videoFormatIDPreference.contains(a.format_id)) videoFormatIDPreference.contains(b.format_id).compareTo(videoFormatIDPreference.contains(a.format_id))
} }

View file

@ -621,6 +621,63 @@ class NotificationUtil(var context: Context) {
} }
fun createDataUpdateNotification(): Notification {
val notificationBuilder = getBuilder(DOWNLOAD_MISC_CHANNEL_ID)
return notificationBuilder
.setContentTitle(resources.getString(R.string.updating_download_data))
.setOngoing(true)
.setCategory(Notification.CATEGORY_PROGRESS)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setLargeIcon(
BitmapFactory.decodeResource(
resources,
R.drawable.ic_launcher_foreground_large
)
)
.setContentText("")
.setPriority(NotificationCompat.PRIORITY_LOW)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setProgress(PROGRESS_MAX, PROGRESS_CURR, false)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.clearActions()
.build()
}
@SuppressLint("MissingPermission")
fun updateDataUpdateNotification(
workID: Int,
workTag: String,
progress: Int,
queue: Int,
) {
val notificationBuilder = getBuilder(DOWNLOAD_MISC_CHANNEL_ID)
val contentText = """${queue - progress} ${resources.getString(R.string.items_left)}"""
val cancelIntent = Intent(context, CancelWorkReceiver::class.java)
cancelIntent.putExtra("workTag", workTag)
val cancelNotificationPendingIntent = PendingIntent.getBroadcast(
context,
workID,
cancelIntent,
PendingIntent.FLAG_IMMUTABLE
)
try {
notificationBuilder.setProgress(queue, progress, progress == 0)
.setContentTitle(resources.getString(R.string.updating_download_data))
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
.clearActions()
.addAction(0, resources.getString(R.string.cancel), cancelNotificationPendingIntent)
notificationManager.notify(workID, notificationBuilder.build())
} catch (e: Exception) {
e.printStackTrace()
}
}
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
fun showFormatsUpdatedNotification(downloadIds: List<Long>) { fun showFormatsUpdatedNotification(downloadIds: List<Long>) {
val notificationBuilder = getBuilder(DOWNLOAD_FINISHED_CHANNEL_ID) val notificationBuilder = getBuilder(DOWNLOAD_FINISHED_CHANNEL_ID)

View file

@ -5,6 +5,7 @@ import android.animation.ObjectAnimator
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.Dialog
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
@ -33,6 +34,7 @@ import android.widget.PopupMenu
import android.widget.RadioButton import android.widget.RadioButton
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.DimenRes import androidx.annotation.DimenRes
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
@ -62,6 +64,7 @@ import com.deniscerri.ytdl.database.repository.DownloadRepository
import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdl.ui.downloadcard.VideoCutListener import com.deniscerri.ytdl.ui.downloadcard.VideoCutListener
import com.deniscerri.ytdl.ui.more.WebViewActivity
import com.deniscerri.ytdl.util.Extensions.enableTextHighlight import com.deniscerri.ytdl.util.Extensions.enableTextHighlight
import com.deniscerri.ytdl.util.Extensions.getMediaDuration import com.deniscerri.ytdl.util.Extensions.getMediaDuration
import com.deniscerri.ytdl.util.Extensions.toStringDuration import com.deniscerri.ytdl.util.Extensions.toStringDuration
@ -94,6 +97,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.net.URL
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Locale import java.util.Locale
@ -1633,9 +1637,7 @@ object UiUtil {
} }
} }
fun handleNoResults(context: Activity, message: String, url: String? = null, continueAnyway: Boolean = false, continued: () -> Unit, closed: () -> Unit, cookieFetch: () -> Unit) {
fun handleNoResults(context: Activity, message: String, continueAnyway: Boolean = false, continued: () -> Unit, closed: () -> Unit) {
val errDialog = MaterialAlertDialogBuilder(context) val errDialog = MaterialAlertDialogBuilder(context)
.setTitle(R.string.no_results) .setTitle(R.string.no_results)
.setMessage(message) .setMessage(message)
@ -1646,12 +1648,19 @@ object UiUtil {
d?.dismiss() d?.dismiss()
} }
if (continueAnyway) { val cookieRelated = message.contains("cookie", true) || message.contains("sign in", true)
if (cookieRelated && !url.isNullOrBlank()) {
errDialog.setNeutralButton(context.getString(R.string.get_cookies)) { d: DialogInterface?, _ : Int ->
cookieFetch()
}
}else if (continueAnyway) {
errDialog.setNeutralButton(R.string.continue_anyway) {d: DialogInterface?, _:Int -> errDialog.setNeutralButton(R.string.continue_anyway) {d: DialogInterface?, _:Int ->
continued() continued()
} }
} }
errDialog.setOnCancelListener { errDialog.setOnCancelListener {
closed() closed()
} }

View file

@ -228,7 +228,7 @@ class UpdateUtil(var context: Context) {
updatingYTDL = true updatingYTDL = true
val channel = if (c.isNullOrBlank()) sharedPreferences.getString("ytdlp_source", "nightly") else c val channel = if (c.isNullOrBlank()) sharedPreferences.getString("ytdlp_source", "stable") else c
val request = YoutubeDLRequest(emptyList()) val request = YoutubeDLRequest(emptyList())
request.addOption("--update-to", "${channel}@latest") request.addOption("--update-to", "${channel}@latest")

View file

@ -110,7 +110,7 @@ class YTDLPUtil(private val context: Context) {
} }
} }
if (searchEngine == "ytsearch" || query.isYoutubeURL()) { if (searchEngine == "ytsearch" || query.isYoutubeURL()) {
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}") request.setYoutubeExtractorArgs()
} }
request.addOption("--flat-playlist") request.addOption("--flat-playlist")
@ -250,7 +250,7 @@ class YTDLPUtil(private val context: Context) {
fun getYoutubeWatchLater() : ArrayList<ResultItem> { fun getYoutubeWatchLater() : ArrayList<ResultItem> {
val request = YoutubeDLRequest(listOf()) val request = YoutubeDLRequest(listOf())
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}") request.setYoutubeExtractorArgs()
request.addOption( "-j") request.addOption( "-j")
request.addOption("--flat-playlist") request.addOption("--flat-playlist")
request.applyDefaultOptionsForFetchingData() request.applyDefaultOptionsForFetchingData()
@ -269,7 +269,7 @@ class YTDLPUtil(private val context: Context) {
fun getYoutubeRecommendations() : ArrayList<ResultItem> { fun getYoutubeRecommendations() : ArrayList<ResultItem> {
val request = YoutubeDLRequest(listOf()) val request = YoutubeDLRequest(listOf())
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}") request.setYoutubeExtractorArgs()
request.addOption( "-j") request.addOption( "-j")
request.addOption("--flat-playlist") request.addOption("--flat-playlist")
request.applyDefaultOptionsForFetchingData() request.applyDefaultOptionsForFetchingData()
@ -288,7 +288,7 @@ class YTDLPUtil(private val context: Context) {
fun getYoutubeLikedVideos() : ArrayList<ResultItem> { fun getYoutubeLikedVideos() : ArrayList<ResultItem> {
val request = YoutubeDLRequest(listOf()) val request = YoutubeDLRequest(listOf())
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}") request.setYoutubeExtractorArgs()
request.addOption( "-j") request.addOption( "-j")
request.addOption("--flat-playlist") request.addOption("--flat-playlist")
request.applyDefaultOptionsForFetchingData() request.applyDefaultOptionsForFetchingData()
@ -307,7 +307,7 @@ class YTDLPUtil(private val context: Context) {
fun getYoutubeWatchHistory() : ArrayList<ResultItem> { fun getYoutubeWatchHistory() : ArrayList<ResultItem> {
val request = YoutubeDLRequest(listOf()) val request = YoutubeDLRequest(listOf())
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}") request.setYoutubeExtractorArgs()
request.addOption( "-j") request.addOption( "-j")
request.addOption("--flat-playlist") request.addOption("--flat-playlist")
request.applyDefaultOptionsForFetchingData() request.applyDefaultOptionsForFetchingData()
@ -574,9 +574,12 @@ class YTDLPUtil(private val context: Context) {
}else{ }else{
addOption("-I", "${downloadItem.playlistIndex!!}:${downloadItem.playlistIndex}") addOption("-I", "${downloadItem.playlistIndex!!}:${downloadItem.playlistIndex}")
} }
addOption("-i")
} }
} }
request.addOption("--newline")
val metadataCommands = mutableListOf<String>() val metadataCommands = mutableListOf<String>()
if (downloadItem.playlistIndex != null && useItemURL) { if (downloadItem.playlistIndex != null && useItemURL) {
@ -732,21 +735,22 @@ class YTDLPUtil(private val context: Context) {
} }
if (downloadItem.url.isYoutubeURL()) { if (downloadItem.url.isYoutubeURL()) {
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}") request.setYoutubeExtractorArgs()
} }
if (!sharedPreferences.getBoolean("disable_write_info_json", false)) { //TODO REVIEW TO ADD THIS AGAIN LATER?
val cachePath = "${FileUtil.getCachePath(context)}infojsons" // if (!sharedPreferences.getBoolean("disable_write_info_json", false)) {
val infoJsonName = MessageDigest.getInstance("MD5").digest(downloadItem.url.toByteArray()).toHexString() // val cachePath = "${FileUtil.getCachePath(context)}infojsons"
// val infoJsonName = MessageDigest.getInstance("MD5").digest(downloadItem.url.toByteArray()).toHexString()
val infoJsonFile = File(cachePath).walkTopDown().firstOrNull { it.name == "${infoJsonName}.info.json" } //
//ignore info file if its older than 5 hours. puny measure to prevent expired formats in some cases // val infoJsonFile = File(cachePath).walkTopDown().firstOrNull { it.name == "${infoJsonName}.info.json" }
if (infoJsonFile == null || System.currentTimeMillis() - infoJsonFile.lastModified() > (1000 * 60 * 60 * 5)) { // //ignore info file if its older than 5 hours. puny measure to prevent expired formats in some cases
request.addCommands(listOf("--print-to-file", "video:%()#j", "${cachePath}/${infoJsonName}.info.json")) // if (infoJsonFile == null || System.currentTimeMillis() - infoJsonFile.lastModified() > (1000 * 60 * 60 * 5)) {
}else { // request.addCommands(listOf("--print-to-file", "video:%()#j", "${cachePath}/${infoJsonName}.info.json"))
request.addOption("--load-info-json", infoJsonFile.absolutePath) // }else {
} // request.addOption("--load-info-json", infoJsonFile.absolutePath)
} // }
// }
} }
if (sharedPreferences.getString("prevent_duplicate_downloads", "")!! == "download_archive"){ if (sharedPreferences.getString("prevent_duplicate_downloads", "")!! == "download_archive"){
@ -856,7 +860,7 @@ class YTDLPUtil(private val context: Context) {
metadataCommands.addOption("--parse-metadata", "%(album_artist,first_artist|)s:%(album_artist)s") metadataCommands.addOption("--parse-metadata", "%(album_artist,first_artist|)s:%(album_artist)s")
metadataCommands.addOption("--parse-metadata", "description:(?:Released on: )(?P<dscrptn_year>\\d{4})") metadataCommands.addOption("--parse-metadata", "description:(?:.+?Released\\ on\\s*(?P<dscrptn_year>\\d{4}))?")
metadataCommands.addOption("--parse-metadata", "%(dscrptn_year,release_year,release_date>%Y,upload_date>%Y)s:(?P<meta_date>\\d+)") metadataCommands.addOption("--parse-metadata", "%(dscrptn_year,release_year,release_date>%Y,upload_date>%Y)s:(?P<meta_date>\\d+)")
if (isPlaylistItem) { if (isPlaylistItem) {
@ -1167,7 +1171,7 @@ class YTDLPUtil(private val context: Context) {
return YoutubeDL.getInstance().execute(req).out.trim() return YoutubeDL.getInstance().execute(req).out.trim()
} }
private fun getYoutubeExtractorArgs() : String { private fun YoutubeDLRequest.setYoutubeExtractorArgs() {
val playerClient = sharedPreferences.getString("yt_player_client", "")!!.split(",").filter { it.isNotBlank() }.toMutableList() val playerClient = sharedPreferences.getString("yt_player_client", "")!!.split(",").filter { it.isNotBlank() }.toMutableList()
val extractorArgs = mutableListOf<String>() val extractorArgs = mutableListOf<String>()
@ -1196,7 +1200,10 @@ class YTDLPUtil(private val context: Context) {
extractorArgs.add(otherArgs) extractorArgs.add(otherArgs)
} }
return extractorArgs.joinToString(";") val extArgs = extractorArgs.joinToString(";")
if (extractorArgs.isNotEmpty()) {
this.addOption("--extractor-args", "youtube:${extArgs}")
}
} }
private fun YoutubeDLRequest.addConfig(commandString: String) { private fun YoutubeDLRequest.addConfig(commandString: String) {

View file

@ -15,6 +15,7 @@ import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.deniscerri.ytdl.App import com.deniscerri.ytdl.App
import com.deniscerri.ytdl.R
import com.deniscerri.ytdl.database.DBManager import com.deniscerri.ytdl.database.DBManager
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
@ -22,6 +23,7 @@ import com.deniscerri.ytdl.database.repository.HistoryRepository
import com.deniscerri.ytdl.database.repository.ObserveSourcesRepository import com.deniscerri.ytdl.database.repository.ObserveSourcesRepository
import com.deniscerri.ytdl.database.repository.ResultRepository import com.deniscerri.ytdl.database.repository.ResultRepository
import com.deniscerri.ytdl.util.Extensions.calculateNextTimeForObserving import com.deniscerri.ytdl.util.Extensions.calculateNextTimeForObserving
import com.deniscerri.ytdl.util.Extensions.needsDataUpdating
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.extractors.YTDLPUtil import com.deniscerri.ytdl.util.extractors.YTDLPUtil
@ -113,8 +115,6 @@ class ObserveSourceWorker(
val string = Gson().toJson(item.downloadItemTemplate, DownloadItem::class.java) val string = Gson().toJson(item.downloadItemTemplate, DownloadItem::class.java)
val downloadItem = Gson().fromJson(string, DownloadItem::class.java) val downloadItem = Gson().fromJson(string, DownloadItem::class.java)
downloadItem.url = it.url downloadItem.url = it.url
downloadItem.title = it.title
downloadItem.author = it.author
downloadItem.thumb = it.thumb downloadItem.thumb = it.thumb
downloadItem.status = DownloadRepository.Status.Queued.toString() downloadItem.status = DownloadRepository.Status.Queued.toString()
downloadItem.playlistTitle = it.playlistTitle downloadItem.playlistTitle = it.playlistTitle
@ -137,9 +137,6 @@ class ObserveSourceWorker(
//if scheduler is on //if scheduler is on
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false) val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
if (useScheduler && !alarmScheduler.isDuringTheScheduledTime()){
alarmScheduler.schedule()
}
// if (items.any { it.playlistTitle.isEmpty() } && items.size > 1){ // if (items.any { it.playlistTitle.isEmpty() } && items.size > 1){
// items.forEachIndexed { index, it -> it.playlistTitle = "Various[${index+1}]" } // items.forEachIndexed { index, it -> it.playlistTitle = "Various[${index+1}]" }
@ -184,33 +181,17 @@ class ObserveSourceWorker(
} }
} }
if (!useScheduler || alarmScheduler.isDuringTheScheduledTime() || queuedItems.any { it.downloadStartTime > 0L } ){ if (useScheduler && !alarmScheduler.isDuringTheScheduledTime() && alarmScheduler.canSchedule()){
alarmScheduler.schedule()
}else {
downloadRepo.startDownloadWorker(queuedItems, context) downloadRepo.startDownloadWorker(queuedItems, context)
if(!useScheduler){
queuedItems.filter { it.downloadStartTime != 0L || (it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty()) }.forEach {
runCatching {
resultsRepo.updateDownloadItem(it)?.apply {
downloadRepo.updateWithoutUpsert(this)
}
}
}
}else{
queuedItems.filter { it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty() }.forEach {
runCatching {
resultsRepo.updateDownloadItem(it)?.apply {
downloadRepo.updateWithoutUpsert(this)
}
}
}
}
} }
item.alreadyProcessedLinks.removeAll(items.map { it.url }) item.alreadyProcessedLinks.removeAll(items.map { it.url })
item.alreadyProcessedLinks.addAll(items.map { it.url }) item.alreadyProcessedLinks.addAll(items.map { it.url })
} }
item.runCount = item.runCount + 1 item.runCount += 1
if (item.runCount > item.endsAfterCount && item.endsAfterCount > 0){ if (item.runCount > item.endsAfterCount && item.endsAfterCount > 0){
item.status = ObserveSourcesRepository.SourceStatus.STOPPED item.status = ObserveSourcesRepository.SourceStatus.STOPPED

View file

@ -0,0 +1,77 @@
package com.deniscerri.ytdl.work
import android.content.Context
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
import android.os.Build
import androidx.work.ForegroundInfo
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.deniscerri.ytdl.App
import com.deniscerri.ytdl.database.DBManager
import com.deniscerri.ytdl.database.repository.ResultRepository
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
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 {
val dbManager = DBManager.getInstance(context)
val dao = dbManager.downloadDao
val resDao = dbManager.resultDao
val resultRepo = ResultRepository(resDao, 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_SPECIAL_USE))
}else{
setForegroundAsync(ForegroundInfo(workID, notification))
}
var count = 0
return try{
ids.forEach {
if (!isStopped){
val d = dao.getDownloadById(it)
if (d.title.isNotBlank() && d.author.isNotBlank() && d.thumb.isNotBlank()) {
count++
return@forEach
}
runCatching {
runBlocking {
resultRepo.updateDownloadItem(d)?.apply {
val dd = dao.getNullableDownloadById(it)
if (dd != null) {
d.status = dd.status
dao.updateWithoutUpsert(this)
}
}
}
}
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()
}
}
}

View file

@ -10,7 +10,6 @@
android:paddingTop="10dp" android:paddingTop="10dp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:paddingBottom="5dp" android:paddingBottom="5dp"
android:textSize="15sp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/adjust_audio" /> android:text="@string/adjust_audio" />

View file

@ -9,7 +9,6 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:paddingBottom="5dp" android:paddingBottom="5dp"
android:textSize="15sp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/adjust_templates" /> android:text="@string/adjust_templates" />

View file

@ -12,8 +12,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingBottom="5dp" android:paddingBottom="5dp"
android:text="@string/adjust_video" android:text="@string/adjust_video"/>
android:textSize="15sp" />
<HorizontalScrollView <HorizontalScrollView
android:scrollbars="none" android:scrollbars="none"

View file

@ -19,7 +19,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" android:layout_marginHorizontal="20dp"
android:layout_marginTop="20dp" android:layout_marginTop="15dp"
android:orientation="horizontal"> android:orientation="horizontal">
<com.facebook.shimmer.ShimmerFrameLayout <com.facebook.shimmer.ShimmerFrameLayout
@ -104,6 +104,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:outlineProvider="none"
app:icon="@drawable/ic_clock" app:icon="@drawable/ic_clock"
android:contentDescription="@string/schedule" android:contentDescription="@string/schedule"
app:layout_constraintBottom_toBottomOf="@id/bottomsheet_download_button" app:layout_constraintBottom_toBottomOf="@id/bottomsheet_download_button"
@ -117,6 +118,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autoLink="all" android:autoLink="all"
android:text="@string/download" android:text="@string/download"
android:outlineProvider="none"
app:icon="@drawable/ic_down" app:icon="@drawable/ic_down"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -129,7 +131,8 @@
android:id="@+id/download_tablayout" android:id="@+id/download_tablayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="10dp" android:layout_marginHorizontal="10dp"
android:layout_marginTop="10dp"
android:backgroundTint="@android:color/transparent" android:backgroundTint="@android:color/transparent"
app:tabGravity="start" app:tabGravity="start"
app:tabMinWidth="0dp" app:tabMinWidth="0dp"

View file

@ -19,7 +19,7 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" android:paddingHorizontal="20dp"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingTop="20dp"> android:paddingTop="20dp">
@ -53,6 +53,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autoLink="all" android:autoLink="all"
android:text="@string/ok" android:text="@string/ok"
android:outlineProvider="none"
app:icon="@drawable/ic_check" app:icon="@drawable/ic_check"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View file

@ -102,7 +102,9 @@
style="@style/Widget.Material3.Chip.Assist.Elevated" style="@style/Widget.Material3.Chip.Assist.Elevated"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_margin="12dp" android:layout_marginHorizontal="5dp"
android:layout_marginVertical="8dp"
android:outlineProvider="none"
app:chipIcon="@drawable/baseline_loop_24" app:chipIcon="@drawable/baseline_loop_24"
app:chipIconVisible="true" app:chipIconVisible="true"
app:chipMinTouchTargetSize="0dp" app:chipMinTouchTargetSize="0dp"

View file

@ -21,6 +21,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
app:cardCornerRadius="20dp" app:cardCornerRadius="20dp"
app:cardElevation="5dp" app:cardElevation="5dp"
app:cardBackgroundColor="?attr/colorSurfaceContainer"
app:strokeColor="?attr/colorPrimary" app:strokeColor="?attr/colorPrimary"
app:checkedIconTint="?attr/colorPrimary" app:checkedIconTint="?attr/colorPrimary"
app:cardMaxElevation="12dp" app:cardMaxElevation="12dp"
@ -71,8 +72,8 @@
android:paddingEnd="10dp" android:paddingEnd="10dp"
android:scrollbars="none" android:scrollbars="none"
android:shadowColor="@color/black" android:shadowColor="@color/black"
android:shadowDx="2" android:shadowDx="1"
android:shadowDy="2" android:shadowDy="1"
android:shadowRadius="1" android:shadowRadius="1"
android:textColor="#FFF" android:textColor="#FFF"
android:textSize="17sp" android:textSize="17sp"
@ -89,8 +90,8 @@
android:paddingEnd="10dp" android:paddingEnd="10dp"
android:scrollbars="none" android:scrollbars="none"
android:shadowColor="@color/black" android:shadowColor="@color/black"
android:shadowDx="2" android:shadowDx="1"
android:shadowDy="2" android:shadowDy="1"
android:shadowRadius="1" android:shadowRadius="1"
android:textColor="#FFF" android:textColor="#FFF"
android:textSize="12sp" android:textSize="12sp"
@ -107,8 +108,8 @@
android:paddingEnd="10dp" android:paddingEnd="10dp"
android:paddingBottom="20dp" android:paddingBottom="20dp"
android:shadowColor="@color/black" android:shadowColor="@color/black"
android:shadowDx="2" android:shadowDx="1"
android:shadowDy="2" android:shadowDy="1"
android:shadowRadius="1" android:shadowRadius="1"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="12sp" android:textSize="12sp"
@ -124,6 +125,7 @@
app:elevation="0dp" app:elevation="0dp"
android:elevation="0dp" android:elevation="0dp"
app:borderWidth="0dp" app:borderWidth="0dp"
android:outlineProvider="none"
android:contentDescription="@string/audio" android:contentDescription="@string/audio"
android:layout_height="55dp" android:layout_height="55dp"
android:layout_marginVertical="10dp" android:layout_marginVertical="10dp"
@ -142,6 +144,7 @@
android:elevation="0dp" android:elevation="0dp"
android:contentDescription="@string/video" android:contentDescription="@string/video"
app:borderWidth="0dp" app:borderWidth="0dp"
android:outlineProvider="none"
app:icon="@drawable/ic_video" app:icon="@drawable/ic_video"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />

View file

@ -1462,6 +1462,7 @@
<item>@string/language</item> <item>@string/language</item>
<item>@string/codec</item> <item>@string/codec</item>
<item>@string/container</item> <item>@string/container</item>
<item>@string/file_size</item>
</string-array> </string-array>
<string-array name="format_importance_audio_values"> <string-array name="format_importance_audio_values">
@ -1469,6 +1470,7 @@
<item>language</item> <item>language</item>
<item>codec</item> <item>codec</item>
<item>container</item> <item>container</item>
<item>file_size</item>
</string-array> </string-array>
<string-array name="format_importance_video"> <string-array name="format_importance_video">
@ -1477,6 +1479,7 @@
<item>@string/codec</item> <item>@string/codec</item>
<item>@string/no_audio</item> <item>@string/no_audio</item>
<item>@string/container</item> <item>@string/container</item>
<item>@string/file_size</item>
</string-array> </string-array>
<string-array name="format_importance_video_values"> <string-array name="format_importance_video_values">
@ -1485,6 +1488,7 @@
<item>codec</item> <item>codec</item>
<item>no_audio</item> <item>no_audio</item>
<item>container</item> <item>container</item>
<item>file_size</item>
</string-array> </string-array>
<string-array name="label_visibility"> <string-array name="label_visibility">

View file

@ -33,7 +33,7 @@
android:title="@string/data_fetching_extra_command" /> android:title="@string/data_fetching_extra_command" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/downloading"> <PreferenceCategory android:title="@string/downloading" android:enabled="false">
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:icon="@drawable/if_file_rename" android:icon="@drawable/if_file_rename"
android:key="disable_write_info_json" android:key="disable_write_info_json"