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.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" />
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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> {
|
||||||
|
|
|
||||||
|
|
@ -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(){
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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"){
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 = {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -558,4 +558,9 @@ object Extensions {
|
||||||
t4.second
|
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()
|
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))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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: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" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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" />
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue