slight fixes
This commit is contained in:
parent
3c3ef92cbd
commit
2e5fc3206e
29 changed files with 381 additions and 136 deletions
|
|
@ -18,7 +18,7 @@
|
|||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<!-- 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-->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
|
|
|||
|
|
@ -153,6 +153,9 @@ interface DownloadDao {
|
|||
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
|
||||
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)")
|
||||
fun getDownloadsByIds(ids: List<Long>) : List<DownloadItem>
|
||||
|
||||
|
|
|
|||
|
|
@ -106,8 +106,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun update(item: DownloadItem){
|
||||
downloadDao.update(item)
|
||||
suspend fun update(item: DownloadItem) : Long {
|
||||
return downloadDao.update(item)
|
||||
}
|
||||
|
||||
suspend fun updateAll(list: List<DownloadItem>) : List<DownloadItem> {
|
||||
|
|
|
|||
|
|
@ -106,25 +106,37 @@ class HistoryRepository(private val historyDao: HistoryDao) {
|
|||
|
||||
suspend fun deleteAllWithIDs(ids: List<Long>, deleteFile: Boolean = false){
|
||||
if (deleteFile){
|
||||
historyDao.getAllHistoryByIDs(ids).forEach { item ->
|
||||
item.downloadPath.forEach {
|
||||
FileUtil.deleteFile(it)
|
||||
ids.chunked(500).forEach { chunks ->
|
||||
historyDao.getAllHistoryByIDs(chunks).forEach { item ->
|
||||
item.downloadPath.forEach {
|
||||
FileUtil.deleteFile(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
historyDao.deleteAllByIDs(ids)
|
||||
ids.chunked(500).forEach { chunks ->
|
||||
historyDao.deleteAllByIDs(chunks)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
suspend fun deleteAllWithIDsCheckFiles(ids: List<Long>){
|
||||
val idsToDelete = mutableListOf<Long>()
|
||||
historyDao.getAllHistoryByIDs(ids).forEach { item ->
|
||||
val filesNotPresent = item.downloadPath.all { !File(it).exists() && it.isNotBlank()}
|
||||
if (filesNotPresent) {
|
||||
idsToDelete.add(item.id)
|
||||
ids.chunked(500).forEach { chunks ->
|
||||
historyDao.getAllHistoryByIDs(chunks).forEach { item ->
|
||||
val filesNotPresent = item.downloadPath.all { !File(it).exists() && it.isNotBlank()}
|
||||
if (filesNotPresent) {
|
||||
idsToDelete.add(item.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>> {
|
||||
val res = historyDao.getDownloadPathsFromIDs(ids)
|
||||
return res.map { it.downloadPath }
|
||||
val res : MutableList<List<String>> = mutableListOf()
|
||||
ids.chunked(500).forEach { chunks ->
|
||||
val tmp = historyDao.getDownloadPathsFromIDs(chunks)
|
||||
res.addAll(tmp.map { it.downloadPath })
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
suspend fun deleteDuplicates(){
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
|||
import com.deniscerri.ytdl.util.Extensions.isYoutubeChannelURL
|
||||
import com.deniscerri.ytdl.util.Extensions.isYoutubeURL
|
||||
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.newpipe.NewPipeUtil
|
||||
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
||||
|
|
@ -414,7 +415,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
|||
suspend fun updateDownloadItem(
|
||||
downloadItem: DownloadItem
|
||||
) : DownloadItem? {
|
||||
if (downloadItem.title.isEmpty() || downloadItem.author.isEmpty() || downloadItem.thumb.isEmpty()){
|
||||
if (downloadItem.needsDataUpdating()){
|
||||
runCatching {
|
||||
val info = getResultsFromSource(downloadItem.url, resetResults = false, addToResults = false, singleItem = true).first()
|
||||
if (downloadItem.title.isEmpty()) downloadItem.title = info.title
|
||||
|
|
|
|||
|
|
@ -39,12 +39,14 @@ import com.deniscerri.ytdl.database.repository.DownloadRepository
|
|||
import com.deniscerri.ytdl.database.repository.HistoryRepository
|
||||
import com.deniscerri.ytdl.database.repository.ResultRepository
|
||||
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.FileUtil
|
||||
import com.deniscerri.ytdl.util.FormatUtil
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
||||
import com.deniscerri.ytdl.work.AlarmScheduler
|
||||
import com.deniscerri.ytdl.work.UpdateMultipleDownloadsDataWorker
|
||||
import com.deniscerri.ytdl.work.UpdateMultipleDownloadsFormatsWorker
|
||||
import com.google.gson.Gson
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
|
|
@ -217,6 +219,14 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
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 {
|
||||
return repository.getItemByID(id)
|
||||
}
|
||||
|
|
@ -641,7 +651,10 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
//repository.insert(downloadItem)
|
||||
}
|
||||
}
|
||||
repository.insertAll(toInsert)
|
||||
toInsert.chunked(500).forEach { chunked ->
|
||||
repository.insertAll(chunked)
|
||||
}
|
||||
|
||||
processingItems.emit(false)
|
||||
} catch (e: Exception) {
|
||||
deleteProcessing()
|
||||
|
|
@ -678,7 +691,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
//repository.insert(downloadItem)
|
||||
}
|
||||
}
|
||||
repository.insertAll(toInsert)
|
||||
toInsert.chunked(500).forEach { chunked ->
|
||||
repository.insertAll(chunked)
|
||||
}
|
||||
processingItems.emit(false)
|
||||
}catch (e: Exception) {
|
||||
deleteProcessing()
|
||||
|
|
@ -1031,27 +1046,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
|
||||
result.message = repository.startDownloadWorker(queued, context).getOrElse { "" }
|
||||
|
||||
if(!useScheduler){
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val ids = queued.filter { it.needsDataUpdating() }.map { it.id }
|
||||
continueUpdatingDataInBackground(ids)
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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) {
|
||||
val processing = repository.getProcessingDownloads()
|
||||
processing.apply {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import android.view.*
|
|||
import android.view.View.*
|
||||
import android.widget.*
|
||||
import androidx.activity.addCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
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.ui.adapter.HomeAdapter
|
||||
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.isURL
|
||||
import com.deniscerri.ytdl.util.FileUtil
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.deniscerri.ytdl.util.ThemeUtil
|
||||
import com.deniscerri.ytdl.util.UiUtil
|
||||
|
|
@ -64,6 +67,8 @@ import kotlinx.coroutines.flow.collectLatest
|
|||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
|
||||
|
|
@ -263,25 +268,37 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
|||
if (res.errorMessage != null){
|
||||
val isSingleQueryAndURL = queryList.size == 1 && Patterns.WEB_URL.matcher(queryList.first()).matches()
|
||||
|
||||
kotlin.runCatching { UiUtil.handleNoResults(requireActivity(), res.errorMessage!!, continueAnyway = isSingleQueryAndURL, continued = {
|
||||
lifecycleScope.launch {
|
||||
if (sharedPreferences!!.getBoolean("download_card", true)) {
|
||||
withContext(Dispatchers.Main){
|
||||
showSingleDownloadSheet(
|
||||
resultItem = downloadViewModel.createEmptyResultItem(queryList.first()),
|
||||
type = DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!),
|
||||
disableUpdateData = true
|
||||
kotlin.runCatching {
|
||||
UiUtil.handleNoResults(requireActivity(), res.errorMessage!!,
|
||||
url = if (isSingleQueryAndURL) queryList.first() else null,
|
||||
continueAnyway = isSingleQueryAndURL,
|
||||
continued = {
|
||||
lifecycleScope.launch {
|
||||
if (sharedPreferences!!.getBoolean("download_card", 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) }
|
||||
}
|
||||
|
||||
|
|
@ -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() {
|
||||
super.onResume()
|
||||
if(arguments?.getString("url") == null){
|
||||
|
|
@ -653,6 +679,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
|||
resultViewModel.addSearchQueryToHistory(q)
|
||||
}
|
||||
}
|
||||
startSearch()
|
||||
}
|
||||
|
||||
private fun startSearch() {
|
||||
lifecycleScope.launch(Dispatchers.IO){
|
||||
resultViewModel.deleteAll()
|
||||
if(sharedPreferences!!.getBoolean("quick_download", false) || sharedPreferences!!.getString("preferred_download_type", "video") == "command"){
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ class HistoryPaginatedAdapter(onItemClickListener: OnItemClickListener, activity
|
|||
true
|
||||
}
|
||||
card.setOnClickListener {
|
||||
if (checkedItems.size > 0) {
|
||||
if (checkedItems.size > 0 || inverted) {
|
||||
checkCard(card, item.id, position)
|
||||
} else {
|
||||
onItemClickListener.onCardClick(item.id, finalFilePresent)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.deniscerri.ytdl.ui.downloadcard
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
|
|
@ -16,6 +18,7 @@ import android.widget.Button
|
|||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.os.bundleOf
|
||||
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.receiver.ShareActivity
|
||||
import com.deniscerri.ytdl.ui.BaseActivity
|
||||
import com.deniscerri.ytdl.ui.more.WebViewActivity
|
||||
import com.deniscerri.ytdl.util.UiUtil
|
||||
import com.facebook.shimmer.ShimmerFrameLayout
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
|
|
@ -58,6 +62,7 @@ import kotlinx.coroutines.flow.update
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.net.URL
|
||||
|
||||
|
||||
class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||
|
|
@ -397,9 +402,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
dd.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
dd.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
lifecycleScope.launch(Dispatchers.IO){
|
||||
val item = getDownloadItem()
|
||||
item.status = DownloadRepository.Status.Saved.toString()
|
||||
downloadViewModel.updateDownload(item)
|
||||
downloadViewModel.putToSaved(getDownloadItem())
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
|
@ -468,16 +471,25 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
lifecycleScope.launch {
|
||||
resultViewModel.uiState.collectLatest { res ->
|
||||
if (res.errorMessage != null){
|
||||
kotlin.runCatching { UiUtil.handleNoResults(requireActivity(), res.errorMessage!!, true, continued = {}, closed = {
|
||||
dismiss()
|
||||
}) }
|
||||
kotlin.runCatching {
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
return fragmentAdapter.getDownloadItem(selectedTabPosition)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ class FormatSelectionBottomSheetDialog(
|
|||
}
|
||||
}.onFailure { err ->
|
||||
withContext(Dispatchers.Main){
|
||||
UiUtil.handleNoResults(requireActivity(), err.message.toString(), false, continued = {}, closed = {})
|
||||
UiUtil.handleNoResults(requireActivity(), err.message.toString(), null, false, continued = {}, closed = {}, cookieFetch = {})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,8 @@ class WebViewActivity : BaseActivity() {
|
|||
}
|
||||
cookieManager.removeAllCookies(null)
|
||||
withContext(Dispatchers.Main) {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
this@WebViewActivity.setResult(RESULT_OK)
|
||||
this@WebViewActivity.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ class ProcessingSettingsFragment : BaseSettingsFragment() {
|
|||
|
||||
setOnPreferenceClickListener {
|
||||
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)])
|
||||
}.toMutableList()
|
||||
|
||||
|
|
@ -80,7 +81,8 @@ class ProcessingSettingsFragment : BaseSettingsFragment() {
|
|||
|
||||
setOnPreferenceClickListener {
|
||||
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)])
|
||||
}.toMutableList()
|
||||
|
||||
|
|
|
|||
|
|
@ -558,4 +558,9 @@ object Extensions {
|
|||
t4.second
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
fun DownloadItem.needsDataUpdating() : Boolean {
|
||||
return this.title.isBlank() || this.author.isBlank() || this.thumb.isBlank()
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ class FormatUtil(private var context: Context) {
|
|||
val orderPreferences = sharedPreferences.getString("format_importance_audio", itemValues.joinToString(","))!!.split(",").toMutableSet()
|
||||
if (preferSmallerFormats) {
|
||||
orderPreferences.add("smallsize")
|
||||
orderPreferences.remove("file_size")
|
||||
}
|
||||
|
||||
return orderPreferences
|
||||
|
|
@ -46,6 +47,7 @@ class FormatUtil(private var context: Context) {
|
|||
val orderPreferences = sharedPreferences.getString("format_importance_video", itemValues.joinToString(","))!!.split(",").toMutableSet()
|
||||
if (preferSmallerFormats) {
|
||||
orderPreferences.add("smallsize")
|
||||
orderPreferences.remove("file_size")
|
||||
}
|
||||
|
||||
return orderPreferences
|
||||
|
|
@ -63,7 +65,10 @@ class FormatUtil(private var context: Context) {
|
|||
"smallsize" -> {
|
||||
(a.filesize).compareTo(b.filesize)
|
||||
}
|
||||
|
||||
"file_size" -> {
|
||||
val result = b.filesize.compareTo(a.filesize)
|
||||
result
|
||||
}
|
||||
"id" -> {
|
||||
(audioFormatIDPreference.contains(b.format_id)).compareTo(
|
||||
audioFormatIDPreference.contains(a.format_id)
|
||||
|
|
@ -119,6 +124,10 @@ class FormatUtil(private var context: Context) {
|
|||
val result = a.filesize.compareTo(b.filesize)
|
||||
result
|
||||
}
|
||||
"file_size" -> {
|
||||
val result = b.filesize.compareTo(a.filesize)
|
||||
result
|
||||
}
|
||||
"id" -> {
|
||||
videoFormatIDPreference.contains(b.format_id).compareTo(videoFormatIDPreference.contains(a.format_id))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
fun showFormatsUpdatedNotification(downloadIds: List<Long>) {
|
||||
val notificationBuilder = getBuilder(DOWNLOAD_FINISHED_CHANNEL_ID)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.animation.ObjectAnimator
|
|||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
|
|
@ -33,6 +34,7 @@ import android.widget.PopupMenu
|
|||
import android.widget.RadioButton
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.DimenRes
|
||||
import androidx.annotation.OptIn
|
||||
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.DownloadViewModel
|
||||
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.getMediaDuration
|
||||
import com.deniscerri.ytdl.util.Extensions.toStringDuration
|
||||
|
|
@ -94,6 +97,7 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
|
@ -1633,9 +1637,7 @@ object UiUtil {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fun handleNoResults(context: Activity, message: String, continueAnyway: Boolean = false, continued: () -> Unit, closed: () -> Unit) {
|
||||
|
||||
fun handleNoResults(context: Activity, message: String, url: String? = null, continueAnyway: Boolean = false, continued: () -> Unit, closed: () -> Unit, cookieFetch: () -> Unit) {
|
||||
val errDialog = MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.no_results)
|
||||
.setMessage(message)
|
||||
|
|
@ -1646,12 +1648,19 @@ object UiUtil {
|
|||
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 ->
|
||||
continued()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
errDialog.setOnCancelListener {
|
||||
closed()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ class UpdateUtil(var context: Context) {
|
|||
|
||||
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())
|
||||
request.addOption("--update-to", "${channel}@latest")
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ class YTDLPUtil(private val context: Context) {
|
|||
}
|
||||
}
|
||||
if (searchEngine == "ytsearch" || query.isYoutubeURL()) {
|
||||
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}")
|
||||
request.setYoutubeExtractorArgs()
|
||||
}
|
||||
|
||||
request.addOption("--flat-playlist")
|
||||
|
|
@ -250,7 +250,7 @@ class YTDLPUtil(private val context: Context) {
|
|||
|
||||
fun getYoutubeWatchLater() : ArrayList<ResultItem> {
|
||||
val request = YoutubeDLRequest(listOf())
|
||||
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}")
|
||||
request.setYoutubeExtractorArgs()
|
||||
request.addOption( "-j")
|
||||
request.addOption("--flat-playlist")
|
||||
request.applyDefaultOptionsForFetchingData()
|
||||
|
|
@ -269,7 +269,7 @@ class YTDLPUtil(private val context: Context) {
|
|||
|
||||
fun getYoutubeRecommendations() : ArrayList<ResultItem> {
|
||||
val request = YoutubeDLRequest(listOf())
|
||||
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}")
|
||||
request.setYoutubeExtractorArgs()
|
||||
request.addOption( "-j")
|
||||
request.addOption("--flat-playlist")
|
||||
request.applyDefaultOptionsForFetchingData()
|
||||
|
|
@ -288,7 +288,7 @@ class YTDLPUtil(private val context: Context) {
|
|||
|
||||
fun getYoutubeLikedVideos() : ArrayList<ResultItem> {
|
||||
val request = YoutubeDLRequest(listOf())
|
||||
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}")
|
||||
request.setYoutubeExtractorArgs()
|
||||
request.addOption( "-j")
|
||||
request.addOption("--flat-playlist")
|
||||
request.applyDefaultOptionsForFetchingData()
|
||||
|
|
@ -307,7 +307,7 @@ class YTDLPUtil(private val context: Context) {
|
|||
|
||||
fun getYoutubeWatchHistory() : ArrayList<ResultItem> {
|
||||
val request = YoutubeDLRequest(listOf())
|
||||
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}")
|
||||
request.setYoutubeExtractorArgs()
|
||||
request.addOption( "-j")
|
||||
request.addOption("--flat-playlist")
|
||||
request.applyDefaultOptionsForFetchingData()
|
||||
|
|
@ -574,9 +574,12 @@ class YTDLPUtil(private val context: Context) {
|
|||
}else{
|
||||
addOption("-I", "${downloadItem.playlistIndex!!}:${downloadItem.playlistIndex}")
|
||||
}
|
||||
addOption("-i")
|
||||
}
|
||||
}
|
||||
|
||||
request.addOption("--newline")
|
||||
|
||||
val metadataCommands = mutableListOf<String>()
|
||||
|
||||
if (downloadItem.playlistIndex != null && useItemURL) {
|
||||
|
|
@ -732,21 +735,22 @@ class YTDLPUtil(private val context: Context) {
|
|||
}
|
||||
|
||||
if (downloadItem.url.isYoutubeURL()) {
|
||||
request.addOption("--extractor-args", "youtube:${getYoutubeExtractorArgs()}")
|
||||
request.setYoutubeExtractorArgs()
|
||||
}
|
||||
|
||||
if (!sharedPreferences.getBoolean("disable_write_info_json", false)) {
|
||||
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
|
||||
if (infoJsonFile == null || System.currentTimeMillis() - infoJsonFile.lastModified() > (1000 * 60 * 60 * 5)) {
|
||||
request.addCommands(listOf("--print-to-file", "video:%()#j", "${cachePath}/${infoJsonName}.info.json"))
|
||||
}else {
|
||||
request.addOption("--load-info-json", infoJsonFile.absolutePath)
|
||||
}
|
||||
}
|
||||
//TODO REVIEW TO ADD THIS AGAIN LATER?
|
||||
// if (!sharedPreferences.getBoolean("disable_write_info_json", false)) {
|
||||
// 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
|
||||
// if (infoJsonFile == null || System.currentTimeMillis() - infoJsonFile.lastModified() > (1000 * 60 * 60 * 5)) {
|
||||
// request.addCommands(listOf("--print-to-file", "video:%()#j", "${cachePath}/${infoJsonName}.info.json"))
|
||||
// }else {
|
||||
// request.addOption("--load-info-json", infoJsonFile.absolutePath)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
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", "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+)")
|
||||
|
||||
if (isPlaylistItem) {
|
||||
|
|
@ -1167,7 +1171,7 @@ class YTDLPUtil(private val context: Context) {
|
|||
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 extractorArgs = mutableListOf<String>()
|
||||
|
||||
|
|
@ -1196,7 +1200,10 @@ class YTDLPUtil(private val context: Context) {
|
|||
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) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.work.OneTimeWorkRequestBuilder
|
|||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import com.deniscerri.ytdl.App
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.deniscerri.ytdl.database.DBManager
|
||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
||||
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.ResultRepository
|
||||
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.NotificationUtil
|
||||
import com.deniscerri.ytdl.util.extractors.YTDLPUtil
|
||||
|
|
@ -113,8 +115,6 @@ class ObserveSourceWorker(
|
|||
val string = Gson().toJson(item.downloadItemTemplate, DownloadItem::class.java)
|
||||
val downloadItem = Gson().fromJson(string, DownloadItem::class.java)
|
||||
downloadItem.url = it.url
|
||||
downloadItem.title = it.title
|
||||
downloadItem.author = it.author
|
||||
downloadItem.thumb = it.thumb
|
||||
downloadItem.status = DownloadRepository.Status.Queued.toString()
|
||||
downloadItem.playlistTitle = it.playlistTitle
|
||||
|
|
@ -137,9 +137,6 @@ class ObserveSourceWorker(
|
|||
|
||||
//if scheduler is on
|
||||
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
|
||||
if (useScheduler && !alarmScheduler.isDuringTheScheduledTime()){
|
||||
alarmScheduler.schedule()
|
||||
}
|
||||
|
||||
// if (items.any { it.playlistTitle.isEmpty() } && items.size > 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)
|
||||
|
||||
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.addAll(items.map { it.url })
|
||||
}
|
||||
|
||||
item.runCount = item.runCount + 1
|
||||
item.runCount += 1
|
||||
|
||||
if (item.runCount > item.endsAfterCount && item.endsAfterCount > 0){
|
||||
item.status = ObserveSourcesRepository.SourceStatus.STOPPED
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@
|
|||
android:paddingTop="10dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingBottom="5dp"
|
||||
android:textSize="15sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/adjust_audio" />
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:paddingBottom="5dp"
|
||||
android:textSize="15sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/adjust_templates" />
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="5dp"
|
||||
android:text="@string/adjust_video"
|
||||
android:textSize="15sp" />
|
||||
android:text="@string/adjust_video"/>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:scrollbars="none"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
|
|
@ -104,6 +104,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:outlineProvider="none"
|
||||
app:icon="@drawable/ic_clock"
|
||||
android:contentDescription="@string/schedule"
|
||||
app:layout_constraintBottom_toBottomOf="@id/bottomsheet_download_button"
|
||||
|
|
@ -117,6 +118,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:autoLink="all"
|
||||
android:text="@string/download"
|
||||
android:outlineProvider="none"
|
||||
app:icon="@drawable/ic_down"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
@ -129,7 +131,8 @@
|
|||
android:id="@+id/download_tablayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:backgroundTint="@android:color/transparent"
|
||||
app:tabGravity="start"
|
||||
app:tabMinWidth="0dp"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="20dp">
|
||||
|
||||
|
|
@ -53,6 +53,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:autoLink="all"
|
||||
android:text="@string/ok"
|
||||
android:outlineProvider="none"
|
||||
app:icon="@drawable/ic_check"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
|
|||
|
|
@ -102,7 +102,9 @@
|
|||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
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:chipIconVisible="true"
|
||||
app:chipMinTouchTargetSize="0dp"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
android:layout_height="match_parent"
|
||||
app:cardCornerRadius="20dp"
|
||||
app:cardElevation="5dp"
|
||||
app:cardBackgroundColor="?attr/colorSurfaceContainer"
|
||||
app:strokeColor="?attr/colorPrimary"
|
||||
app:checkedIconTint="?attr/colorPrimary"
|
||||
app:cardMaxElevation="12dp"
|
||||
|
|
@ -71,8 +72,8 @@
|
|||
android:paddingEnd="10dp"
|
||||
android:scrollbars="none"
|
||||
android:shadowColor="@color/black"
|
||||
android:shadowDx="2"
|
||||
android:shadowDy="2"
|
||||
android:shadowDx="1"
|
||||
android:shadowDy="1"
|
||||
android:shadowRadius="1"
|
||||
android:textColor="#FFF"
|
||||
android:textSize="17sp"
|
||||
|
|
@ -89,8 +90,8 @@
|
|||
android:paddingEnd="10dp"
|
||||
android:scrollbars="none"
|
||||
android:shadowColor="@color/black"
|
||||
android:shadowDx="2"
|
||||
android:shadowDy="2"
|
||||
android:shadowDx="1"
|
||||
android:shadowDy="1"
|
||||
android:shadowRadius="1"
|
||||
android:textColor="#FFF"
|
||||
android:textSize="12sp"
|
||||
|
|
@ -107,8 +108,8 @@
|
|||
android:paddingEnd="10dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:shadowColor="@color/black"
|
||||
android:shadowDx="2"
|
||||
android:shadowDy="2"
|
||||
android:shadowDx="1"
|
||||
android:shadowDy="1"
|
||||
android:shadowRadius="1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="12sp"
|
||||
|
|
@ -124,6 +125,7 @@
|
|||
app:elevation="0dp"
|
||||
android:elevation="0dp"
|
||||
app:borderWidth="0dp"
|
||||
android:outlineProvider="none"
|
||||
android:contentDescription="@string/audio"
|
||||
android:layout_height="55dp"
|
||||
android:layout_marginVertical="10dp"
|
||||
|
|
@ -142,6 +144,7 @@
|
|||
android:elevation="0dp"
|
||||
android:contentDescription="@string/video"
|
||||
app:borderWidth="0dp"
|
||||
android:outlineProvider="none"
|
||||
app:icon="@drawable/ic_video"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
|
|
|||
|
|
@ -1462,6 +1462,7 @@
|
|||
<item>@string/language</item>
|
||||
<item>@string/codec</item>
|
||||
<item>@string/container</item>
|
||||
<item>@string/file_size</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="format_importance_audio_values">
|
||||
|
|
@ -1469,6 +1470,7 @@
|
|||
<item>language</item>
|
||||
<item>codec</item>
|
||||
<item>container</item>
|
||||
<item>file_size</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="format_importance_video">
|
||||
|
|
@ -1477,6 +1479,7 @@
|
|||
<item>@string/codec</item>
|
||||
<item>@string/no_audio</item>
|
||||
<item>@string/container</item>
|
||||
<item>@string/file_size</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="format_importance_video_values">
|
||||
|
|
@ -1485,6 +1488,7 @@
|
|||
<item>codec</item>
|
||||
<item>no_audio</item>
|
||||
<item>container</item>
|
||||
<item>file_size</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="label_visibility">
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
android:title="@string/data_fetching_extra_command" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/downloading">
|
||||
<PreferenceCategory android:title="@string/downloading" android:enabled="false">
|
||||
<SwitchPreferenceCompat
|
||||
android:icon="@drawable/if_file_rename"
|
||||
android:key="disable_write_info_json"
|
||||
|
|
|
|||
Loading…
Reference in a new issue