Fix format details fps in arabic being rtl
Removed h264 from codec
Fixed Search engine not getting updated in home when changing in settings fragment.
Added language preference in android 13 and up
Added multiple selection for command templates
Fix issue when app crashes when creating config files for items with weird titles
Fix auto preferred download type not working correctly in yt music
This commit is contained in:
deniscerri 2023-09-16 14:22:01 +02:00
parent 25f53aeb87
commit 2d08d2f643
No known key found for this signature in database
GPG key ID: 95C43D517D830350
30 changed files with 422 additions and 304 deletions

View file

@ -10,7 +10,7 @@ def properties = new Properties()
def versionMajor = 1 def versionMajor = 1
def versionMinor = 6 def versionMinor = 6
def versionPatch = 6 def versionPatch = 6
def versionBuild = 2 // bump for dogfood builds, public betas, etc. def versionBuild = 3 // bump for dogfood builds, public betas, etc.
def versionExt = "" def versionExt = ""
if (versionBuild > 0){ if (versionBuild > 0){

View file

@ -17,13 +17,14 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.DownloadItemSimple
import com.deniscerri.ytdlnis.database.repository.DownloadRepository import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.FileUtil
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : PagingDataAdapter<DownloadItem, GenericDownloadAdapter.ViewHolder>(DIFF_CALLBACK) { class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : PagingDataAdapter<DownloadItemSimple, GenericDownloadAdapter.ViewHolder>(DIFF_CALLBACK) {
private val onItemClickListener: OnItemClickListener private val onItemClickListener: OnItemClickListener
private val activity: Activity private val activity: Activity
val checkedItems: ArrayList<Long> val checkedItems: ArrayList<Long>
@ -73,7 +74,7 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
} }
val duration = card.findViewById<TextView>(R.id.duration) val duration = card.findViewById<TextView>(R.id.duration)
duration.text = item!!.duration duration.text = item.duration
// TITLE ---------------------------------- // TITLE ----------------------------------
val itemTitle = card.findViewById<TextView>(R.id.title) val itemTitle = card.findViewById<TextView>(R.id.title)
@ -204,13 +205,13 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
} }
companion object { companion object {
private val DIFF_CALLBACK: DiffUtil.ItemCallback<DownloadItem> = object : DiffUtil.ItemCallback<DownloadItem>() { private val DIFF_CALLBACK: DiffUtil.ItemCallback<DownloadItemSimple> = object : DiffUtil.ItemCallback<DownloadItemSimple>() {
override fun areItemsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean { override fun areItemsTheSame(oldItem: DownloadItemSimple, newItem: DownloadItemSimple): Boolean {
val ranged = arrayListOf(oldItem.id, newItem.id) val ranged = arrayListOf(oldItem.id, newItem.id)
return ranged[0] == ranged[1] return ranged[0] == ranged[1]
} }
override fun areContentsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean { override fun areContentsTheSame(oldItem: DownloadItemSimple, newItem: DownloadItemSimple): Boolean {
return oldItem.id == newItem.id && oldItem.title == newItem.title && oldItem.author == newItem.author && oldItem.thumb == newItem.thumb return oldItem.id == newItem.id && oldItem.title == newItem.title && oldItem.author == newItem.author && oldItem.thumb == newItem.thumb
} }
} }

View file

@ -3,6 +3,7 @@ package com.deniscerri.ytdlnis.database.dao
import androidx.paging.PagingSource import androidx.paging.PagingSource
import androidx.room.* import androidx.room.*
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.DownloadItemSimple
import com.deniscerri.ytdlnis.database.models.Format import com.deniscerri.ytdlnis.database.models.Format
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -34,7 +35,7 @@ interface DownloadDao {
fun getActiveAndQueuedDownloads() : Flow<List<DownloadItem>> fun getActiveAndQueuedDownloads() : Flow<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Queued' or status='QueuedPaused' ORDER BY downloadStartTime, id") @Query("SELECT * FROM downloads WHERE status='Queued' or status='QueuedPaused' ORDER BY downloadStartTime, id")
fun getQueuedDownloads() : PagingSource<Int, DownloadItem> fun getQueuedDownloads() : PagingSource<Int, DownloadItemSimple>
@Query("SELECT format FROM downloads WHERE status='Queued' or status='QueuedPaused'") @Query("SELECT format FROM downloads WHERE status='Queued' or status='QueuedPaused'")
fun getSelectedFormatFromQueued() : List<Format> fun getSelectedFormatFromQueued() : List<Format>
@ -49,7 +50,7 @@ interface DownloadDao {
fun getQueuedDownloadsList() : List<DownloadItem> fun getQueuedDownloadsList() : List<DownloadItem>
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC") @Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
fun getCancelledDownloads() : PagingSource<Int, DownloadItem> fun getCancelledDownloads() : PagingSource<Int, DownloadItemSimple>
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC") @Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
fun getCancelledDownloadsList() : List<DownloadItem> fun getCancelledDownloadsList() : List<DownloadItem>
@ -58,13 +59,13 @@ interface DownloadDao {
fun getPausedDownloadsList() : List<DownloadItem> fun getPausedDownloadsList() : List<DownloadItem>
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC") @Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
fun getErroredDownloads() : PagingSource<Int, DownloadItem> fun getErroredDownloads() : PagingSource<Int, DownloadItemSimple>
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC") @Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
fun getErroredDownloadsList() : List<DownloadItem> fun getErroredDownloadsList() : List<DownloadItem>
@Query("SELECT * FROM downloads WHERE status='Saved' ORDER BY id DESC") @Query("SELECT * FROM downloads WHERE status='Saved' ORDER BY id DESC")
fun getSavedDownloads() : PagingSource<Int, DownloadItem> fun getSavedDownloads() : PagingSource<Int, DownloadItemSimple>
@Query("SELECT * FROM downloads WHERE status='Saved' ORDER BY id DESC") @Query("SELECT * FROM downloads WHERE status='Saved' ORDER BY id DESC")
fun getSavedDownloadsList() : List<DownloadItem> fun getSavedDownloadsList() : List<DownloadItem>
@ -115,6 +116,9 @@ interface DownloadDao {
@Query("DELETE FROM downloads WHERE status='Saved'") @Query("DELETE FROM downloads WHERE status='Saved'")
suspend fun deleteSaved() suspend fun deleteSaved()
@Query("DELETE FROM downloads WHERE id in (:list)")
suspend fun deleteAllWithIDs(list: List<Long>)
@Query("UPDATE downloads SET status='Cancelled' WHERE status='Queued' or status='QueuedPaused'") @Query("UPDATE downloads SET status='Cancelled' WHERE status='Queued' or status='QueuedPaused'")
suspend fun cancelQueued() suspend fun cancelQueued()

View file

@ -0,0 +1,21 @@
package com.deniscerri.ytdlnis.database.models
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
@Entity(tableName = "downloads")
data class DownloadItemSimple(
@PrimaryKey(autoGenerate = true)
var id: Long,
var url: String,
var title: String,
var author: String,
var thumb: String,
var duration: String,
var format: Format,
@ColumnInfo(defaultValue = "Queued")
var status: String,
var logID: Long?
)

View file

@ -5,6 +5,7 @@ import androidx.paging.PagingConfig
import com.deniscerri.ytdlnis.App import com.deniscerri.ytdlnis.App
import com.deniscerri.ytdlnis.database.dao.DownloadDao import com.deniscerri.ytdlnis.database.dao.DownloadDao
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.DownloadItemSimple
import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.FileUtil
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import java.io.File import java.io.File
@ -16,19 +17,19 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
pagingSourceFactory = {downloadDao.getAllDownloads()} pagingSourceFactory = {downloadDao.getAllDownloads()}
) )
val activeDownloads : Flow<List<DownloadItem>> = downloadDao.getActiveDownloads() val activeDownloads : Flow<List<DownloadItem>> = downloadDao.getActiveDownloads()
val queuedDownloads : Pager<Int, DownloadItem> = Pager( val queuedDownloads : Pager<Int, DownloadItemSimple> = Pager(
config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1), config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1),
pagingSourceFactory = {downloadDao.getQueuedDownloads()} pagingSourceFactory = {downloadDao.getQueuedDownloads()}
) )
val cancelledDownloads : Pager<Int, DownloadItem> = Pager( val cancelledDownloads : Pager<Int, DownloadItemSimple> = Pager(
config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1), config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1),
pagingSourceFactory = {downloadDao.getCancelledDownloads()} pagingSourceFactory = {downloadDao.getCancelledDownloads()}
) )
val erroredDownloads : Pager<Int, DownloadItem> = Pager( val erroredDownloads : Pager<Int, DownloadItemSimple> = Pager(
config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1), config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1),
pagingSourceFactory = {downloadDao.getErroredDownloads()} pagingSourceFactory = {downloadDao.getErroredDownloads()}
) )
val savedDownloads : Pager<Int, DownloadItem> = Pager( val savedDownloads : Pager<Int, DownloadItemSimple> = Pager(
config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1), config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1),
pagingSourceFactory = {downloadDao.getSavedDownloads()} pagingSourceFactory = {downloadDao.getSavedDownloads()}
) )
@ -114,6 +115,11 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
downloadDao.deleteSaved() downloadDao.deleteSaved()
} }
suspend fun deleteAllWithIDs(ids: List<Long>){
downloadDao.deleteAllWithIDs(ids)
}
suspend fun cancelQueued(){ suspend fun cancelQueued(){
downloadDao.cancelQueued() downloadDao.cancelQueued()
} }

View file

@ -8,6 +8,7 @@ import android.content.res.Resources
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.os.Looper import android.os.Looper
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
@ -31,6 +32,7 @@ import com.deniscerri.ytdlnis.database.dao.ResultDao
import com.deniscerri.ytdlnis.database.models.AudioPreferences import com.deniscerri.ytdlnis.database.models.AudioPreferences
import com.deniscerri.ytdlnis.database.models.CommandTemplate import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.DownloadItemSimple
import com.deniscerri.ytdlnis.database.models.Format import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.HistoryItem import com.deniscerri.ytdlnis.database.models.HistoryItem
import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.models.ResultItem
@ -62,12 +64,12 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
private val commandTemplateDao: CommandTemplateDao private val commandTemplateDao: CommandTemplateDao
private val infoUtil : InfoUtil private val infoUtil : InfoUtil
val allDownloads : Flow<PagingData<DownloadItem>> val allDownloads : Flow<PagingData<DownloadItem>>
val queuedDownloads : Flow<PagingData<DownloadItem>> val queuedDownloads : Flow<PagingData<DownloadItemSimple>>
val activeDownloads : LiveData<List<DownloadItem>> val activeDownloads : LiveData<List<DownloadItem>>
val activeDownloadsCount : LiveData<Int> val activeDownloadsCount : LiveData<Int>
val cancelledDownloads : Flow<PagingData<DownloadItem>> val cancelledDownloads : Flow<PagingData<DownloadItemSimple>>
val erroredDownloads : Flow<PagingData<DownloadItem>> val erroredDownloads : Flow<PagingData<DownloadItemSimple>>
val savedDownloads : Flow<PagingData<DownloadItem>> val savedDownloads : Flow<PagingData<DownloadItemSimple>>
private var bestVideoFormat : Format private var bestVideoFormat : Format
private var bestAudioFormat : Format private var bestAudioFormat : Format
@ -125,35 +127,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
resources = Resources(application.assets, metrics, confTmp) resources = Resources(application.assets, metrics, confTmp)
val videoFormatValues = resources.getStringArray(R.array.video_formats_values)
videoContainer = sharedPreferences.getString("video_format", "Default") videoContainer = sharedPreferences.getString("video_format", "Default")
defaultVideoFormats = getGenericVideoFormats()
defaultVideoFormats = mutableListOf()
videoFormatValues.forEach {
val tmp = Format(
it,
videoContainer!!,
"",
"",
"",
0,
it
)
defaultVideoFormats.add(tmp)
}
formatIDPreference.reversed().forEach {
val tmp = Format(
it,
videoContainer!!,
"",
"",
"",
0,
it
)
defaultVideoFormats.add(tmp)
}
bestVideoFormat = defaultVideoFormats.last() bestVideoFormat = defaultVideoFormats.last()
audioContainer = sharedPreferences.getString("audio_format", "mp3") audioContainer = sharedPreferences.getString("audio_format", "mp3")
@ -200,11 +175,17 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
return repository.getItemByID(id) return repository.getItemByID(id)
} }
private fun getSuitableDownloadType(url: String): Type{ fun getDownloadType(t: Type, url: String) : Type {
if (urlsForAudioType.any { url.contains(it) }){ return when(t){
return Type.audio Type.auto -> {
if (urlsForAudioType.any { url.contains(it) }){
Type.audio
}else{
Type.video
}
}
else -> t
} }
return Type.video
} }
fun createDownloadItemFromResult(resultItem: ResultItem, givenType: Type) : DownloadItem { fun createDownloadItemFromResult(resultItem: ResultItem, givenType: Type) : DownloadItem {
@ -214,10 +195,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false) val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
var type = givenType var type = getDownloadType(givenType, resultItem.url)
if (type == Type.auto){
type = getSuitableDownloadType(resultItem.url)
}
if(type == Type.command && commandTemplateDao.getTotalNumber() == 0) type = Type.video if(type == Type.command && commandTemplateDao.getTotalNumber() == 0) type = Type.video
val customFileNameTemplate = when(type) { val customFileNameTemplate = when(type) {
@ -466,8 +444,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val audioFormats = resources.getStringArray(R.array.audio_formats) val audioFormats = resources.getStringArray(R.array.audio_formats)
val formats = mutableListOf<Format>() val formats = mutableListOf<Format>()
val containerPreference = sharedPreferences.getString("audio_format", "") val containerPreference = sharedPreferences.getString("audio_format", "")
audioFormatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) }
audioFormats.forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) } audioFormats.forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) }
audioFormatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"","", "",1, it)) }
return formats return formats
} }
@ -475,8 +453,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val videoFormats = resources.getStringArray(R.array.video_formats_values) val videoFormats = resources.getStringArray(R.array.video_formats_values)
val formats = mutableListOf<Format>() val formats = mutableListOf<Format>()
val containerPreference = sharedPreferences.getString("video_format", "") val containerPreference = sharedPreferences.getString("video_format", "")
formatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"Default","", "",0, it)) }
videoFormats.forEach { formats.add(Format(it, containerPreference!!,"Default","", "",0, it)) } videoFormats.forEach { formats.add(Format(it, containerPreference!!,"Default","", "",0, it)) }
formatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"Default","", "",1, it)) }
return formats return formats
} }
@ -489,8 +467,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val list : MutableList<DownloadItem> = mutableListOf() val list : MutableList<DownloadItem> = mutableListOf()
var preferredType = sharedPreferences.getString("preferred_download_type", "video") var preferredType = sharedPreferences.getString("preferred_download_type", "video")
items.forEach { items.forEach {
if (preferredType == Type.auto.toString()) preferredType = getSuitableDownloadType(it!!.url).toString() preferredType = getDownloadType(Type.valueOf(preferredType!!), it!!.url).toString()
list.add(createDownloadItemFromResult(it!!, Type.valueOf(preferredType!!))) list.add(createDownloadItemFromResult(it, Type.valueOf(preferredType!!)))
} }
return list return list
} }
@ -525,6 +503,11 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
repository.deleteSaved() repository.deleteSaved()
} }
fun deleteAllWithID(ids: List<Long>) = viewModelScope.launch(Dispatchers.IO){
Log.e("Aa", ids.size.toString())
repository.deleteAllWithIDs(ids)
}
fun cancelQueued() = viewModelScope.launch(Dispatchers.IO) { fun cancelQueued() = viewModelScope.launch(Dispatchers.IO) {
repository.cancelQueued() repository.cancelQueued()
} }

