more fixes

This commit is contained in:
deniscerri 2025-06-28 20:51:31 +02:00
parent e5674805da
commit da276e4518
No known key found for this signature in database
GPG key ID: 95C43D517D830350
19 changed files with 190 additions and 49 deletions

View file

@ -11,8 +11,8 @@ plugins {
def properties = new Properties() def properties = new Properties()
def versionMajor = 1 def versionMajor = 1
def versionMinor = 8 def versionMinor = 8
def versionPatch = 5 def versionPatch = 4
def versionBuild = 0 // bump for dogfood builds, public betas, etc. def versionBuild = 2 // bump for dogfood builds, public betas, etc.
def isBeta = true def isBeta = true
def versionExt = "" def versionExt = ""

View file

@ -8,5 +8,6 @@ data class AudioPreferences(
var embedThumb: Boolean = true, var embedThumb: Boolean = true,
var cropThumb: Boolean? = null, var cropThumb: Boolean? = null,
var splitByChapters: Boolean = false, var splitByChapters: Boolean = false,
var sponsorBlockFilters: ArrayList<String> = arrayListOf() var sponsorBlockFilters: ArrayList<String> = arrayListOf(),
var bitrate: String = ""
) : Parcelable ) : Parcelable

View file

@ -293,8 +293,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet()) val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
val bitrate = sharedPreferences.getString("audio_bitrate", "")
val audioPreferences = AudioPreferences(embedThumb, cropThumb,false, ArrayList(sponsorblock!!)) val audioPreferences = AudioPreferences(embedThumb, cropThumb,false, ArrayList(sponsorblock!!), bitrate!!)
val preferredAudioFormats = getPreferredAudioFormats(resultItem.formats) val preferredAudioFormats = getPreferredAudioFormats(resultItem.formats)
@ -473,6 +474,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
} }
val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet()) val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
val audioBitrate = sharedPreferences.getString("audio_bitrate", "")
val extraCommands = when (historyItem.type) { val extraCommands = when (historyItem.type) {
Type.audio -> extraCommandsForAudio Type.audio -> extraCommandsForAudio
@ -484,7 +486,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
} }
}.joinToString(" ") { it.content } }.joinToString(" ") { it.content }
val audioPreferences = AudioPreferences(embedThumb, cropThumb,false, ArrayList(sponsorblock!!)) val audioPreferences = AudioPreferences(embedThumb, cropThumb,false, ArrayList(sponsorblock!!), audioBitrate!!)
val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs, saveAutoSubs, subsLanguages, recodeVideo = recodeVideo) val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs, saveAutoSubs, subsLanguages, recodeVideo = recodeVideo)
var path = defaultPath var path = defaultPath
historyItem.downloadPath.first().apply { historyItem.downloadPath.first().apply {
@ -890,9 +892,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
repository.startDownloadWorker(emptyList(), application) repository.startDownloadWorker(emptyList(), application)
} }
suspend fun queueProcessingDownloads() : QueueDownloadsResult { suspend fun queueProcessingDownloads(ignoreDuplicates: Boolean = false) : QueueDownloadsResult {
val processingItems = repository.getAllProcessingDownloads() val processingItems = repository.getAllProcessingDownloads()
return queueDownloads(processingItems) return queueDownloads(processingItems, ignoreDuplicates)
} }
data class QueueDownloadsResult( data class QueueDownloadsResult(
@ -1262,13 +1264,13 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
} }
} }
suspend fun updateProcessingDownloadTimeAndQueueScheduled(time: Long) : QueueDownloadsResult { suspend fun updateProcessingDownloadTimeAndQueueScheduled(time: Long, ignoreDuplicates: Boolean = false) : QueueDownloadsResult {
val processing = repository.getAllProcessingDownloads() val processing = repository.getAllProcessingDownloads()
processing.forEach { processing.forEach {
it.downloadStartTime = time it.downloadStartTime = time
it.status = DownloadRepository.Status.Scheduled.toString() it.status = DownloadRepository.Status.Scheduled.toString()
} }
return queueDownloads(processing) return queueDownloads(processing, ignoreDuplicates)
} }
fun checkIfAllProcessingItemsHaveSameType(selectedItems: List<Long>?) : Pair<Boolean, Type> { fun checkIfAllProcessingItemsHaveSameType(selectedItems: List<Long>?) : Pair<Boolean, Type> {

View file

@ -315,6 +315,9 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
splitByChaptersClicked = { splitByChaptersClicked = {
downloadItem.audioPreferences.splitByChapters = it downloadItem.audioPreferences.splitByChapters = it
}, },
bitrateSet = {
downloadItem.audioPreferences.bitrate = it
},
filenameTemplateSet = { filenameTemplateSet = {
downloadItem.customFileNameTemplate = it downloadItem.customFileNameTemplate = it
}, },

View file

@ -85,6 +85,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
private lateinit var result: ResultItem private lateinit var result: ResultItem
private lateinit var type: Type private lateinit var type: Type
private var ignoreDuplicates: Boolean = false
private var disableUpdateData : Boolean = false private var disableUpdateData : Boolean = false
private var currentDownloadItem: DownloadItem? = null private var currentDownloadItem: DownloadItem? = null
private var incognito: Boolean = false private var incognito: Boolean = false
@ -108,6 +109,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
} }
type = arguments?.getSerializable("type") as Type type = arguments?.getSerializable("type") as Type
disableUpdateData = arguments?.getBoolean("disableUpdateData") == true disableUpdateData = arguments?.getBoolean("disableUpdateData") == true
ignoreDuplicates = arguments?.getBoolean("ignore_duplicates") == true
if (res == null){ if (res == null){
dismiss() dismiss()
@ -335,7 +337,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
lifecycleScope.launch { lifecycleScope.launch {
val result = withContext(Dispatchers.IO){ val result = withContext(Dispatchers.IO){
downloadViewModel.queueDownloads(itemsToQueue) downloadViewModel.queueDownloads(itemsToQueue, ignoreDuplicates)
} }
if (result.message.isNotBlank()){ if (result.message.isNotBlank()){
@ -350,7 +352,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
}else{ }else{
lifecycleScope.launch { lifecycleScope.launch {
val result = withContext(Dispatchers.IO){ val result = withContext(Dispatchers.IO){
downloadViewModel.queueDownloads(listOf(item)) downloadViewModel.queueDownloads(listOf(item), ignoreDuplicates)
} }
if (result.message.isNotBlank()){ if (result.message.isNotBlank()){
@ -381,7 +383,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
lifecycleScope.launch { lifecycleScope.launch {
val result = withContext(Dispatchers.IO) { val result = withContext(Dispatchers.IO) {
downloadViewModel.queueDownloads(itemsToQueue) downloadViewModel.queueDownloads(itemsToQueue, ignoreDuplicates)
} }
withContext(Dispatchers.Main){ withContext(Dispatchers.Main){
handleDuplicatesAndDismiss(result.duplicateDownloadIDs) handleDuplicatesAndDismiss(result.duplicateDownloadIDs)
@ -390,7 +392,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
}) })
}else{ }else{
val result = withContext(Dispatchers.IO) { val result = withContext(Dispatchers.IO) {
downloadViewModel.queueDownloads(listOf(item)) downloadViewModel.queueDownloads(listOf(item), ignoreDuplicates)
} }
handleDuplicatesAndDismiss(result.duplicateDownloadIDs) handleDuplicatesAndDismiss(result.duplicateDownloadIDs)
} }

View file

@ -105,6 +105,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
private lateinit var currentDownloadIDs: List<Long> private lateinit var currentDownloadIDs: List<Long>
private lateinit var currentHistoryIDs: List<Long> private lateinit var currentHistoryIDs: List<Long>
private var ignoreDuplicates: Boolean = false
private var processingItemsCount : Int = 0 private var processingItemsCount : Int = 0
private lateinit var formatBtn : MenuItem private lateinit var formatBtn : MenuItem
@ -128,6 +129,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
currentDownloadIDs = arguments?.getLongArray("currentDownloadIDs")?.toList() ?: listOf() currentDownloadIDs = arguments?.getLongArray("currentDownloadIDs")?.toList() ?: listOf()
currentHistoryIDs = arguments?.getLongArray("currentHistoryIDs")?.toList() ?: listOf() currentHistoryIDs = arguments?.getLongArray("currentHistoryIDs")?.toList() ?: listOf()
ignoreDuplicates = arguments?.getBoolean("ignore_duplicates") == true
processingItemsCount = currentDownloadIDs.size processingItemsCount = currentDownloadIDs.size
} }
@ -242,7 +244,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
lifecycleScope.launch { lifecycleScope.launch {
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
historyViewModel.deleteAllWithIDsCheckFiles(currentHistoryIDs) historyViewModel.deleteAllWithIDsCheckFiles(currentHistoryIDs)
val result = downloadViewModel.updateProcessingDownloadTimeAndQueueScheduled(cal.timeInMillis) val result = downloadViewModel.updateProcessingDownloadTimeAndQueueScheduled(cal.timeInMillis, ignoreDuplicates)
if (result.message.isNotBlank()){ if (result.message.isNotBlank()){
lifecycleScope.launch { lifecycleScope.launch {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@ -265,7 +267,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
lifecycleScope.launch { lifecycleScope.launch {
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
historyViewModel.deleteAllWithIDsCheckFiles(currentHistoryIDs) historyViewModel.deleteAllWithIDsCheckFiles(currentHistoryIDs)
val result = downloadViewModel.queueProcessingDownloads() val result = downloadViewModel.queueProcessingDownloads(ignoreDuplicates)
if (result.message.isNotBlank()){ if (result.message.isNotBlank()){
lifecycleScope.launch { lifecycleScope.launch {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@ -545,6 +547,15 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
} }
CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } } CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } }
}, },
bitrateSet = { bitrate ->
items.forEach {
it.audioPreferences.bitrate = bitrate
}
requireActivity().lifecycleScope.launch {
items.forEach { downloadViewModel.updateDownload(it) }
}
bottomSheet.dismiss()
},
filenameTemplateSet = {template -> filenameTemplateSet = {template ->
items.forEach { items.forEach {
it.customFileNameTemplate = template it.customFileNameTemplate = template

View file

@ -516,7 +516,7 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
if (!filePresent) { if (!filePresent) {
historyViewModel.delete(it, false) historyViewModel.delete(it, false)
} }
downloadViewModel.queueDownloads(listOf(downloadItem)) downloadViewModel.queueDownloads(listOf(downloadItem), ignoreDuplicates = true)
} }
historyViewModel.delete(it, false) historyViewModel.delete(it, false)
}, },
@ -524,6 +524,7 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf( findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf(
Pair("result", downloadViewModel.createResultItemFromHistory(it)), Pair("result", downloadViewModel.createResultItemFromHistory(it)),
Pair("type", it.type), Pair("type", it.type),
Pair("ignore_duplicates", true),
)) ))
} }
) )
@ -647,7 +648,8 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf( findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf(
Pair("result", downloadViewModel.createResultItemFromHistory(tmp)), Pair("result", downloadViewModel.createResultItemFromHistory(tmp)),
Pair("type", tmp.type) Pair("type", tmp.type),
Pair("ignore_duplicates", true)
)) ))
}else { }else {
val showDownloadCard = sharedPreferences.getBoolean("download_card", true) val showDownloadCard = sharedPreferences.getBoolean("download_card", true)
@ -656,6 +658,7 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
if (showDownloadCard){ if (showDownloadCard){
val bundle = Bundle() val bundle = Bundle()
bundle.putLongArray("currentHistoryIDs", selectedObjects.toLongArray()) bundle.putLongArray("currentHistoryIDs", selectedObjects.toLongArray())
bundle.putBoolean("ignore_duplicates", true)
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundle) findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundle)
} }
} }
@ -741,7 +744,8 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
historyAdapter.notifyItemChanged(position) historyAdapter.notifyItemChanged(position)
findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf( findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf(
Pair("result", downloadViewModel.createResultItemFromHistory(item)), Pair("result", downloadViewModel.createResultItemFromHistory(item)),
Pair("type", item.type) Pair("type", item.type),
Pair("ignore_duplicates", true)
)) ))
} }
} }

