Fixed quotes breaking command, removed format sorting by default, moved to advanced settings

This commit is contained in:
deniscerri 2025-02-22 17:44:20 +01:00
parent 1a4c515262
commit 0bb9d9d8fb
No known key found for this signature in database
GPG key ID: 95C43D517D830350
14 changed files with 326 additions and 203 deletions

View file

@ -532,12 +532,19 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
fun getFormat(formats: List<Format>, type: Type, url: String? = null) : Format { fun getFormat(formats: List<Format>, type: Type, url: String? = null) : Format {
val sortFormats = sharedPreferences.getBoolean("use_format_sorting", false)
when(type) { when(type) {
Type.audio -> { Type.audio -> {
return cloneFormat ( return cloneFormat (
try { try {
val theFormats = formats.filter { it.vcodec.isBlank() || it.vcodec == "none" } val theFormats = formats.filter { it.vcodec.isBlank() || it.vcodec == "none" }
FormatUtil(application).sortAudioFormats(theFormats).first()
if (sortFormats) {
FormatUtil(application).sortAudioFormats(theFormats).first()
}else {
theFormats.first()
}
}catch (e: Exception){ }catch (e: Exception){
formatUtil.getGenericAudioFormats(resources).first() formatUtil.getGenericAudioFormats(resources).first()
} }
@ -551,9 +558,11 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
formatUtil.getGenericVideoFormats(resources).sortedByDescending { it.filesize } formatUtil.getGenericVideoFormats(resources).sortedByDescending { it.filesize }
} }
val sorted = FormatUtil(application).sortVideoFormats(theFormats) if (sortFormats) {
println(sorted.map { it.vcodec }) FormatUtil(application).sortVideoFormats(theFormats).first()
sorted.first() }else {
theFormats.first()
}
}catch (e: Exception){ }catch (e: Exception){
formatUtil.getGenericVideoFormats(resources).first() formatUtil.getGenericVideoFormats(resources).first()
} }

View file

@ -5,6 +5,7 @@ import android.app.Activity
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.text.TextUtils
import android.util.TypedValue import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
@ -23,6 +24,8 @@ import com.deniscerri.ytdl.database.models.CommandTemplate
import com.deniscerri.ytdl.database.models.YoutubePlayerClientItem import com.deniscerri.ytdl.database.models.YoutubePlayerClientItem
import com.deniscerri.ytdl.util.Extensions.popup import com.deniscerri.ytdl.util.Extensions.popup
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.materialswitch.MaterialSwitch import com.google.android.material.materialswitch.MaterialSwitch
class YoutubePlayerClientAdapter(onItemClickListener: OnItemClickListener, activity: Activity, private var itemTouchHelper: ItemTouchHelper) : ListAdapter<YoutubePlayerClientItem?, YoutubePlayerClientAdapter.ViewHolder>(AsyncDifferConfig.Builder( class YoutubePlayerClientAdapter(onItemClickListener: OnItemClickListener, activity: Activity, private var itemTouchHelper: ItemTouchHelper) : ListAdapter<YoutubePlayerClientItem?, YoutubePlayerClientAdapter.ViewHolder>(AsyncDifferConfig.Builder(
@ -61,21 +64,38 @@ class YoutubePlayerClientAdapter(onItemClickListener: OnItemClickListener, activ
val title = card.findViewById<TextView>(R.id.title) val title = card.findViewById<TextView>(R.id.title)
title.text = item.playerClient title.text = item.playerClient
val content = card.findViewById<TextView>(R.id.content) val content = card.findViewById<ChipGroup>(R.id.content)
content.isVisible = item.poTokens.isNotEmpty() val chips = mutableListOf<TextView>()
if (item.poTokens.isNotEmpty()) { item.poTokens.forEach {
val text = item.poTokens.joinToString("\n") { "PO Token (${it.context}): ${it.token}" } val tmp = activity.layoutInflater.inflate(R.layout.textview_chip, content, false) as TextView
content.text = text tmp.maxWidth = 500
tmp.maxLines = 1
tmp.ellipsize = TextUtils.TruncateAt.END
val text = "PO Token (${it.context}): ${it.token}"
tmp.text = text
chips.add(tmp)
}
for (chip in chips.reversed()) {
content.addView(chip, 0)
} }
if (item.urlRegex.isNotEmpty()) { if (item.urlRegex.isNotEmpty()) {
val text = content.text.toString() + "\nURL Regex: " + item.urlRegex.joinToString(", ") val text = "URL Regex: " + item.urlRegex.joinToString(", ")
content.text = text content.findViewById<TextView>(R.id.urlRegex).apply {
isVisible = true
setText(text)
}
} }
title.alpha = if (item.enabled) 1f else 0.3f
content.alpha = if (item.enabled) 1f else 0.3f
val switch = card.findViewById<MaterialSwitch>(R.id.materialSwitch) val switch = card.findViewById<MaterialSwitch>(R.id.materialSwitch)
switch.isChecked = item.enabled switch.isChecked = item.enabled
switch.setOnClickListener { switch.setOnClickListener {
title.alpha = if (switch.isChecked) 1f else 0.3f
content.alpha = if (switch.isChecked) 1f else 0.3f
onItemClickListener.onStatusToggle(item, switch.isChecked, position) onItemClickListener.onStatusToggle(item, switch.isChecked, position)
} }

View file

@ -30,8 +30,7 @@ class ProcessingSettingsFragment : BaseSettingsFragment() {
val preferredFormatID : EditTextPreference? = findPreference("format_id") val preferredFormatID : EditTextPreference? = findPreference("format_id")
val preferredFormatIDAudio : EditTextPreference? = findPreference("format_id_audio") val preferredFormatIDAudio : EditTextPreference? = findPreference("format_id_audio")
val subtitleLanguages : Preference? = findPreference("subs_lang") val subtitleLanguages : Preference? = findPreference("subs_lang")
val formatImportanceAudio: Preference? = findPreference("format_importance_audio")
val formatImportanceVideo: Preference? = findPreference("format_importance_video")
preferredFormatID?.title = "${getString(R.string.preferred_format_id)} [${getString(R.string.video)}]" preferredFormatID?.title = "${getString(R.string.preferred_format_id)} [${getString(R.string.video)}]"
preferredFormatID?.dialogTitle = "${getString(R.string.file_name_template)} [${getString(R.string.video)}]" preferredFormatID?.dialogTitle = "${getString(R.string.file_name_template)} [${getString(R.string.video)}]"
@ -50,50 +49,6 @@ class ProcessingSettingsFragment : BaseSettingsFragment() {
} }
formatImportanceAudio?.apply {
title = "${getString(R.string.format_importance)} [${getString(R.string.audio)}]"
val items = requireContext().getStringArray(R.array.format_importance_audio)
val itemValues = requireContext().getStringArray(R.array.format_importance_audio_values).toSet()
val prefVideo = prefs.getString("format_importance_audio", itemValues.joinToString(","))!!
summary = prefVideo.split(",").mapIndexed { index, s -> "${index + 1}. ${items[itemValues.indexOf(s)]}" }.joinToString("\n")
setOnPreferenceClickListener {
val pref = prefs.getString("format_importance_audio", itemValues.joinToString(","))!!
val prefArr = pref.split(",")
val itms = itemValues.sortedBy { prefArr.indexOf(it) }.map {
Pair(it, items[itemValues.indexOf(it)])
}.toMutableList()
showFormatImportanceDialog(title.toString(), itms) { new ->
editor.putString("format_importance_audio", new.joinToString(",") { it.first }).apply()
formatImportanceAudio.summary = new.map { it.second }.mapIndexed { index, s -> "${index + 1}. $s" }.joinToString("\n")
}
true
}
}
formatImportanceVideo?.apply {
title = "${getString(R.string.format_importance)} [${getString(R.string.video)}]"
val items = requireContext().getStringArray(R.array.format_importance_video)
val itemValues = requireContext().getStringArray(R.array.format_importance_video_values).toSet()
val prefVideo = prefs.getString("format_importance_video", itemValues.joinToString(","))!!
summary = prefVideo.split(",").mapIndexed { index, s -> "${index + 1}. ${items[itemValues.indexOf(s)]}" }.joinToString("\n")
setOnPreferenceClickListener {
val pref = prefs.getString("format_importance_video", itemValues.joinToString(","))!!
val prefArr = pref.split(",")
val itms = itemValues.sortedBy { prefArr.indexOf(it) }.map {
Pair(it, items[itemValues.indexOf(it)])
}.toMutableList()
showFormatImportanceDialog(title.toString(), itms) {new ->
editor.putString("format_importance_video", new.joinToString(",") { it.first }).apply()
formatImportanceVideo.summary = new.map { it.second }.mapIndexed { index, s -> "${index + 1}. $s" }.joinToString("\n")
}
true
}
}
findPreference<EditTextPreference>("format_id")?.apply { findPreference<EditTextPreference>("format_id")?.apply {
val s = getString(R.string.preferred_format_id_summary) val s = getString(R.string.preferred_format_id_summary)
summary = if (text.isNullOrBlank()) { summary = if (text.isNullOrBlank()) {
@ -128,77 +83,4 @@ class ProcessingSettingsFragment : BaseSettingsFragment() {
} }
} }
} }
private fun showFormatImportanceDialog(t: String, items: MutableList<Pair<String, String>>, onChange: (items: List<Pair<String, String>>) -> Unit){
val builder = MaterialAlertDialogBuilder(requireContext())
builder.setTitle(t)
val adapter = SortableTextItemAdapter(items)
val itemTouchCallback = object : ItemTouchHelper.Callback() {
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
return makeMovementFlags(dragFlags, 0)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val itemToMove = adapter.items[viewHolder.absoluteAdapterPosition]
adapter.items.remove(itemToMove)
adapter.items.add(target.absoluteAdapterPosition, itemToMove)
adapter.notifyItemMoved(
viewHolder.absoluteAdapterPosition,
target.absoluteAdapterPosition
)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
// do nothing
}
}
val linear = LinearLayout(requireActivity())
linear.orientation = LinearLayout.VERTICAL
val note = TextView(requireActivity())
note.text = getString(R.string.format_importance_note)
note.textSize = 16f
note.setTypeface(note.typeface, Typeface.BOLD)
note.setPadding(20,20,20,20)
linear.addView(note)
val recycler = RecyclerView(requireContext())
recycler.layoutManager = LinearLayoutManager(requireContext())
recycler.adapter = adapter
linear.addView(recycler)
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(recycler)
builder.setView(linear)
builder.setPositiveButton(
getString(android.R.string.ok)
) { _: DialogInterface?, _: Int ->
onChange(adapter.items)
}
// handle the negative button of the alert dialog
builder.setNegativeButton(
getString(R.string.cancel)
) { _: DialogInterface?, _: Int -> }
val dialog = builder.create()
dialog.show()
}
} }

View file

@ -1,13 +1,23 @@
package com.deniscerri.ytdl.ui.more.settings.advanced package com.deniscerri.ytdl.ui.more.settings.advanced
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.DialogInterface
import android.graphics.Typeface
import android.os.Bundle import android.os.Bundle
import android.widget.LinearLayout
import android.widget.TextView
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdl.R import com.deniscerri.ytdl.R
import com.deniscerri.ytdl.ui.adapter.SortableTextItemAdapter
import com.deniscerri.ytdl.ui.more.settings.BaseSettingsFragment import com.deniscerri.ytdl.ui.more.settings.BaseSettingsFragment
import com.deniscerri.ytdl.util.UiUtil import com.deniscerri.ytdl.util.UiUtil
import com.google.android.material.dialog.MaterialAlertDialogBuilder
class AdvancedSettingsFragment : BaseSettingsFragment() { class AdvancedSettingsFragment : BaseSettingsFragment() {
@ -15,9 +25,132 @@ class AdvancedSettingsFragment : BaseSettingsFragment() {
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.advanced_preferences, rootKey) setPreferencesFromResource(R.xml.advanced_preferences, rootKey)
val prefs = PreferenceManager.getDefaultSharedPreferences(requireActivity())
val editor = prefs.edit()
findPreference<Preference>("yt_player_client")?.setOnPreferenceClickListener { findPreference<Preference>("yt_player_client")?.setOnPreferenceClickListener {
findNavController().navigate(R.id.youtubePlayerClientFragment) findNavController().navigate(R.id.youtubePlayerClientFragment)
false false
} }
val formatImportanceAudio: Preference? = findPreference("format_importance_audio")
val formatImportanceVideo: Preference? = findPreference("format_importance_video")
formatImportanceAudio?.apply {
title = "${getString(R.string.format_importance)} [${getString(R.string.audio)}]"
val items = requireContext().getStringArray(R.array.format_importance_audio)
val itemValues = requireContext().getStringArray(R.array.format_importance_audio_values).toSet()
val prefVideo = prefs.getString("format_importance_audio", itemValues.joinToString(","))!!
summary = prefVideo.split(",").mapIndexed { index, s -> "${index + 1}. ${items[itemValues.indexOf(s)]}" }.joinToString("\n")
setOnPreferenceClickListener {
val pref = prefs.getString("format_importance_audio", itemValues.joinToString(","))!!
val prefArr = pref.split(",")
val itms = itemValues.sortedBy { prefArr.indexOf(it) }.map {
Pair(it, items[itemValues.indexOf(it)])
}.toMutableList()
showFormatImportanceDialog(title.toString(), itms) { new ->
editor.putString("format_importance_audio", new.joinToString(",") { it.first }).apply()
formatImportanceAudio.summary = new.map { it.second }.mapIndexed { index, s -> "${index + 1}. $s" }.joinToString("\n")
}
true
}
}
formatImportanceVideo?.apply {
title = "${getString(R.string.format_importance)} [${getString(R.string.video)}]"
val items = requireContext().getStringArray(R.array.format_importance_video)
val itemValues = requireContext().getStringArray(R.array.format_importance_video_values).toSet()
val prefVideo = prefs.getString("format_importance_video", itemValues.joinToString(","))!!
summary = prefVideo.split(",").mapIndexed { index, s -> "${index + 1}. ${items[itemValues.indexOf(s)]}" }.joinToString("\n")
setOnPreferenceClickListener {
val pref = prefs.getString("format_importance_video", itemValues.joinToString(","))!!
val prefArr = pref.split(",")
val itms = itemValues.sortedBy { prefArr.indexOf(it) }.map {
Pair(it, items[itemValues.indexOf(it)])
}.toMutableList()
showFormatImportanceDialog(title.toString(), itms) {new ->
editor.putString("format_importance_video", new.joinToString(",") { it.first }).apply()
formatImportanceVideo.summary = new.map { it.second }.mapIndexed { index, s -> "${index + 1}. $s" }.joinToString("\n")
}
true
}
}
}
private fun showFormatImportanceDialog(t: String, items: MutableList<Pair<String, String>>, onChange: (items: List<Pair<String, String>>) -> Unit){
val builder = MaterialAlertDialogBuilder(requireContext())
builder.setTitle(t)
val adapter = SortableTextItemAdapter(items)
val itemTouchCallback = object : ItemTouchHelper.Callback() {
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
return makeMovementFlags(dragFlags, 0)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val itemToMove = adapter.items[viewHolder.absoluteAdapterPosition]
adapter.items.remove(itemToMove)
adapter.items.add(target.absoluteAdapterPosition, itemToMove)
adapter.notifyItemMoved(
viewHolder.absoluteAdapterPosition,
target.absoluteAdapterPosition
)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
// do nothing
}
}
val linear = LinearLayout(requireActivity())
linear.orientation = LinearLayout.VERTICAL
val note = TextView(requireActivity())
note.text = getString(R.string.format_importance_note)
note.textSize = 16f
note.setTypeface(note.typeface, Typeface.BOLD)
note.setPadding(20,20,20,20)
linear.addView(note)
val recycler = RecyclerView(requireContext())
recycler.layoutManager = LinearLayoutManager(requireContext())
recycler.adapter = adapter
linear.addView(recycler)
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(recycler)
builder.setView(linear)
builder.setPositiveButton(
getString(android.R.string.ok)
) { _: DialogInterface?, _: Int ->
onChange(adapter.items)
}
// handle the negative button of the alert dialog
builder.setNegativeButton(
getString(R.string.cancel)
) { _: DialogInterface?, _: Int -> }
val dialog = builder.create()
dialog.show()
} }
} }

View file

@ -364,8 +364,8 @@ class TerminalFragment : Fragment() {
.observe(viewLifecycleOwner, workerObserver) .observe(viewLifecycleOwner, workerObserver)
} }
private val workerObserver = object: Observer<MutableList<WorkInfo>> { private val workerObserver = object: Observer<List<WorkInfo>> {
override fun onChanged(value: MutableList<WorkInfo>) { override fun onChanged(value: List<WorkInfo>) {
value.forEach { work -> value.forEach { work ->
if (listOf(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED).contains(work.state)) { if (listOf(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED).contains(work.state)) {
requireActivity().runOnUiThread { requireActivity().runOnUiThread {

View file

@ -1500,6 +1500,7 @@ object UiUtil {
val cut = view.findViewById<Chip>(R.id.cut) val cut = view.findViewById<Chip>(R.id.cut)
if (items.size > 1 || items.first().url.isEmpty()) cut.isVisible = false if (items.size > 1 || items.first().url.isEmpty()) cut.isVisible = false
else{ else{
cut.setOnClickListener(null)
val invalidDuration = items[0].duration == "-1" val invalidDuration = items[0].duration == "-1"
if(items[0].duration.isNotEmpty() && !invalidDuration){ if(items[0].duration.isNotEmpty() && !invalidDuration){
val downloadItem = items[0] val downloadItem = items[0]
@ -1664,6 +1665,7 @@ object UiUtil {
val cut = view.findViewById<Chip>(R.id.cut) val cut = view.findViewById<Chip>(R.id.cut)
if (items.size > 1 || items.first().url.isEmpty()) cut.isVisible = false if (items.size > 1 || items.first().url.isEmpty()) cut.isVisible = false
else{ else{
cut.setOnClickListener(null)
val downloadItem = items[0] val downloadItem = items[0]
val invalidDuration = downloadItem.duration == "-1" val invalidDuration = downloadItem.duration == "-1"
if (downloadItem.duration.isNotEmpty() && !invalidDuration){ if (downloadItem.duration.isNotEmpty() && !invalidDuration){

View file

@ -823,40 +823,49 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
} }
request.addOption("-x") request.addOption("-x")
val formatSorting = mutableListOf("hasaud") val formatSorting = mutableListOf<String>()
val formatImportance = formatUtil.getAudioFormatImportance() if (sharedPreferences.getBoolean("use_format_sorting", false)) {
for(order in formatImportance) { formatSorting.add("hasaud")
when(order) { val formatImportance = formatUtil.getAudioFormatImportance()
"file_size" -> { for(order in formatImportance) {
formatSorting.add("size") when(order) {
} "file_size" -> {
"language" -> { formatSorting.add("size")
if (preferredLanguage.isNotBlank()) {
formatSorting.add("lang:${preferredLanguage}")
} }
} "language" -> {
"codec" -> { if (preferredLanguage.isNotBlank()) {
if (aCodecPref.isNotBlank()){ formatSorting.add("lang:${preferredLanguage}")
formatSorting.add("acodec:$aCodecPref") }
} }
} "codec" -> {
"container" -> { if (aCodecPref.isNotBlank()){
if(ext.isNotBlank()){ formatSorting.add("acodec:$aCodecPref")
if(!ext.matches("(webm)|(Default)|(${context.getString(R.string.defaultValue)})".toRegex()) && supportedContainers.contains(ext)){ }
request.addOption("--audio-format", ext) }
formatSorting.add("aext:$ext") "container" -> {
if(ext.isNotBlank()){
if(!ext.matches("(webm)|(Default)|(${context.getString(R.string.defaultValue)})".toRegex()) && supportedContainers.contains(ext)){
request.addOption("--audio-format", ext)
formatSorting.add("aext:$ext")
}
} }
} }
} }
} }
if (downloadItem.format.format_id == context.resources.getString(R.string.worst_quality) || downloadItem.format.format_id == "wa" || downloadItem.format.format_id == "worst") {
if(formatSorting.contains("size")) {
formatSorting.remove("size")
}
formatSorting.addAll(0,listOf("+br", "+res", "+fps"))
}
}else if (preferredLanguage.isNotBlank()) {
formatSorting.add("lang:${preferredLanguage}")
} }
if (sharedPreferences.getBoolean("prefer_smaller_formats", false) && !formatSorting.contains("+size")) {
if (downloadItem.format.format_id == context.resources.getString(R.string.worst_quality) || downloadItem.format.format_id == "wa" || downloadItem.format.format_id == "worst") { formatSorting.remove("size")
if(formatSorting.contains("size")) { formatSorting.add(0, "+size")
formatSorting.remove("size")
}
formatSorting.addAll(0,listOf("+br", "+res", "+fps"))
} }
if (abrSort.isNotBlank()){ if (abrSort.isNotBlank()){
@ -867,7 +876,6 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
request.addOption("-S", formatSorting.joinToString(",")) request.addOption("-S", formatSorting.joinToString(","))
} }
request.addOption("-P", downDir.absolutePath) request.addOption("-P", downDir.absolutePath)
@ -1119,38 +1127,41 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
val formatImportance = formatUtil.getVideoFormatImportance() val formatImportance = formatUtil.getVideoFormatImportance()
val formatSorting = mutableListOf<String>() val formatSorting = mutableListOf<String>()
for(order in formatImportance) { if (sharedPreferences.getBoolean("use_format_sorting", false)) {
when(order) { for(order in formatImportance) {
"no_audio" -> { when(order) {
formatSorting.add("+hasaud") "no_audio" -> {
} formatSorting.add("+hasaud")
"codec" -> { }
if (vCodecPref.isNotBlank()) formatSorting.add("vcodec:$vCodecPref") "codec" -> {
if (aCodecPref.isNotBlank()) formatSorting.add("acodec:$aCodecPref") if (vCodecPref.isNotBlank()) formatSorting.add("vcodec:$vCodecPref")
} if (aCodecPref.isNotBlank()) formatSorting.add("acodec:$aCodecPref")
"resolution" -> { }
if (hasGenericResulutionFormat.isNotBlank()) { "resolution" -> {
formatSorting.add("res:${hasGenericResulutionFormat}") if (hasGenericResulutionFormat.isNotBlank()) {
formatSorting.add("res:${hasGenericResulutionFormat}")
}
}
"container" -> {
if (cont.isNotBlank()) formatSorting.add("vext:$cont")
if (acont.isNotBlank()) formatSorting.add("aext:$acont")
} }
} }
"container" -> { }
if (cont.isNotBlank()) formatSorting.add("vext:$cont")
if (acont.isNotBlank()) formatSorting.add("aext:$acont") if (downloadItem.format.format_id == context.resources.getString(R.string.worst_quality) || downloadItem.format.format_id == "worst") {
formatSorting.addAll(0, listOf("+br","+res","+fps"))
}else {
if (sharedPreferences.getBoolean("prefer_smaller_formats", false)) {
formatSorting.add(0, "+size")
} }
} }
} }
if (downloadItem.format.format_id == context.resources.getString(R.string.worst_quality) || downloadItem.format.format_id == "worst") { if (sharedPreferences.getBoolean("prefer_smaller_formats", false) && !formatSorting.contains("+br")) {
formatSorting.addAll(0, listOf("+br","+res","+fps")) formatSorting.add(0, "+size")
}else {
if (sharedPreferences.getBoolean("prefer_smaller_formats", false)) {
formatSorting.add(0, "+size")
}else {
formatSorting.add(0, "size")
}
} }
if (abrSort.isNotBlank()) { if (abrSort.isNotBlank()) {
formatSorting.add("abr~${abrSort}") formatSorting.add("abr~${abrSort}")
} }
@ -1324,7 +1335,11 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
this.add(elements.first().toString()) this.add(elements.first().toString())
if (elements.size > 1) { if (elements.size > 1) {
for (el in elements.drop(1)) { for (el in elements.drop(1)) {
this.add(""""$el"""") val arg = el.toString()
.replace("\\", "\\\\")
.replace("\"", "\\\"")
this.add("\"$arg\"")
} }
} }
} }

View file

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?android:colorAccent" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
</vector>

View file

@ -49,17 +49,41 @@
app:layout_constraintStart_toEndOf="@id/drag_view" app:layout_constraintStart_toEndOf="@id/drag_view"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView <com.google.android.material.chip.ChipGroup
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="3"
android:textSize="14sp"
android:layout_marginEnd="20dp" android:layout_marginEnd="20dp"
android:layout_marginTop="10dp"
app:layout_constraintEnd_toStartOf="@+id/materialSwitch" app:layout_constraintEnd_toStartOf="@+id/materialSwitch"
app:layout_constraintStart_toEndOf="@id/drag_view" app:layout_constraintStart_toEndOf="@id/drag_view"
app:layout_constraintTop_toBottomOf="@+id/title" /> app:layout_constraintTop_toBottomOf="@+id/title"
app:layout_constraintHorizontal_bias="0.0"
android:id="@+id/content"
app:chipSpacing="0dp"
android:layout_width="0dp"
android:layout_height="wrap_content">
<TextView
android:id="@+id/urlRegex"
style="@style/Widget.Material3.FloatingActionButton.Large.Secondary"
android:layout_width="wrap_content"
android:visibility="gone"
android:layout_height="wrap_content"
android:text="URL Regex"
android:background="@drawable/rounded_corner"
android:backgroundTint="?attr/colorAccent"
android:clickable="false"
android:gravity="center"
android:minWidth="30dp"
android:paddingHorizontal="5dp"
android:textSize="12sp"
android:textStyle="bold"
app:cornerRadius="10dp"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:ignore="HardcodedText" />
</com.google.android.material.chip.ChipGroup>
<com.google.android.material.materialswitch.MaterialSwitch <com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/materialSwitch" android:id="@+id/materialSwitch"

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
style="@style/Widget.Material3.FloatingActionButton.Large.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/rounded_corner"
android:backgroundTint="?attr/colorTertiaryContainer"
android:clickable="false"
android:gravity="center"
android:minWidth="30dp"
android:paddingHorizontal="5dp"
android:textSize="12sp"
android:textStyle="bold"
app:cornerRadius="10dp"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" />

View file

@ -464,4 +464,5 @@
<string name="cache_directory">Cache Folder</string> <string name="cache_directory">Cache Folder</string>
<string name="cache_directory_warning">Changing the Cache Folder might cause unexpected issues. If the app cannot write to the configured path, it will resort to the default internal cache directory</string> <string name="cache_directory_warning">Changing the Cache Folder might cause unexpected issues. If the app cannot write to the configured path, it will resort to the default internal cache directory</string>
<string name="app_icon_change">Icon could change and Application will close</string> <string name="app_icon_change">Icon could change and Application will close</string>
<string name="use_format_sorting">Use Format Importance Ordering</string>
</resources> </resources>

View file

@ -34,7 +34,29 @@
android:title="@string/data_fetching_extra_command" /> android:title="@string/data_fetching_extra_command" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/downloading" android:enabled="false"> <PreferenceCategory android:title="@string/format">
<SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="false"
app:icon="@drawable/baseline_arrow_upward_24"
app:key="use_format_sorting"
app:title="@string/use_format_sorting" />
<Preference
android:icon="@drawable/ic_format"
android:key="format_importance_video"
android:dependency="use_format_sorting"
app:title="@string/format_importance" />
<Preference
android:icon="@drawable/ic_format"
android:key="format_importance_audio"
android:dependency="use_format_sorting"
app:title="@string/format_importance" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/downloading" android:enabled="false" app:isPreferenceVisible="false">
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch" android:widgetLayout="@layout/preferece_material_switch"
android:icon="@drawable/if_file_rename" android:icon="@drawable/if_file_rename"

View file

@ -285,15 +285,6 @@
app:key="prefer_smaller_formats" app:key="prefer_smaller_formats"
app:title="@string/prefer_smaller_formats" /> app:title="@string/prefer_smaller_formats" />
<Preference
android:icon="@drawable/ic_format"
android:key="format_importance_video"
app:title="@string/format_importance" />
<Preference
android:icon="@drawable/ic_format"
android:key="format_importance_audio"
app:title="@string/format_importance" />
</PreferenceCategory> </PreferenceCategory>

View file

@ -18,7 +18,7 @@ buildscript {
youtubedlAndroidVerNoPyCrypto = "97bee6c0e8" youtubedlAndroidVerNoPyCrypto = "97bee6c0e8"
youtubedlAndroidVerMine = "8831745ea3" youtubedlAndroidVerMine = "8831745ea3"
oldytdlAndroidVer = "0.14.0" oldytdlAndroidVer = "0.14.0"
workVer = "2.9.1" workVer = "2.10.0"
composeVer = '1.4.2' composeVer = '1.4.2'
kotlinVer = "1.7.21" kotlinVer = "1.7.21"
coroutineVer = '1.7.3' coroutineVer = '1.7.3'