View file

@ -164,7 +164,7 @@ class ShareActivity : BaseActivity() {
private fun showDownloadSheet(it: ResultItem){ private fun showDownloadSheet(it: ResultItem){
if (sharedPreferences.getBoolean("download_card", true)){ if (sharedPreferences.getBoolean("download_card", true)){
val bottomSheet = DownloadBottomSheetDialog(it, DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!), null, quickDownload) val bottomSheet = DownloadBottomSheetDialog(it,downloadViewModel.getDownloadType(DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!), it.url), null, quickDownload)
bottomSheet.show(supportFragmentManager, "downloadSingleSheet") bottomSheet.show(supportFragmentManager, "downloadSingleSheet")
}else{ }else{
lifecycleScope.launch(Dispatchers.IO){ lifecycleScope.launch(Dispatchers.IO){

View file

@ -23,6 +23,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.children import androidx.core.view.children
import androidx.core.view.forEach import androidx.core.view.forEach
import androidx.core.view.forEachIndexed
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -283,7 +284,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
val providersChipGroup = searchView!!.findViewById<ChipGroup>(R.id.providers) val providersChipGroup = searchView!!.findViewById<ChipGroup>(R.id.providers)
val providers = resources.getStringArray(R.array.search_engines) val providers = resources.getStringArray(R.array.search_engines)
val providersValues = resources.getStringArray(R.array.search_engines_values).toMutableList() val providersValues = resources.getStringArray(R.array.search_engines_values).toMutableList()
val currentProvider = sharedPreferences?.getString("search_engine", "ytsearch")
for(i in providersValues.indices){ for(i in providersValues.indices){
val provider = providers[i] val provider = providers[i]
@ -291,8 +291,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
val tmp = layoutinflater!!.inflate(R.layout.filter_chip, providersChipGroup, false) as Chip val tmp = layoutinflater!!.inflate(R.layout.filter_chip, providersChipGroup, false) as Chip
tmp.text = provider tmp.text = provider
tmp.id = i tmp.id = i
tmp.tag = providersValues[i]
if (currentProvider == providerValue) tmp.isChecked = true
tmp.setOnClickListener { tmp.setOnClickListener {
val editor = sharedPreferences?.edit() val editor = sharedPreferences?.edit()
@ -308,6 +307,15 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
infoUtil = InfoUtil(requireContext()) infoUtil = InfoUtil(requireContext())
searchView!!.addTransitionListener { _, _, newState -> searchView!!.addTransitionListener { _, _, newState ->
if (newState == SearchView.TransitionState.SHOWN) { if (newState == SearchView.TransitionState.SHOWN) {
val currentProvider = sharedPreferences?.getString("search_engine", "ytsearch")
providersChipGroup.children.forEach {
val tmp = providersChipGroup.findViewById<Chip>(it.id)
if (tmp.tag == currentProvider) {
tmp.isChecked = true
return@forEach
}
}
try{ try{
val clipboard = val clipboard =
requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
@ -596,7 +604,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
} }
private fun showSingleDownloadSheet(resultItem : ResultItem, type: DownloadViewModel.Type, quickDownload: Boolean){ private fun showSingleDownloadSheet(resultItem : ResultItem, type: DownloadViewModel.Type, quickDownload: Boolean){
val bottomSheet = DownloadBottomSheetDialog(resultItem, type, null, quickDownload) val bottomSheet = DownloadBottomSheetDialog(resultItem, downloadViewModel.getDownloadType(type, resultItem.url), null, quickDownload)
bottomSheet.show(parentFragmentManager, "downloadSingleSheet") bottomSheet.show(parentFragmentManager, "downloadSingleSheet")
} }

View file

@ -127,8 +127,10 @@ class AddExtraCommandsDialog(private val item: DownloadItem?, private val callba
Toast.makeText(context, getString(R.string.add_template_first), Toast.LENGTH_SHORT).show() Toast.makeText(context, getString(R.string.add_template_first), Toast.LENGTH_SHORT).show()
}else{ }else{
lifecycleScope.launch { lifecycleScope.launch {
UiUtil.showCommandTemplates(requireActivity(), commandTemplateViewModel){ template -> UiUtil.showCommandTemplates(requireActivity(), commandTemplateViewModel){ templates ->
text.text.insert(text.selectionStart, "${template.content} ") templates.forEach {
text.text.insert(text.selectionStart, "${it.content} ")
}
text.postDelayed({ text.postDelayed({
text.requestFocus() text.requestFocus()
imm.showSoftInput(text, 0) imm.showSoftInput(text, 0)

View file

@ -132,16 +132,19 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
commandTemplateCard.setOnClickListener { commandTemplateCard.setOnClickListener {
lifecycleScope.launch { lifecycleScope.launch {
UiUtil.showCommandTemplates(requireActivity(), commandTemplateViewModel){ UiUtil.showCommandTemplates(requireActivity(), commandTemplateViewModel){
chosenCommandView.editText!!.setText(it.content) chosenCommandView.editText!!.text.clear()
populateCommandCard(commandTemplateCard, it) it.forEach { c ->
chosenCommandView.editText!!.text.insert(chosenCommandView.editText!!.selectionStart, "${c.content} ")
}
populateCommandCard(commandTemplateCard, it.first())
downloadItem.format = Format( downloadItem.format = Format(
it.title, it.first().title,
"", "",
"", "",
"", "",
"", "",
0, 0,
it.content it.map { c -> c.content }.joinToString { " " }
) )
} }
} }

View file

@ -12,6 +12,7 @@ import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.text.format.DateFormat import android.text.format.DateFormat
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
@ -328,7 +329,16 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
if (items.first().type == DownloadViewModel.Type.command){ if (items.first().type == DownloadViewModel.Type.command){
lifecycleScope.launch { lifecycleScope.launch {
UiUtil.showCommandTemplates(requireActivity(), commandTemplateViewModel) { UiUtil.showCommandTemplates(requireActivity(), commandTemplateViewModel) {
val format = downloadViewModel.generateCommandFormat(it) val format = Format(
it.first().title,
"",
"",
"",
"",
0,
it.joinToString(" ") { c -> c.content }
)
items.forEach { f -> items.forEach { f ->
f.format = format f.format = format
} }

View file

@ -202,9 +202,7 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
} }
adapter.clearCheckedItems() adapter.clearCheckedItems()
for (id in selectedObjects){ downloadViewModel.deleteAllWithID(selectedObjects)
downloadViewModel.deleteDownload(id)
}
actionMode?.finish() actionMode?.finish()
} }
} }

View file

@ -208,9 +208,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
adapter.checkedItems.toList() adapter.checkedItems.toList()
} }
adapter.clearCheckedItems() adapter.clearCheckedItems()
for (id in selectedObjects){ downloadViewModel.deleteAllWithID(selectedObjects)
downloadViewModel.deleteDownload(id)
}
actionMode?.finish() actionMode?.finish()
} }
} }

View file

@ -260,8 +260,8 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
YoutubeDL.getInstance().destroyProcessById(id.toInt().toString()) YoutubeDL.getInstance().destroyProcessById(id.toInt().toString())
WorkManager.getInstance(requireContext()).cancelAllWorkByTag(id.toInt().toString()) WorkManager.getInstance(requireContext()).cancelAllWorkByTag(id.toInt().toString())
notificationUtil.cancelDownloadNotification(id.toInt()) notificationUtil.cancelDownloadNotification(id.toInt())
downloadViewModel.deleteDownload(id)
} }
downloadViewModel.deleteAllWithID(selectedObjects)
actionMode?.finish() actionMode?.finish()
} }

View file

@ -222,9 +222,7 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
adapter.checkedItems.toList() adapter.checkedItems.toList()
} }
adapter.clearCheckedItems() adapter.clearCheckedItems()
withContext(Dispatchers.IO) { downloadViewModel.deleteAllWithID(selectedObjects)
downloadViewModel.reQueueDownloadItems(selectedObjects)
}
actionMode?.finish() actionMode?.finish()
} }

View file

@ -1,5 +1,6 @@
package com.deniscerri.ytdlnis.ui.more package com.deniscerri.ytdlnis.ui.more
import android.app.ActionBar.LayoutParams
import android.app.Activity import android.app.Activity
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
@ -8,8 +9,8 @@ import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.FileObserver
import android.os.PersistableBundle import android.os.PersistableBundle
import android.util.DisplayMetrics
import android.util.Log import android.util.Log
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
@ -22,7 +23,6 @@ import android.widget.ScrollView
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.forEach
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
@ -62,8 +62,8 @@ class TerminalActivity : BaseActivity() {
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private var downloadID by Delegates.notNull<Int>() private var downloadID by Delegates.notNull<Int>()
private lateinit var downloadFile : File private lateinit var downloadFile : File
private lateinit var observer: FileObserver
private lateinit var imm : InputMethodManager private lateinit var imm : InputMethodManager
private lateinit var metrics: DisplayMetrics
var context: Context? = null var context: Context? = null
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
@ -82,6 +82,8 @@ class TerminalActivity : BaseActivity() {
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java] commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
metrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(metrics)
bottomAppBar = findViewById(R.id.bottomAppBar) bottomAppBar = findViewById(R.id.bottomAppBar)
@ -113,8 +115,10 @@ class TerminalActivity : BaseActivity() {
Toast.makeText(context, getString(R.string.add_template_first), Toast.LENGTH_SHORT).show() Toast.makeText(context, getString(R.string.add_template_first), Toast.LENGTH_SHORT).show()
}else{ }else{
lifecycleScope.launch { lifecycleScope.launch {
UiUtil.showCommandTemplates(this@TerminalActivity, commandTemplateViewModel){ template -> UiUtil.showCommandTemplates(this@TerminalActivity, commandTemplateViewModel){ templates ->
input!!.text.insert(input!!.selectionStart, template.content + " ") templates.forEach {c ->
input!!.text.insert(input!!.selectionStart, c.content + " ")
}
input!!.postDelayed({ input!!.postDelayed({
input!!.requestFocus() input!!.requestFocus()
imm.showSoftInput(input!!, 0) imm.showSoftInput(input!!, 0)
@ -146,7 +150,7 @@ class TerminalActivity : BaseActivity() {
output = findViewById(R.id.custom_command_output) output = findViewById(R.id.custom_command_output)
output!!.setTextIsSelectable(true) output!!.setTextIsSelectable(true)
output!!.setHorizontallyScrolling(true) output!!.layoutParams!!.width = LayoutParams.WRAP_CONTENT
input = findViewById(R.id.command_edittext) input = findViewById(R.id.command_edittext)
input!!.requestFocus() input!!.requestFocus()
fab = findViewById(R.id.command_fab) fab = findViewById(R.id.command_fab)
@ -168,38 +172,6 @@ class TerminalActivity : BaseActivity() {
notificationUtil = NotificationUtil(this) notificationUtil = NotificationUtil(this)
handleIntent(intent) handleIntent(intent)
if(Build.VERSION.SDK_INT < 29){
observer = object : FileObserver(downloadFile.absolutePath, MODIFY) {
override fun onEvent(event: Int, p: String?) {
runOnUiThread{
try {
val newText = downloadFile.readText()
if (newText.isBlank()) return@runOnUiThread
output!!.append(newText)
output!!.scrollTo(0, output!!.height)
scrollView!!.fullScroll(View.FOCUS_DOWN)
}catch (ignored: Exception) {}
}
}
}
observer.startWatching()
}else{
observer = object : FileObserver(downloadFile, MODIFY) {
override fun onEvent(event: Int, p: String?) {
runOnUiThread{
try {
val newText = downloadFile.readText()
if (newText.isBlank()) return@runOnUiThread
output!!.append(newText)
output!!.scrollTo(0, output!!.height)
scrollView!!.fullScroll(View.FOCUS_DOWN)
}catch (ignored: Exception) {}
}
}
}
observer.startWatching()
}
WorkManager.getInstance(this) WorkManager.getInstance(this)
.getWorkInfosForUniqueWorkLiveData(downloadID.toString()) .getWorkInfosForUniqueWorkLiveData(downloadID.toString())
.observe(this){ list -> .observe(this){ list ->
@ -269,7 +241,6 @@ class TerminalActivity : BaseActivity() {
scrollView.id = R.id.horizontalscroll_output scrollView.id = R.id.horizontalscroll_output
parent.addView(scrollView, 0) parent.addView(scrollView, 0)
} }
output?.setHorizontallyScrolling(output?.canScrollHorizontally(1) != true)
} }
R.id.export_clipboard -> { R.id.export_clipboard -> {
lifecycleScope.launch(Dispatchers.IO){ lifecycleScope.launch(Dispatchers.IO){
@ -317,12 +288,10 @@ class TerminalActivity : BaseActivity() {
private fun hideCancelFab() { private fun hideCancelFab() {
fab!!.text = getString(R.string.run_command) fab!!.text = getString(R.string.run_command)
fab!!.setIconResource(R.drawable.ic_baseline_keyboard_arrow_right_24) fab!!.setIconResource(R.drawable.ic_baseline_keyboard_arrow_right_24)
topAppBar!!.menu.forEach { it.isEnabled = true }
} }
private fun showCancelFab() { private fun showCancelFab() {
fab!!.text = getString(R.string.cancel_download) fab!!.text = getString(R.string.cancel_download)
fab!!.setIconResource(R.drawable.ic_cancel) fab!!.setIconResource(R.drawable.ic_cancel)
topAppBar!!.menu.forEach { it.isEnabled = false }
} }
private var commandPathResultLauncher = registerForActivityResult( private var commandPathResultLauncher = registerForActivityResult(

View file

@ -29,6 +29,7 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
override val title: Int = R.string.general override val title: Int = R.string.general
private var language: ListPreference? = null private var language: ListPreference? = null
private var language13: Preference? = null
private var theme: ListPreference? = null private var theme: ListPreference? = null
private var accent: ListPreference? = null private var accent: ListPreference? = null
private var highContrast: SwitchPreferenceCompat? = null private var highContrast: SwitchPreferenceCompat? = null
@ -50,6 +51,7 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
} }
language = findPreference("app_language") language = findPreference("app_language")
language13 = findPreference("app_language_13")
theme = findPreference("ytdlnis_theme") theme = findPreference("ytdlnis_theme")
accent = findPreference("theme_accent") accent = findPreference("theme_accent")
highContrast = findPreference("high_contrast") highContrast = findPreference("high_contrast")
@ -63,18 +65,32 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
entries.add(Locale(it).getDisplayName(Locale(it))) entries.add(Locale(it).getDisplayName(Locale(it)))
} }
language!!.entries = entries.toTypedArray() language!!.entries = entries.toTypedArray()
}else{
language!!.isVisible = false
}
if(language!!.value == null) language!!.value = Locale.getDefault().language if(language!!.value == null) language!!.value = Locale.getDefault().language
language!!.summary = Locale(language!!.value).getDisplayLanguage(Locale(language!!.value)) language!!.summary = Locale(language!!.value).getDisplayLanguage(Locale(language!!.value))
language!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any -> language!!.onPreferenceChangeListener =
language!!.summary = Locale(newValue.toString()).getDisplayLanguage(Locale(newValue.toString())) Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue.toString())) language!!.summary = Locale(newValue.toString()).getDisplayLanguage(Locale(newValue.toString()))
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue.toString()))
true
}
language13?.isVisible = false
}else{
language?.isVisible = false
language13?.isVisible = true
language13?.summary = Locale.getDefault().displayLanguage
language13!!.setOnPreferenceClickListener {
val intent = Intent(Settings.ACTION_APP_LOCALE_SETTINGS)
intent.data = Uri.fromParts("package", requireActivity().packageName, null)
requireActivity().startActivity(intent)
requireActivity().finishAffinity()
true true
} }
}
theme!!.summary = theme!!.entry theme!!.summary = theme!!.entry
theme!!.onPreferenceChangeListener = theme!!.onPreferenceChangeListener =

View file

@ -245,7 +245,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
} }
val arr = JsonArray() val arr = JsonArray()
historyItems.forEach { historyItems.forEach {
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject) arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
} }
return arr return arr
} }
@ -259,7 +259,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
} }
val arr = JsonArray() val arr = JsonArray()
items.forEach { items.forEach {
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject) arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
} }
return arr return arr
} }
@ -273,7 +273,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
} }
val arr = JsonArray() val arr = JsonArray()
items.forEach { items.forEach {
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject) arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
} }
return arr return arr
} }
@ -287,7 +287,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
} }
val arr = JsonArray() val arr = JsonArray()
items.forEach { items.forEach {
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject) arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
} }
return arr return arr
} }
@ -301,7 +301,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
} }
val arr = JsonArray() val arr = JsonArray()
items.forEach { items.forEach {
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject) arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
} }
return arr return arr
} }
@ -315,7 +315,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
} }
val arr = JsonArray() val arr = JsonArray()
items.forEach { items.forEach {
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject) arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
} }
return arr return arr
} }
@ -330,7 +330,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
val arr = JsonArray() val arr = JsonArray()
items.forEach { items.forEach {
it.useAsExtraCommand = false it.useAsExtraCommand = false
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject) arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
} }
return arr return arr
} }
@ -344,7 +344,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
} }
val arr = JsonArray() val arr = JsonArray()
items.forEach { items.forEach {
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject) arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
} }
return arr return arr
} }
@ -358,7 +358,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
} }
val arr = JsonArray() val arr = JsonArray()
historyItems.forEach { historyItems.forEach {
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject) arr.add(JsonParser.parseString(Gson().toJson(it)).asJsonObject)
} }
return arr return arr
} }
@ -380,7 +380,6 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
lifecycleScope.launch { lifecycleScope.launch {
val preferences = val preferences =
PreferenceManager.getDefaultSharedPreferences(requireContext()) PreferenceManager.getDefaultSharedPreferences(requireContext())
val editor = preferences.edit()
runCatching { runCatching {
val ip = requireContext().contentResolver.openInputStream(result.data!!.data!!) val ip = requireContext().contentResolver.openInputStream(result.data!!.data!!)

View file

@ -290,7 +290,7 @@ object FileUtil {
} }
fun convertFileSize(s: Long): String{ fun convertFileSize(s: Long): String{
if (s <= 0) return "?" if (s <= 1) return "?"
val units = arrayOf("B", "kB", "MB", "GB", "TB") val units = arrayOf("B", "kB", "MB", "GB", "TB")
val digitGroups = (log10(s.toDouble()) / log10(1024.0)).toInt() val digitGroups = (log10(s.toDouble()) / log10(1024.0)).toInt()
val symbols = DecimalFormatSymbols(Locale.US) val symbols = DecimalFormatSymbols(Locale.US)

View file

@ -5,6 +5,7 @@ import android.content.SharedPreferences
import android.os.Looper import android.os.Looper
import android.text.Html import android.text.Html
import android.util.Log import android.util.Log
import android.util.Patterns
import android.widget.Toast import android.widget.Toast
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
@ -547,7 +548,6 @@ class InfoUtil(private val context: Context) {
fun getFromYTDL(query: String): ArrayList<ResultItem?> { fun getFromYTDL(query: String): ArrayList<ResultItem?> {
items = ArrayList() items = ArrayList()
val webpage_urls = mutableListOf<String>()
val searchEngine = sharedPreferences.getString("search_engine", "ytsearch") val searchEngine = sharedPreferences.getString("search_engine", "ytsearch")
try { try {
val request : YoutubeDLRequest val request : YoutubeDLRequest
@ -656,7 +656,11 @@ class InfoUtil(private val context: Context) {
val url = if (jsonObject.has("url")){ val url = if (jsonObject.has("url")){
jsonObject.getString("url") jsonObject.getString("url")
}else{ }else{
jsonObject.getString("webpage_url") if (Patterns.WEB_URL.matcher(query).matches()){
query
}else{
jsonObject.getString("webpage_url")
}
} }
items.add(ResultItem(0, items.add(ResultItem(0,
@ -672,10 +676,6 @@ class InfoUtil(private val context: Context) {
chapters chapters
) )
) )
webpage_urls.add(jsonObject.getString("webpage_url"))
}
if (items.size == 1){
items[0]?.url = webpage_urls.first()
} }
} catch (e: Exception) { } catch (e: Exception) {
Looper.prepare().run { Looper.prepare().run {
@ -970,20 +970,6 @@ class InfoUtil(private val context: Context) {
downDir.mkdirs() downDir.mkdirs()
} }
if (downloadItem.playlistTitle.isNotBlank()){
request.addOption("--parse-metadata","${downloadItem.playlistTitle.split("[")[0]}:%(playlist)s")
runCatching {
request.addOption("--parse-metadata",
downloadItem.playlistTitle
.substring(
downloadItem.playlistTitle.indexOf("[") + 1,
downloadItem.playlistTitle.indexOf("]"),
) + ":%(playlist_index)s"
)
}
}
val aria2 = sharedPreferences.getBoolean("aria2", false) val aria2 = sharedPreferences.getBoolean("aria2", false)
if (aria2) { if (aria2) {
request.addOption("--downloader", "libaria2c.so") request.addOption("--downloader", "libaria2c.so")
@ -1001,6 +987,7 @@ class InfoUtil(private val context: Context) {
val limitRate = sharedPreferences.getString("limit_rate", "") val limitRate = sharedPreferences.getString("limit_rate", "")
if (limitRate != "") request.addOption("-r", limitRate!!) if (limitRate != "") request.addOption("-r", limitRate!!)
request.addOption("--trim-filenames", 120)
if(downloadItem.type != DownloadViewModel.Type.command){ if(downloadItem.type != DownloadViewModel.Type.command){
if (downloadItem.SaveThumb) { if (downloadItem.SaveThumb) {
request.addOption("--write-thumbnail") request.addOption("--write-thumbnail")
@ -1034,12 +1021,12 @@ class InfoUtil(private val context: Context) {
} }
if(downloadItem.title.isNotBlank()){ if(downloadItem.title.isNotBlank()){
request.addOption("--parse-metadata", downloadItem.title.take(200) + ":%(title)s") request.addCommands(listOf("--replace-in-metadata", "title", ".+", downloadItem.title))
} }
if (downloadItem.author.isNotBlank()){ if (downloadItem.author.isNotBlank()){
request.addOption("--parse-metadata", downloadItem.author.take(25) + ":%(artist)s") request.addCommands(listOf("--replace-in-metadata", "uploader", ".+", downloadItem.author))
request.addCommands(listOf("--replace-in-metadata","uploader"," - Topic$",""))
} }
request.addCommands(listOf("--replace-in-metadata","artist"," - Topic$",""))
if (downloadItem.downloadSections.isNotBlank()){ if (downloadItem.downloadSections.isNotBlank()){
downloadItem.downloadSections.split(";").forEach { downloadItem.downloadSections.split(";").forEach {
@ -1077,6 +1064,21 @@ class InfoUtil(private val context: Context) {
if (sharedPreferences.getBoolean("write_description", false)){ if (sharedPreferences.getBoolean("write_description", false)){
request.addOption("--write-description") request.addOption("--write-description")
} }
if (downloadItem.playlistTitle.isNotBlank()){
request.addOption("--parse-metadata","${downloadItem.playlistTitle.split("[")[0]}:%(playlist)s")
runCatching {
request.addOption("--parse-metadata",
downloadItem.playlistTitle
.substring(
downloadItem.playlistTitle.indexOf("[") + 1,
downloadItem.playlistTitle.indexOf("]"),
) + ":%(playlist_index)s"
)
}
}
} }
if (sharedPreferences.getBoolean("restrict_filenames", true)) { if (sharedPreferences.getBoolean("restrict_filenames", true)) {
@ -1144,7 +1146,7 @@ class InfoUtil(private val context: Context) {
if (! request.hasOption("--convert-thumbnails")) request.addOption("--convert-thumbnails", "jpg") if (! request.hasOption("--convert-thumbnails")) request.addOption("--convert-thumbnails", "jpg")
if (sharedPreferences.getBoolean("crop_thumbnail", true)){ if (sharedPreferences.getBoolean("crop_thumbnail", true)){
try { try {
val config = File(context.cacheDir.absolutePath + "/config" + downloadItem.title + "##" + downloadItem.format.format_id + ".txt") val config = File(context.cacheDir.absolutePath + "/config" + downloadItem.id + "##ffmpegCrop.txt")
val configData = "--ppa \"ffmpeg: -c:v mjpeg -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\"" val configData = "--ppa \"ffmpeg: -c:v mjpeg -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\""
config.writeText(configData) config.writeText(configData)
request.addOption("--ppa", "ThumbnailsConvertor:-qmin 1 -q:v 1") request.addOption("--ppa", "ThumbnailsConvertor:-qmin 1 -q:v 1")
@ -1156,7 +1158,7 @@ class InfoUtil(private val context: Context) {
} }
if (downloadItem.customFileNameTemplate.isNotBlank()){ if (downloadItem.customFileNameTemplate.isNotBlank()){
request.addOption("-o", "${downloadItem.customFileNameTemplate}.%(ext)s") request.addOption("-o", "${downloadItem.customFileNameTemplate.removeSuffix(".%(ext)s")}.%(ext)s")
} }
} }
@ -1318,7 +1320,7 @@ class InfoUtil(private val context: Context) {
request.addOption("-o", "chapter:%(section_title)s.%(ext)s") request.addOption("-o", "chapter:%(section_title)s.%(ext)s")
}else{ }else{
if (downloadItem.customFileNameTemplate.isNotBlank()){ if (downloadItem.customFileNameTemplate.isNotBlank()){
request.addOption("-o", "${downloadItem.customFileNameTemplate}.%(ext)s") request.addOption("-o", "${downloadItem.customFileNameTemplate.removeSuffix(".%(ext)s")}.%(ext)s")
} }
} }
@ -1351,10 +1353,16 @@ class InfoUtil(private val context: Context) {
var final = java.lang.String.join(" ", arr).replace("\"\"", "\" \"") var final = java.lang.String.join(" ", arr).replace("\"\"", "\" \"")
val ppas = "--config(-locations)? \"(.*?)\"".toRegex().findAll(final) val ppas = "--config(-locations)? \"(.*?)\"".toRegex().findAll(final)
ppas.forEach { ppas.forEach {res ->
val path = "\"(.*?)\"".toRegex().find(it.value)?.value?.replace("\"", "") val path = "\"(.*?)\"".toRegex().find(res.value)?.value?.replace("\"", "")
final = final.replace(it.value, "") val newVal = runCatching {
final += " ${File(path ?: "").readText()}" File(path ?: "").readText()
}.onFailure {
res.value
}.getOrDefault("")
final = final.replace(res.value, newVal)
} }
return final return final

View file

@ -31,6 +31,7 @@ import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -543,14 +544,14 @@ object UiUtil {
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet.setContentView(R.layout.format_details_sheet) bottomSheet.setContentView(R.layout.format_details_sheet)
val formatIdParent = bottomSheet.findViewById<LinearLayout>(R.id.format_id_parent) val formatIdParent = bottomSheet.findViewById<ConstraintLayout>(R.id.format_id_parent)
val formatURLParent = bottomSheet.findViewById<LinearLayout>(R.id.format_url_parent) val formatURLParent = bottomSheet.findViewById<ConstraintLayout>(R.id.format_url_parent)
val containerParent = bottomSheet.findViewById<LinearLayout>(R.id.container_parent) val containerParent = bottomSheet.findViewById<ConstraintLayout>(R.id.container_parent)
val codecParent = bottomSheet.findViewById<LinearLayout>(R.id.codec_parent) val codecParent = bottomSheet.findViewById<ConstraintLayout>(R.id.codec_parent)
val filesizeParent = bottomSheet.findViewById<LinearLayout>(R.id.filesize_parent) val filesizeParent = bottomSheet.findViewById<ConstraintLayout>(R.id.filesize_parent)
val formatnoteParent = bottomSheet.findViewById<LinearLayout>(R.id.format_note_parent) val formatnoteParent = bottomSheet.findViewById<ConstraintLayout>(R.id.format_note_parent)
val fpsParent = bottomSheet.findViewById<LinearLayout>(R.id.fps_parent) val fpsParent = bottomSheet.findViewById<ConstraintLayout>(R.id.fps_parent)
val asrParent = bottomSheet.findViewById<LinearLayout>(R.id.asr_parent) val asrParent = bottomSheet.findViewById<ConstraintLayout>(R.id.asr_parent)
if (format.format_id.isBlank()) formatIdParent?.visibility = View.GONE if (format.format_id.isBlank()) formatIdParent?.visibility = View.GONE
else { else {
@ -690,7 +691,7 @@ object UiUtil {
} }
suspend fun showCommandTemplates(activity: Activity, commandTemplateViewModel: CommandTemplateViewModel, itemSelected: (itemSelected: CommandTemplate) -> Unit) { suspend fun showCommandTemplates(activity: Activity, commandTemplateViewModel: CommandTemplateViewModel, itemSelected: (itemSelected: List<CommandTemplate>) -> Unit) {
val bottomSheet = BottomSheetDialog(activity) val bottomSheet = BottomSheetDialog(activity)
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet.setContentView(R.layout.command_template_list) bottomSheet.setContentView(R.layout.command_template_list)
@ -701,17 +702,33 @@ object UiUtil {
} }
linearLayout!!.removeAllViews() linearLayout!!.removeAllViews()
val selectedItems = mutableListOf<CommandTemplate>()
val ok = bottomSheet.findViewById<MaterialButton>(R.id.command_ok)
ok?.isEnabled = list.size == 1
list.forEach {template -> list.forEach {template ->
val item = activity.layoutInflater.inflate(R.layout.command_template_item, linearLayout, false) as MaterialCardView val item = activity.layoutInflater.inflate(R.layout.command_template_item, linearLayout, false) as MaterialCardView
item.findViewById<TextView>(R.id.title).text = template.title item.findViewById<TextView>(R.id.title).text = template.title
item.findViewById<TextView>(R.id.content).text = template.content item.findViewById<TextView>(R.id.content).text = template.content
item.setOnClickListener { item.setOnClickListener {
itemSelected(template) if (selectedItems.contains(template)){
bottomSheet.cancel() selectedItems.remove(template)
(it as MaterialCardView).isChecked = false
}else{
selectedItems.add(template)
(it as MaterialCardView).isChecked = true
}
ok?.isEnabled = selectedItems.isNotEmpty()
} }
linearLayout.addView(item) linearLayout.addView(item)
} }
ok?.setOnClickListener {
itemSelected(selectedItems.ifEmpty { listOf(list.first()) })
bottomSheet.cancel()
}
bottomSheet.show() bottomSheet.show()
bottomSheet.window!!.setLayout( bottomSheet.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@ -913,9 +930,6 @@ object UiUtil {
) { _: DialogInterface?, _: Int -> } ) { _: DialogInterface?, _: Int -> }
val dialog = builder.create() val dialog = builder.create()
editText.doOnTextChanged { _, _, _, _ ->
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty()
}
dialog.show() dialog.show()
val imm = context.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager val imm = context.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
editText!!.postDelayed({ editText!!.postDelayed({
@ -1016,9 +1030,6 @@ object UiUtil {
) { _: DialogInterface?, _: Int -> } ) { _: DialogInterface?, _: Int -> }
val dialog = builder.create() val dialog = builder.create()
editText.doOnTextChanged { _, _, _, _ ->
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty()
}
dialog.show() dialog.show()
val imm = context.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager val imm = context.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
editText!!.postDelayed({ editText!!.postDelayed({

View file

@ -116,11 +116,7 @@ class DownloadWorker(
"Title: ${downloadItem.title}\n" + "Title: ${downloadItem.title}\n" +
"URL: ${downloadItem.url}\n" + "URL: ${downloadItem.url}\n" +
"Type: ${downloadItem.type}\n" + "Type: ${downloadItem.type}\n" +
if (downloadItem.type != DownloadViewModel.Type.command){ "Command:\n ${infoUtil.parseYTDLRequestString(request)}\n\n",
"Format: ${downloadItem.format}\n\n"
}
else { "" } +
"Command:\n ${infoUtil.parseYTDLRequestString(request)} ${downloadItem.extraCommands}\n\n",
downloadItem.format, downloadItem.format,
downloadItem.type, downloadItem.type,
System.currentTimeMillis(), System.currentTimeMillis(),

View file

@ -100,7 +100,13 @@ class TerminalDownloadWorker(
} }
YoutubeDL.getInstance().execute(request, itemId.toString()){ progress, _, line -> YoutubeDL.getInstance().execute(request, itemId.toString()){ progress, _, line ->
setProgressAsync(workDataOf("progress" to progress.toInt(), "output" to line.chunked(5000).first().toString(), "id" to itemId, "log" to logDownloads)) runBlocking {
line.chunked(10000).forEach {
setProgress(workDataOf("progress" to progress.toInt(), "output" to it, "id" to itemId, "log" to logDownloads))
Thread.sleep(100)
}
}
val title: String = command.take(65) val title: String = command.take(65)
notificationUtil.updateDownloadNotification( notificationUtil.updateDownloadNotification(
itemId, itemId,
@ -128,32 +134,46 @@ class TerminalDownloadWorker(
} }
} }
outputFile.appendText("${it.out}\n") return runBlocking {
if (logDownloads){ it.out.chunked(10000).forEach {
CoroutineScope(Dispatchers.IO).launch { setProgress(workDataOf("progress" to 100, "output" to it, "id" to itemId, "log" to logDownloads))
logRepo.update(it.out, logItem.id) Thread.sleep(100)
} }
}
notificationUtil.cancelDownloadNotification(itemId)
if (logDownloads){
CoroutineScope(Dispatchers.IO).launch {
logRepo.update(it.out, logItem.id)
}
}
notificationUtil.cancelDownloadNotification(itemId)
return@runBlocking Result.success()
}
}.onFailure { }.onFailure {
if (it is YoutubeDL.CanceledException) { if (it is YoutubeDL.CanceledException) {
return Result.failure() return Result.failure()
} }
outputFile.appendText("\n${it.message}\n")
if (logDownloads){ return runBlocking {
CoroutineScope(Dispatchers.IO).launch { it.message?.chunked(10000)?.forEach {
if (it.message != null){ setProgress(workDataOf("progress" to 100, "output" to it, "id" to itemId, "log" to logDownloads))
logRepo.update(it.message!!, logItem.id) Thread.sleep(100)
}
if (logDownloads){
CoroutineScope(Dispatchers.IO).launch {
if (it.message != null){
logRepo.update(it.message!!, logItem.id)
}
} }
} }
File(FileUtil.getDefaultCommandPath() + "/" + itemId).deleteRecursively()
Log.e(TAG, context.getString(R.string.failed_download), it)
notificationUtil.cancelDownloadNotification(itemId)
return@runBlocking Result.failure()
} }
File(FileUtil.getDefaultCommandPath() + "/" + itemId).deleteRecursively()
Log.e(TAG, context.getString(R.string.failed_download), it)
notificationUtil.cancelDownloadNotification(itemId)
return Result.failure()
} }
return Result.success() return Result.success()

View file

@ -24,8 +24,12 @@
android:id="@+id/bottom_sheet_title" android:id="@+id/bottom_sheet_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="false"
android:text="@string/command_templates" android:text="@string/command_templates"
android:textSize="25sp" android:textSize="25sp"
android:maxLines="2"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintEnd_toStartOf="@+id/command_ok"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@ -39,6 +43,18 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bottom_sheet_title" /> app:layout_constraintTop_toBottomOf="@+id/bottom_sheet_title" />
<Button
android:id="@+id/command_ok"
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="all"
android:text="@string/ok"
app:icon="@drawable/ic_check"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
@ -15,37 +16,41 @@
android:layout_margin="20dp" android:layout_margin="20dp"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/format_id_parent" android:id="@+id/format_id_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center"
android:paddingHorizontal="10dp" android:paddingHorizontal="10dp"
android:weightSum="100"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal" > android:orientation="horizontal" >
<TextView <TextView
android:layout_width="0dp" android:id="@+id/textView"
android:layout_weight="30" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:maxLines="2" android:maxLines="2"
android:text="@string/format_id" android:text="@string/format_id"
android:textSize="14sp" android:textSize="14sp"
android:layout_margin="10dp" app:layout_constraintBottom_toBottomOf="parent"
android:layout_height="wrap_content"/> app:layout_constraintEnd_toStartOf="@+id/format_id_value"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/format_id_value" android:id="@+id/format_id_value"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_weight="70" android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:gravity="end" android:gravity="end"
android:layout_height="wrap_content"/> android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/format_url_parent" android:id="@+id/format_url_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -56,29 +61,35 @@
android:orientation="horizontal" > android:orientation="horizontal" >
<TextView <TextView
android:layout_width="0dp" android:id="@+id/textView2"
android:layout_weight="30" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:maxLines="2" android:maxLines="2"
android:text="@string/url" android:text="@string/url"
app:layout_constraintHorizontal_bias="0.0"
android:textSize="14sp" android:textSize="14sp"
android:layout_margin="10dp" app:layout_constraintBottom_toBottomOf="parent"
android:layout_height="wrap_content"/> app:layout_constraintEnd_toStartOf="@+id/format_url_value"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/format_url_value" android:id="@+id/format_url_value"
android:layout_width="0dp" android:layout_width="wrap_content"
android:maxLines="3" android:layout_height="wrap_content"
android:ellipsize="end"
android:layout_weight="70"
android:textSize="14sp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:ellipsize="end"
android:gravity="end" android:gravity="end"
android:layout_height="wrap_content"/> android:maxLines="3"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container_parent" android:id="@+id/container_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -89,26 +100,32 @@
android:orientation="horizontal" > android:orientation="horizontal" >
<TextView <TextView
android:layout_width="0dp" android:id="@+id/textView3"
android:layout_weight="30" android:layout_width="wrap_content"
android:maxLines="2" app:layout_constraintHorizontal_bias="0.0"
android:textSize="14sp" android:layout_height="wrap_content"
android:text="@string/container"
android:layout_margin="10dp" android:layout_margin="10dp"
android:layout_height="wrap_content"/> android:maxLines="2"
android:text="@string/container"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/container_value"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/container_value" android:id="@+id/container_value"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_weight="70" android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:gravity="end" android:gravity="end"
android:layout_height="wrap_content"/> android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/codec_parent" android:id="@+id/codec_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -119,25 +136,31 @@
android:orientation="horizontal" > android:orientation="horizontal" >
<TextView <TextView
android:layout_width="0dp" android:id="@+id/textView4"
android:layout_weight="30" android:layout_width="wrap_content"
android:textSize="14sp" android:layout_height="wrap_content"
android:text="@string/codec"
android:layout_margin="10dp" android:layout_margin="10dp"
android:layout_height="wrap_content"/> android:text="@string/codec"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintEnd_toStartOf="@+id/codec_value"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/codec_value" android:id="@+id/codec_value"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_weight="70" android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:gravity="end" android:gravity="end"
android:layout_height="wrap_content"/> android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/filesize_parent" android:id="@+id/filesize_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -148,25 +171,31 @@
android:orientation="horizontal" > android:orientation="horizontal" >
<TextView <TextView
android:layout_width="0dp" android:id="@+id/textView5"
android:layout_weight="30" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/file_size" android:text="@string/file_size"
android:textSize="14sp" android:textSize="14sp"
android:layout_margin="10dp" app:layout_constraintBottom_toBottomOf="parent"
android:layout_height="wrap_content"/> app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintEnd_toStartOf="@+id/filesize_value"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/filesize_value" android:id="@+id/filesize_value"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_weight="70" android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:gravity="end" android:gravity="end"
android:layout_height="wrap_content"/> android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/format_note_parent" android:id="@+id/format_note_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -177,25 +206,30 @@
android:orientation="horizontal" > android:orientation="horizontal" >
<TextView <TextView
android:layout_width="0dp" android:layout_width="wrap_content"
android:text="@string/quality" android:layout_height="wrap_content"
android:layout_weight="30"
android:textSize="14sp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:layout_height="wrap_content"/> android:text="@string/quality"
app:layout_constraintHorizontal_bias="0.0"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/format_note_value"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/format_note_value" android:id="@+id/format_note_value"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_weight="70" android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:gravity="end" android:gravity="end"
android:layout_height="wrap_content"/> android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/fps_parent" android:id="@+id/fps_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -206,25 +240,31 @@
android:orientation="horizontal" > android:orientation="horizontal" >
<TextView <TextView
android:layout_width="0dp" android:id="@+id/textView6"
android:layout_weight="30" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="FPS" android:text="FPS"
android:textSize="14sp" android:textSize="14sp"
android:layout_margin="10dp" app:layout_constraintBottom_toBottomOf="parent"
android:layout_height="wrap_content"/> app:layout_constraintEnd_toStartOf="@+id/fps_value"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/fps_value" android:id="@+id/fps_value"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_weight="70" android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:gravity="end" android:gravity="end"
android:layout_height="wrap_content"/> android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/asr_parent" android:id="@+id/asr_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -235,23 +275,29 @@
android:orientation="horizontal" > android:orientation="horizontal" >
<TextView <TextView
android:layout_width="0dp" android:id="@+id/textView7"
android:layout_weight="30" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
app:layout_constraintHorizontal_bias="0.0"
android:text="@string/audio_samplerate" android:text="@string/audio_samplerate"
android:textSize="14sp" android:textSize="14sp"
android:layout_margin="10dp" app:layout_constraintBottom_toBottomOf="parent"
android:layout_height="wrap_content"/> app:layout_constraintEnd_toStartOf="@+id/asr_value"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/asr_value" android:id="@+id/asr_value"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_weight="70" android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_margin="10dp" android:layout_margin="10dp"
android:gravity="end" android:gravity="end"
android:layout_height="wrap_content"/> android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout> </LinearLayout>

View file

@ -26,6 +26,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/format" android:text="@string/format"
android:maxLines="2"
android:singleLine="false"
android:textSize="25sp" android:textSize="25sp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />

View file

@ -788,9 +788,8 @@
<item>@string/defaultValue</item> <item>@string/defaultValue</item>
<item tools:ignore="Typos">AV1</item> <item tools:ignore="Typos">AV1</item>
<item>VP9</item> <item>VP9</item>
<item tools:ignore="Typos">AVC</item> <item tools:ignore="Typos">AVC (H264)</item>
<item>H265</item> <item>HEVC (H265)</item>
<item>H264</item>
</string-array> </string-array>
<string-array name="video_codec_values"> <string-array name="video_codec_values">
@ -799,8 +798,5 @@
<item>vp</item> <item>vp</item>
<item>avc</item> <item>avc</item>
<item>hev</item> <item>hev</item>
<item>h264</item>
</string-array> </string-array>
<string name="auto">Auto</string>
</resources> </resources>

View file

@ -302,4 +302,5 @@
<string name="use_scheduler">Download On Schedule</string> <string name="use_scheduler">Download On Schedule</string>
<string name="update_app_beta">Beta Releases</string> <string name="update_app_beta">Beta Releases</string>
<string name="update_app_beta_summary">Enroll in the beta program. It may contain bugs.</string> <string name="update_app_beta_summary">Enroll in the beta program. It may contain bugs.</string>
<string name="auto">Auto</string>
</resources> </resources>

View file

@ -11,6 +11,12 @@
app:summary="en" app:summary="en"
app:title="@string/language" /> app:title="@string/language" />
<Preference
android:icon="@drawable/baseline_translate_24"
app:key="app_language_13"
app:summary="en"
app:title="@string/language" />
<ListPreference <ListPreference
android:entries="@array/themes" android:entries="@array/themes"

View file

@ -57,7 +57,7 @@
<EditTextPreference <EditTextPreference
app:key="piped_instance" app:key="piped_instance"
app:defaultValue="https://api.piped.projectsegfau.lt" app:defaultValue=""
android:summary="@string/piped_instance_summary" android:summary="@string/piped_instance_summary"
app:title="@string/piped_instance" /> app:title="@string/piped_instance" />