View file

@ -6,6 +6,7 @@ import androidx.navigation.fragment.findNavController
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdl.R import com.deniscerri.ytdl.R
import com.deniscerri.ytdl.util.UiUtil import com.deniscerri.ytdl.util.UiUtil
@ -74,6 +75,31 @@ class ProcessingSettingsFragment : BaseSettingsFragment() {
} }
} }
findPreference<Preference>("audio_bitrate")?.apply {
var currentValue = prefs.getString("audio_bitrate", "")!!
val entries = context.getStringArray(R.array.audio_bitrate)
val entryValues = context.getStringArray(R.array.audio_bitrate_values)
summary = if (currentValue.isNotBlank()) {
entries[entryValues.indexOf(currentValue)]
}else {
getString(R.string.defaultValue)
}
setOnPreferenceClickListener {
currentValue = prefs.getString("audio_bitrate", "")!!
UiUtil.showAudioBitrateDialog(requireActivity(), currentValue) {
editor.putString("audio_bitrate", it).apply()
summary = if (it.isNotBlank()) {
entries[entryValues.indexOf(it)]
}else {
getString(R.string.defaultValue)
}
}
true
}
}
findPreference<Preference>("reset_preferences")?.setOnPreferenceClickListener { findPreference<Preference>("reset_preferences")?.setOnPreferenceClickListener {
UiUtil.showGenericConfirmDialog(requireContext(), getString(R.string.reset), getString(R.string.reset_preferences_in_screen)) { UiUtil.showGenericConfirmDialog(requireContext(), getString(R.string.reset), getString(R.string.reset_preferences_in_screen)) {
resetPreferences(editor, R.xml.processing_preferences) resetPreferences(editor, R.xml.processing_preferences)

View file

@ -320,7 +320,7 @@ object Extensions {
Picasso.get() Picasso.get()
.load(imageURL) .load(imageURL)
.resize(1280, 0) .resize(1280, 0)
.transform(BlurTransformation(context, 10, 1)) .transform(BlurTransformation(context, 7, 1))
.onlyScaleDown() .onlyScaleDown()
.into(this) .into(this)

View file

@ -1180,6 +1180,21 @@ object UiUtil {
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).gravity = Gravity.START dialog.getButton(AlertDialog.BUTTON_NEUTRAL).gravity = Gravity.START
} }
fun showAudioBitrateDialog(context: Activity, currentValue: String, ok: (newValue: String) -> Unit){
val entries = context.getStringArray(R.array.audio_bitrate)
val entryValues = context.getStringArray(R.array.audio_bitrate_values)
val prefIndex = entryValues.indexOf(currentValue)
MaterialAlertDialogBuilder(context)
.setTitle(context.getString(R.string.bitrate))
.setSingleChoiceItems(entries, prefIndex) { dialog, index ->
ok(entryValues[index])
dialog.dismiss()
}
.setNegativeButton(R.string.cancel, null)
.show()
}
fun copyToClipboard(text: String, activity: Activity){ fun copyToClipboard(text: String, activity: Activity){
val clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
@ -1620,6 +1635,7 @@ object UiUtil {
embedThumbClicked: (Boolean) -> Unit, embedThumbClicked: (Boolean) -> Unit,
cropThumbClicked: (Boolean) -> Unit, cropThumbClicked: (Boolean) -> Unit,
splitByChaptersClicked: (Boolean) -> Unit, splitByChaptersClicked: (Boolean) -> Unit,
bitrateSet: (String) -> Unit,
filenameTemplateSet: (String) -> Unit, filenameTemplateSet: (String) -> Unit,
sponsorBlockItemsSet: (Array<String>, List<Boolean>) -> Unit, sponsorBlockItemsSet: (Array<String>, List<Boolean>) -> Unit,
cutClicked: (VideoCutListener) -> Unit, cutClicked: (VideoCutListener) -> Unit,
@ -1654,6 +1670,34 @@ object UiUtil {
splitByChaptersClicked(splitByChapters.isChecked) splitByChaptersClicked(splitByChapters.isChecked)
} }
val bitrate = view.findViewById<Chip>(R.id.audio_bitrate)
bitrate.apply {
fun changeLabelText(newVal: String) {
if(newVal.isBlank()) return
val t = context.getString(R.string.bitrate) + " (${newVal})"
text = t
}
fun getCurrentBitrate() = if (items.size == 1 || items.all { it.audioPreferences.bitrate == items[0].audioPreferences.bitrate }){
items[0].audioPreferences.bitrate
}else {
""
}
changeLabelText(getCurrentBitrate())
setOnClickListener {
showAudioBitrateDialog(context, getCurrentBitrate()) {
var newText = context.getString(R.string.bitrate)
if (it.isNotBlank()) {
newText += " (${it})"
}
text = newText
bitrateSet(it)
}
}
}
val filenameTemplate = view.findViewById<Chip>(R.id.filename_template) val filenameTemplate = view.findViewById<Chip>(R.id.filename_template)
filenameTemplate.setOnClickListener { filenameTemplate.setOnClickListener {
val currentFilename = if (items.size == 1 || items.all { it.customFileNameTemplate == items[0].customFileNameTemplate }){ val currentFilename = if (items.size == 1 || items.all { it.customFileNameTemplate == items[0].customFileNameTemplate }){

View file

@ -867,13 +867,13 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
} }
if(downloadItem.title.isNotBlank()){ if(downloadItem.title.isNotBlank()){
metadataCommands.addOption("--replace-in-metadata", "title", """^.*$""", downloadItem.title) metadataCommands.addOption("--replace-in-metadata", "title", """^.*$""", downloadItem.title.replace("""\""", """\\"""))
metadataCommands.addOption("--parse-metadata", "%(title)s:%(meta_title)s") metadataCommands.addOption("--parse-metadata", "%(title)s:%(meta_title)s")
} }
if (downloadItem.author.isNotBlank()){ if (downloadItem.author.isNotBlank()){
metadataCommands.addOption("--replace-in-metadata", "uploader", """^.*$""", downloadItem.author) metadataCommands.addOption("--replace-in-metadata", "uploader", """^.*$""", downloadItem.author.replace("""\""", """\\"""))
metadataCommands.addOption("--parse-metadata", "%(uploader)s:%(artist)s") metadataCommands.addOption("--parse-metadata", "%(uploader)s:%(artist)s")
} }
@ -892,10 +892,6 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
} }
} }
if (sharedPreferences.getBoolean("use_audio_quality", false)){
request.addOption("--audio-quality", sharedPreferences.getInt("audio_quality", 0).toString())
}
if (sharedPreferences.getBoolean("write_description", false)){ if (sharedPreferences.getBoolean("write_description", false)){
request.addOption("--write-description") request.addOption("--write-description")
} }
@ -1089,6 +1085,10 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
} }
} }
if (downloadItem.audioPreferences.bitrate.isNotBlank()) {
request.addOption("--audio-quality", downloadItem.audioPreferences.bitrate)
}
} }
DownloadViewModel.Type.video -> { DownloadViewModel.Type.video -> {
val useArtistTags = if (downloadItem.url.isYoutubeURL()) "artists,artist," else "" val useArtistTags = if (downloadItem.url.isYoutubeURL()) "artists,artist," else ""

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="M19,4L5,4c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.9,-2 -2,-2zM11,15L9.5,15v-2h-2v2L6,15L6,9h1.5v2.5h2L9.5,9L11,9v6zM18,14c0,0.55 -0.45,1 -1,1h-0.75v1.5h-1.5L14.75,15L14,15c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1h3c0.55,0 1,0.45 1,1v4zM14.5,13.5h2v-3h-2v3z"/>
</vector>

View file

@ -80,6 +80,21 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.chip.Chip
android:id="@+id/audio_bitrate"
style="@style/Widget.Material3.Chip.Assist.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/bitrate"
app:chipIconVisible="true"
app:chipIcon="@drawable/baseline_high_quality_24"
android:minWidth="30dp"
app:cornerRadius="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/filename_template" android:id="@+id/filename_template"
style="@style/Widget.Material3.Chip.Assist.Elevated" style="@style/Widget.Material3.Chip.Assist.Elevated"

View file

@ -24,7 +24,7 @@
android:checked="false" android:checked="false"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" android:textStyle="bold"
android:text="Web Client Po Token" android:text="@string/web_client_po_token"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView

View file

@ -131,7 +131,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:text="PO Token" android:text="@string/potoken"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
@ -140,7 +140,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:tag="potoken_gvs" android:tag="potoken_gvs"
android:hint="PO Token (GVS)" android:hint="GVS"
app:endIconDrawable="@drawable/ic_clipboard" app:endIconDrawable="@drawable/ic_clipboard"
app:endIconMode="custom" app:endIconMode="custom"
android:paddingBottom="10dp"> android:paddingBottom="10dp">
@ -158,10 +158,30 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:tag="potoken_player" android:tag="potoken_player"
android:hint="PO Token (Player)" android:hint="Player"
app:endIconDrawable="@drawable/ic_clipboard" app:endIconDrawable="@drawable/ic_clipboard"
app:endIconMode="custom" app:endIconMode="custom"
android:paddingBottom="10dp"> android:paddingBottom="10dp"
tools:ignore="HardcodedText">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/po_token_subs"
style="@style/Widget.Material3.TextInputLayout.FilledBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="potoken_subs"
android:hint="Subs"
app:endIconDrawable="@drawable/ic_clipboard"
app:endIconMode="custom"
android:paddingBottom="10dp"
tools:ignore="HardcodedText">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
@ -176,7 +196,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:tag="url_regex" android:tag="url_regex"
android:hint="PO Token URL Regex" android:hint="URL Regex"
app:endIconDrawable="@drawable/ic_plus" app:endIconDrawable="@drawable/ic_plus"
app:endIconMode="custom" app:endIconMode="custom"
android:paddingBottom="10dp" android:paddingBottom="10dp"

View file

@ -1556,4 +1556,20 @@
<item>web_music</item> <item>web_music</item>
</string-array> </string-array>
<string-array name="audio_bitrate">
<item>@string/defaultValue</item>
<item>320 kpbs</item>
<item>192 kpbs</item>
<item>128 kpbs</item>
<item>64 kpbs</item>
</string-array>
<string-array name="audio_bitrate_values">
<item></item>
<item>320k</item>
<item>192k</item>
<item>128k</item>
<item>64k</item>
</string-array>
</resources> </resources>

View file

@ -478,4 +478,6 @@
<string name="custom">Custom</string> <string name="custom">Custom</string>
<string name="cut_unsupported">Cutting is not available for this item</string> <string name="cut_unsupported">Cutting is not available for this item</string>
<string name="select_between_desc">Specify the range of items to select</string> <string name="select_between_desc">Specify the range of items to select</string>
<string name="potoken">PO Token</string>
<string name="web_client_po_token">Web Client PO Token</string>
</resources> </resources>

View file

@ -14,7 +14,7 @@
android:defaultValue="" android:defaultValue=""
android:icon="@drawable/baseline_stars_24" android:icon="@drawable/baseline_stars_24"
android:key="generate_po_tokens" android:key="generate_po_tokens"
android:summary="Web Client PO Token" android:summary="@string/web_client_po_token"
android:title="@string/generate_potokens" /> android:title="@string/generate_potokens" />
<SwitchPreferenceCompat <SwitchPreferenceCompat

View file

@ -73,6 +73,13 @@
app:summary="@string/embed_metadata_summary" app:summary="@string/embed_metadata_summary"
app:title="@string/embed_metadata" /> app:title="@string/embed_metadata" />
<Preference
android:icon="@drawable/baseline_high_quality_24"
app:key="audio_bitrate"
app:defaultValue=""
android:summary="@string/defaultValue"
app:title="@string/bitrate" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch" android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="true" app:defaultValue="true"
@ -98,23 +105,6 @@
app:summary="@string/playlist_as_album_summary" app:summary="@string/playlist_as_album_summary"
app:title="@string/playlist_as_album" /> app:title="@string/playlist_as_album" />
<SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="false"
app:icon="@drawable/ic_music"
app:key="use_audio_quality"
app:title="@string/custom_audio_quality" />
<SeekBarPreference
android:defaultValue="0"
android:max="10"
android:dependency="use_audio_quality"
app:key="audio_quality"
app:min="0"
app:showSeekBarValue="true"
app:summary="@string/audio_quality_summary"
app:title="@string/audio_quality" />
<ListPreference <ListPreference
app:icon="@drawable/ic_language" app:icon="@drawable/ic_language"
app:dialogTitle="@string/preferred_audio_language" app:dialogTitle="@string/preferred_audio_language"