added auto preferred download type
Show collected file size in multi download card
fixed app chossing worst format and not best
fixed update util not working in release
fixed ytm playlists not loading
fixed sponsorblock not being checked in multiple download card
added playlist and playlist index metadata in download items
added -a txt path if preferred download type is command or if the txt file is shared in terminal
fixed app cancelling workmanager earlier than expected
added title and author sync between audio and video frgaments
This commit is contained in:
deniscerri 2023-09-09 12:15:07 +02:00
parent a1510cdcfa
commit 236f83d161
No known key found for this signature in database
GPG key ID: 95C43D517D830350
29 changed files with 377 additions and 155 deletions

View file

@ -56,9 +56,7 @@
android:theme="@style/Theme.BottomSheet">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>

View file

@ -18,6 +18,7 @@ import android.view.View
import android.view.WindowInsets
import android.widget.CheckBox
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat
@ -39,7 +40,9 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.ui.BaseActivity
import com.deniscerri.ytdlnis.ui.HomeFragment
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.ThemeUtil
import com.deniscerri.ytdlnis.util.UpdateUtil
import com.google.android.material.bottomnavigation.BottomNavigationView
@ -54,8 +57,10 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader
import java.io.Reader
import java.net.URI
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import kotlin.system.exitProcess
@ -332,26 +337,38 @@ class MainActivity : BaseActivity() {
}else{
intent.getParcelableExtra(Intent.EXTRA_STREAM)
}
val `is` = contentResolver.openInputStream(uri!!)
val textBuilder = StringBuilder()
val reader: Reader = BufferedReader(
InputStreamReader(
`is`, Charset.forName(
StandardCharsets.UTF_8.name()
if (preferences.getString("preferred_download_type", "video") == "command"){
val f = File(FileUtil.formatPath(uri?.path ?: ""))
if (!f.exists()){
Toast.makeText(context, "Couldn't read file", Toast.LENGTH_LONG).show()
return
}
val item = downloadViewModel.createEmptyResultItem(f.absolutePath)
val bottomSheet = DownloadBottomSheetDialog(item, DownloadViewModel.Type.command, null, true)
bottomSheet.show(supportFragmentManager, "downloadSingleSheet")
}else{
val `is` = contentResolver.openInputStream(uri!!)
val textBuilder = StringBuilder()
val reader: Reader = BufferedReader(
InputStreamReader(
`is`, Charset.forName(
StandardCharsets.UTF_8.name()
)
)
)
)
var c: Int
while (reader.read().also { c = it } != -1) {
textBuilder.append(c.toChar())
var c: Int
while (reader.read().also { c = it } != -1) {
textBuilder.append(c.toChar())
}
val bundle = Bundle()
bundle.putString("url", textBuilder.toString())
navController.popBackStack(R.id.homeFragment, true)
navController.navigate(
R.id.homeFragment,
bundle
)
}
val bundle = Bundle()
bundle.putString("url", textBuilder.toString())
navController.popBackStack(R.id.homeFragment, true)
navController.navigate(
R.id.homeFragment,
bundle
)
} catch (e: Exception) {
e.printStackTrace()
}

View file

@ -104,6 +104,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
DownloadViewModel.Type.audio -> type.setIconResource(R.drawable.ic_music)
DownloadViewModel.Type.video -> type.setIconResource(R.drawable.ic_video)
DownloadViewModel.Type.command -> type.setIconResource(R.drawable.ic_terminal)
else -> {}
}
val formatDetailsChip = card.findViewById<Chip>(R.id.format_note)

View file

@ -66,6 +66,7 @@ class DownloadLogsAdapter(onItemClickListener: OnItemClickListener, activity: Ac
DownloadViewModel.Type.audio -> downloadTypeIcon.setIconResource(R.drawable.ic_music)
DownloadViewModel.Type.video -> downloadTypeIcon.setIconResource(R.drawable.ic_video)
DownloadViewModel.Type.command -> downloadTypeIcon.setIconResource(R.drawable.ic_terminal)
else -> {}
}
val formatNote = card.findViewById<TextView>(R.id.format_note)

View file

@ -9,7 +9,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
data class DownloadItem(
@PrimaryKey(autoGenerate = true)
var id: Long,
val url: String,
var url: String,
var title: String,
var author: String,
var thumb: String,

View file

@ -1,13 +1,20 @@
package com.deniscerri.ytdlnis.database.models
import com.google.gson.annotations.SerializedName
data class GithubRelease(
@SerializedName(value = "tag_name")
var tag_name: String,
@SerializedName(value = "body")
var body: String,
@SerializedName(value = "assets")
var assets: List<GithubReleaseAsset>
)
data class GithubReleaseAsset(
@SerializedName(value = "name")
var name: String,
@SerializedName(value = "browser_download_url")
var browser_download_url: String
)

View file

@ -85,9 +85,15 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
private val dao: DownloadDao
enum class Type {
audio, video, command
auto, audio, video, command
}
private val urlsForAudioType = listOf(
"music",
"audio",
"soundcloud"
)
init {
dbManager = DBManager.getInstance(application)
dao = dbManager.downloadDao
@ -194,6 +200,13 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
return repository.getItemByID(id)
}
private fun getSuitableDownloadType(url: String): Type{
if (urlsForAudioType.any { url.contains(it) }){
return Type.audio
}
return Type.video
}
fun createDownloadItemFromResult(resultItem: ResultItem, givenType: Type) : DownloadItem {
val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
val saveSubs = sharedPreferences.getBoolean("write_subtitles", false)
@ -202,6 +215,9 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
var type = givenType
if (type == Type.auto){
type = getSuitableDownloadType(resultItem.url)
}
if(type == Type.command && commandTemplateDao.getTotalNumber() == 0) type = Type.video
val customFileNameTemplate = when(type) {
@ -320,6 +336,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
Type.audio -> sharedPreferences.getString("music_path", FileUtil.getDefaultAudioPath())!!
Type.video -> sharedPreferences.getString("video_path", FileUtil.getDefaultVideoPath())!!
Type.command -> sharedPreferences.getString("command_path", FileUtil.getDefaultCommandPath())!!
else -> ""
}
list.forEach {
@ -329,6 +346,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
Type.audio -> sharedPreferences.getString("music_path", FileUtil.getDefaultAudioPath())
Type.video -> sharedPreferences.getString("video_path", FileUtil.getDefaultVideoPath())
Type.command -> sharedPreferences.getString("command_path", FileUtil.getDefaultCommandPath())
else -> ""
}
if (it.downloadPath == currentDownloadPath) it.downloadPath = updatedDownloadPath
@ -359,6 +377,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
Type.audio -> FileUtil.getDefaultAudioPath()
Type.video -> FileUtil.getDefaultVideoPath()
Type.command -> FileUtil.getDefaultCommandPath()
else -> ""
}
val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
@ -404,7 +423,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
Type.video -> {
return cloneFormat(
try {
val theFormats = formats.filter { !it.format_note.contains("audio", true) }.sortedBy { it.format_id }.ifEmpty { defaultVideoFormats }
val theFormats = formats.filter { !it.format_note.contains("audio", true) }.sortedBy { it.format_id }.ifEmpty { defaultVideoFormats }.sortedByDescending { it.filesize }
when (videoQualityPreference) {
"worst" -> {
theFormats.first()
@ -416,7 +435,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
requirements.add { it: Format -> it.format_note.contains(videoQualityPreference.substring(0, videoQualityPreference.length - 1)) }
requirements.add { it: Format -> it.vcodec.startsWith(videoCodec!!, true) }
theFormats.reversed().maxByOrNull { f -> requirements.count{ req -> req(f)} } ?: throw Exception()
theFormats.maxByOrNull { f -> requirements.count{ req -> req(f)} } ?: throw Exception()
}
}
}catch (e: Exception){
@ -468,8 +487,9 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
fun turnResultItemsToDownloadItems(items: List<ResultItem?>) : List<DownloadItem> {
val list : MutableList<DownloadItem> = mutableListOf()
val preferredType = sharedPreferences.getString("preferred_download_type", "video")
var preferredType = sharedPreferences.getString("preferred_download_type", "video")
items.forEach {
if (preferredType == Type.auto.toString()) preferredType = getSuitableDownloadType(it!!.url).toString()
list.add(createDownloadItemFromResult(it!!, Type.valueOf(preferredType!!)))
}
return list

View file

@ -3,6 +3,7 @@ package com.deniscerri.ytdlnis.database.viewmodel
import android.app.Application
import android.content.SharedPreferences
import android.util.Log
import android.util.Patterns
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
@ -111,14 +112,14 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
}
private fun getQueryType(inputQuery: String) : String {
var type = "Search"
val p = Pattern.compile("(^(https?)://(www.)?(music.)?youtu(.be)?)|(^(https?)://(www.)?piped.video)")
val p = Pattern.compile("(^(https?)://(www.)?youtu(.be)?)|(^(https?)://(www.)?piped.video)")
val m = p.matcher(inputQuery)
if (m.find()) {
type = "YT_Video"
if (inputQuery.contains("playlist?list=") && !inputQuery.contains("music.youtu")) {
if (inputQuery.contains("playlist?list=")) {
type = "YT_Playlist"
}
} else if (inputQuery.contains("http")) {
} else if (Patterns.WEB_URL.matcher(inputQuery).matches()) {
type = "Default"
}
return type

View file

@ -159,7 +159,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
homeAdapter!!.submitList(it)
resultsList = it
if(resultViewModel.repository.itemCount.value > 1 || resultViewModel.repository.itemCount.value == -1){
if (it.size > 1 && it[0].playlistTitle.isNotEmpty() && it[0].playlistTitle != getString(R.string.trendingPlaylist) && !loadingItems){
if (it.size > 1 && it[0].playlistTitle.isNotEmpty() && !loadingItems){
downloadAllFabCoordinator!!.visibility = VISIBLE
}else{
downloadAllFabCoordinator!!.visibility = GONE
@ -187,7 +187,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
recyclerView?.setPadding(0,0,0,100)
shimmerCards!!.stopShimmer()
shimmerCards!!.visibility = GONE
if (resultsList!!.size > 1 && resultsList!![0]!!.playlistTitle.isNotEmpty() && resultsList!![0]!!.playlistTitle != getString(R.string.trendingPlaylist)){
if (resultsList!!.size > 1 && resultsList!![0]!!.playlistTitle.isNotEmpty()){
downloadAllFabCoordinator!!.visibility = VISIBLE
}else{
downloadAllFabCoordinator!!.visibility = GONE

View file

@ -11,6 +11,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
@ -64,7 +65,6 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
super.setupDialog(dialog, style)
val view = LayoutInflater.from(context).inflate(R.layout.configure_download_bottom_sheet, null)
dialog.setContentView(view)
dialog.setOnShowListener {
behavior = BottomSheetBehavior.from(view.parent as View)
val displayMetrics = DisplayMetrics()
@ -80,7 +80,7 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
overScrollMode = View.OVER_SCROLL_NEVER
}
val fragments = mutableListOf(
val fragments = mutableListOf<Fragment>(
DownloadAudioFragment(resultItem, downloadItem),
DownloadVideoFragment(resultItem, downloadItem)
)
@ -161,6 +161,9 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
viewPager2.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
tabLayout.selectTab(tabLayout.getTabAt(position))
runCatching {
updateTitleAuthorWhenSwitching()
}
}
})
@ -168,22 +171,7 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
val ok = view.findViewById<Button>(R.id.bottom_sheet_ok)
ok!!.setOnClickListener {
val item : DownloadItem = when(tabLayout.selectedTabPosition){
0 -> {
val f = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
f.downloadItem
}
1 -> {
val f = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
f.downloadItem
}
else -> {
val f = fragmentManager.findFragmentByTag("f2") as DownloadCommandFragment
f.downloadItem
}
}
Log.e("aa", item.toString())
val item = getDownloadItem()
onDownloadItemUpdateListener.onDownloadItemUpdate(resultItem.id, item)
dismiss()
}
@ -200,6 +188,47 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
}
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem{
return when(selectedTabPosition){
0 -> {
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
f.downloadItem
}
1 -> {
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
f.downloadItem
}
else -> {
val f = fragmentManager?.findFragmentByTag("f2") as DownloadCommandFragment
f.downloadItem
}
}
}
private fun updateTitleAuthorWhenSwitching(){
val prevDownloadItem = getDownloadItem(
if (viewPager2.currentItem == 1) 0 else 1
)
resultItem.title = prevDownloadItem.title
resultItem.author = prevDownloadItem.author
downloadItem.title = prevDownloadItem.title
downloadItem.author = prevDownloadItem.author
when(viewPager2.currentItem){
0 -> {
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
}
1 -> {
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
}
else -> {}
}
}
interface OnDownloadItemUpdateListener {
fun onDownloadItemUpdate(resultItemID: Long, item: DownloadItem)
@ -216,16 +245,6 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
cleanUp()
}
// private fun returnPreviousState(){
// CoroutineScope(Dispatchers.IO).launch {
// val result = resultViewModel.getItemByURL(currentItem.url)
// result.title = currentItem.title
// result.author = currentItem.author
// Log.e("TAG, ", currentItem.toString())
// resultViewModel.update(result)
// downloadViewModel.updateDownload(currentItem)
// }
// }
private fun cleanUp(){
kotlin.runCatching {

View file

@ -37,20 +37,19 @@ import kotlinx.coroutines.withContext
import java.io.File
class DownloadAudioFragment(private var resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment() {
class DownloadAudioFragment(private var resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment(), TitleAuthorSync {
private var _binding : FragmentHomeBinding? = null
private var fragmentView: View? = null
private var activity: Activity? = null
private lateinit var downloadViewModel : DownloadViewModel
private lateinit var resultViewModel : ResultViewModel
private lateinit var title : TextInputLayout
private lateinit var author : TextInputLayout
private lateinit var saveDir : TextInputLayout
private lateinit var freeSpace : TextView
private lateinit var infoUtil: InfoUtil
lateinit var downloadItem : DownloadItem
lateinit var title : TextInputLayout
lateinit var author : TextInputLayout
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -238,6 +237,13 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
}
}
override fun updateTitleAuthor(t: String, a: String){
downloadItem.title = t
downloadItem.author = a
title.editText?.setText(t)
author.editText?.setText(a)
}
private var audioPathResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->

View file

@ -8,12 +8,14 @@ import android.content.res.Configuration
import android.os.Bundle
import android.text.format.DateFormat
import android.util.DisplayMetrics
import android.util.Patterns
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
@ -23,13 +25,13 @@ import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.receiver.ShareActivity
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.bottomsheet.BottomSheetBehavior
@ -38,7 +40,6 @@ import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayout
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow
import java.text.SimpleDateFormat
import java.util.*
@ -56,7 +57,6 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
private lateinit var downloadAudioFragment: DownloadAudioFragment
private lateinit var downloadVideoFragment: DownloadVideoFragment
private lateinit var downloadCommandFragment: DownloadCommandFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
@ -91,11 +91,12 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
downloadAudioFragment = DownloadAudioFragment(resultItem, downloadItem)
downloadVideoFragment = DownloadVideoFragment(resultItem, downloadItem)
val fragments = mutableListOf(downloadAudioFragment, downloadVideoFragment)
val fragments = mutableListOf<Fragment>(downloadAudioFragment, downloadVideoFragment)
var commandTemplateNr = 0
lifecycleScope.launch{
withContext(Dispatchers.IO){
commandTemplateNr = commandTemplateDao.getTotalNumber()
if (!Patterns.WEB_URL.matcher(resultItem.url).matches()) commandTemplateNr++
if(commandTemplateNr > 0){
downloadCommandFragment = DownloadCommandFragment(resultItem, downloadItem)
fragments.add(downloadCommandFragment)
@ -113,6 +114,16 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.alpha = 0.3f
}
//check if the item is coming from a text file
val isCommandOnly = (type == Type.command && !Patterns.WEB_URL.matcher(resultItem.url).matches())
if (isCommandOnly){
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(0)?.isClickable = false
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(0)?.alpha = 0.3f
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.isClickable = false
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.alpha = 0.3f
}
val fragmentManager = parentFragmentManager
fragmentAdapter = DownloadFragmentAdapter(
@ -173,6 +184,9 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
viewPager2.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
tabLayout.selectTab(tabLayout.getTabAt(position))
runCatching {
updateTitleAuthorWhenSwitching()
}
}
})
@ -186,7 +200,7 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
UiUtil.showDatePicker(fragmentManager) {
scheduleBtn.isEnabled = false
download.isEnabled = false
val item: DownloadItem = getDownloadItem();
val item: DownloadItem = getDownloadItem()
item.downloadStartTime = it.timeInMillis
runBlocking {
downloadViewModel.queueDownloads(listOf(item))
@ -199,7 +213,7 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
download!!.setOnClickListener {
scheduleBtn.isEnabled = false
download.isEnabled = false
val item: DownloadItem = getDownloadItem();
val item: DownloadItem = getDownloadItem()
runBlocking {
downloadViewModel.queueDownloads(listOf(item))
}
@ -223,45 +237,50 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
}
val link = view.findViewById<Button>(R.id.bottom_sheet_link)
link.text = resultItem.url
link.setOnClickListener{
UiUtil.openLinkIntent(requireContext(), resultItem.url, null)
}
link.setOnLongClickListener{
UiUtil.copyLinkToClipBoard(requireContext(), resultItem.url, null)
true
}
val updateItem = view.findViewById<Button>(R.id.update_item)
if (quickDownload) {
(updateItem.parent as LinearLayout).visibility = View.VISIBLE
updateItem.setOnClickListener {
if (activity is ShareActivity) {
dismiss()
val intent = Intent(context, ShareActivity::class.java)
intent.action = Intent.ACTION_SEND
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_TEXT, resultItem.url)
intent.putExtra("quick_download", false)
startActivity(intent)
}else{
dismiss()
val bundle = Bundle()
bundle.putString("url", resultItem.url)
findNavController().popBackStack(R.id.homeFragment, true)
findNavController().navigate(
R.id.homeFragment,
bundle
)
}
if (Patterns.WEB_URL.matcher(resultItem.url).matches()){
link.text = resultItem.url
link.setOnClickListener{
UiUtil.openLinkIntent(requireContext(), resultItem.url, null)
}
link.setOnLongClickListener{
UiUtil.copyLinkToClipBoard(requireContext(), resultItem.url, null)
true
}
if (quickDownload) {
(updateItem.parent as LinearLayout).visibility = View.VISIBLE
updateItem.setOnClickListener {
if (activity is ShareActivity) {
dismiss()
val intent = Intent(context, ShareActivity::class.java)
intent.action = Intent.ACTION_SEND
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_TEXT, resultItem.url)
intent.putExtra("quick_download", false)
startActivity(intent)
}else{
dismiss()
val bundle = Bundle()
bundle.putString("url", resultItem.url)
findNavController().popBackStack(R.id.homeFragment, true)
findNavController().navigate(
R.id.homeFragment,
bundle
)
}
}
}else{
(updateItem.parent as LinearLayout).visibility = View.GONE
}
}else{
(updateItem.parent as LinearLayout).visibility = View.GONE
link.visibility = View.GONE
updateItem.visibility = View.GONE
}
}
private fun getDownloadItem() : DownloadItem{
return when(tabLayout.selectedTabPosition){
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem{
return when(selectedTabPosition){
0 -> {
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
f.downloadItem
@ -277,6 +296,29 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
}
}
private fun updateTitleAuthorWhenSwitching(){
val prevDownloadItem = getDownloadItem(
if (viewPager2.currentItem == 1) 0 else 1
)
resultItem.title = prevDownloadItem.title
resultItem.author = prevDownloadItem.author
downloadItem?.title = prevDownloadItem.title
downloadItem?.author = prevDownloadItem.author
when(viewPager2.currentItem){
0 -> {
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
}
1 -> {
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
}
else -> {}
}
}
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
cleanUp()

View file

@ -8,6 +8,7 @@ import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.util.Patterns
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
@ -32,6 +33,7 @@ import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.enableTextHighlight
import com.deniscerri.ytdlnis.util.UiUtil.populateCommandCard
import com.google.android.material.card.MaterialCardView
import com.google.android.material.chip.Chip
import com.google.android.material.textfield.TextInputLayout
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
@ -77,6 +79,11 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
}
}
if (!Patterns.WEB_URL.matcher(downloadItem.url).matches()){
downloadItem.format = downloadViewModel.generateCommandFormat(CommandTemplate(0,"txt", "-a \"${downloadItem.url}\"", false))
downloadItem.url = ""
}
try {
val templates : MutableList<CommandTemplate> = withContext(Dispatchers.IO){
commandTemplateViewModel.getAll().toMutableList()
@ -90,6 +97,7 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
view.findViewById<MaterialCardView>(R.id.command_card).alpha = 0.3f
if (p0!!.isNotEmpty()){
chosenCommandView.endIconDrawable = AppCompatResources.getDrawable(requireContext(), R.drawable.ic_delete_all)
}else{
@ -119,26 +127,34 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
chosenCommandView.editText!!.enableScrollText()
val commandTemplateCard = view.findViewById<MaterialCardView>(R.id.command_card)
populateCommandCard(commandTemplateCard, templates.first())
commandTemplateCard.setOnClickListener {
lifecycleScope.launch {
UiUtil.showCommandTemplates(requireActivity(), commandTemplateViewModel){
chosenCommandView.editText!!.setText(it.content)
populateCommandCard(commandTemplateCard, it)
downloadItem.format = Format(
it.title,
"",
"",
"",
"",
0,
it.content
)
runCatching {
populateCommandCard(commandTemplateCard, templates.first())
commandTemplateCard.setOnClickListener {
lifecycleScope.launch {
UiUtil.showCommandTemplates(requireActivity(), commandTemplateViewModel){
chosenCommandView.editText!!.setText(it.content)
populateCommandCard(commandTemplateCard, it)
downloadItem.format = Format(
it.title,
"",
"",
"",
"",
0,
it.content
)
}
}
}
if (downloadItem.url.isEmpty()){
view.findViewById<MaterialCardView>(R.id.command_card).alpha = 0.3f
}
}.onFailure {
commandTemplateCard.visibility = View.GONE
view.findViewById<Chip>(R.id.editSelected).isEnabled = false
view.findViewById<TextView>(R.id.command_txt).visibility = View.GONE
}
saveDir = view.findViewById(R.id.outputPath)
@ -166,7 +182,7 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
UiUtil.configureCommand(
view,
1,
if (downloadItem.url.isEmpty()) 0 else 1,
shortcutCount,
newTemplateClicked = {
UiUtil.showCommandTemplateCreationOrUpdatingSheet(null, requireActivity(), viewLifecycleOwner, commandTemplateViewModel) {
@ -181,6 +197,9 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
0,
it.content
)
commandTemplateCard.visibility = View.VISIBLE
view.findViewById<TextView>(R.id.command_txt).visibility = View.VISIBLE
view.findViewById<Chip>(R.id.editSelected).isEnabled = true
populateCommandCard(commandTemplateCard, it)
}
},

View file

@ -79,6 +79,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
private lateinit var recyclerView: RecyclerView
private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var bottomAppBar: BottomAppBar
private lateinit var filesize : TextView
private lateinit var sharedPreferences: SharedPreferences
@ -126,6 +127,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
val scheduleBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_schedule_button)
val download = view.findViewById<Button>(R.id.bottomsheet_download_button)
filesize = view.findViewById<TextView>(R.id.filesize)
scheduleBtn.setOnClickListener{
@ -184,6 +186,8 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
DownloadViewModel.Type.command -> {
preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24)
}
else -> {}
}
val formatListener = object : OnFormatClickListener {
@ -204,6 +208,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
}
listAdapter.submitList(items.toList())
listAdapter.notifyDataSetChanged()
updateFileSize(items.map { it.format.filesize })
}
@ -272,6 +277,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
items = downloadViewModel.switchDownloadType(items, DownloadViewModel.Type.audio).toMutableList()
listAdapter.submitList(items.toList())
listAdapter.notifyDataSetChanged()
updateFileSize(items.map { it.format.filesize })
preferredDownloadType.setIcon(R.drawable.baseline_audio_file_24)
bottomAppBar.menu[1].icon?.alpha = 255
bottomAppBar.menu[3].icon?.alpha = 255
@ -282,6 +288,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
items = downloadViewModel.switchDownloadType(items, DownloadViewModel.Type.video).toMutableList()
listAdapter.submitList(items.toList())
listAdapter.notifyDataSetChanged()
updateFileSize(items.map { it.format.filesize })
preferredDownloadType.setIcon(R.drawable.baseline_video_file_24)
bottomAppBar.menu[1].icon?.alpha = 255
bottomAppBar.menu[3].icon?.alpha = 255
@ -295,6 +302,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
}
listAdapter.submitList(items.toList())
listAdapter.notifyDataSetChanged()
updateFileSize(items.map { it.format.filesize })
preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24)
bottomAppBar.menu[1].icon?.alpha = 255
bottomAppBar.menu[3].icon?.alpha = 30
@ -477,6 +485,8 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
}
DownloadViewModel.Type.command -> {
}
else -> {}
}
}
}
@ -486,6 +496,15 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
}
private fun updateFileSize(items: List<Long>){
if (items.all { it > 0L }){
filesize.visibility = View.VISIBLE
filesize.text = "${getString(R.string.file_size)}: ~ ${FileUtil.convertFileSize(items.sum())}"
}else{
filesize.visibility = View.GONE
}
}
private var pathResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->

View file

@ -38,15 +38,15 @@ import kotlinx.coroutines.withContext
import java.io.File
class DownloadVideoFragment(private val resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment() {
class DownloadVideoFragment(private val resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment(), TitleAuthorSync {
private var _binding : FragmentHomeBinding? = null
private var fragmentView: View? = null
private var activity: Activity? = null
private lateinit var downloadViewModel : DownloadViewModel
private lateinit var resultViewModel: ResultViewModel
private lateinit var title : TextInputLayout
private lateinit var author : TextInputLayout
lateinit var title : TextInputLayout
lateinit var author : TextInputLayout
private lateinit var saveDir : TextInputLayout
private lateinit var freeSpace : TextView
private lateinit var infoUtil: InfoUtil
@ -266,6 +266,13 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
}
}
override fun updateTitleAuthor(t: String, a: String){
downloadItem.title = t
downloadItem.author = a
title.editText?.setText(t)
author.editText?.setText(a)
}
private var videoPathResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->

View file

@ -0,0 +1,5 @@
package com.deniscerri.ytdlnis.ui.downloadcard
interface TitleAuthorSync {
fun updateTitleAuthor(t: String, a: String)
}

View file

@ -5,6 +5,7 @@ import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.FileObserver
@ -297,7 +298,17 @@ class TerminalActivity : BaseActivity() {
Log.e(TAG, "$action $type")
if (action == Intent.ACTION_SEND && type != null) {
Log.e(TAG, action)
val txt = "yt-dlp " + intent.getStringExtra(Intent.EXTRA_TEXT)
val text = if (intent.getStringExtra(Intent.EXTRA_TEXT) == null){
val uri = if (Build.VERSION.SDK_INT >= 33){
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
}else{
intent.getParcelableExtra(Intent.EXTRA_STREAM)
}
"-a \"${FileUtil.formatPath(uri?.path ?: "")}\""
}else{
intent.getStringExtra(Intent.EXTRA_TEXT)
}
val txt = "yt-dlp $text"
input!!.setText(txt)
}
}

View file

@ -1,24 +1,24 @@
package com.deniscerri.ytdlnis.util
import android.content.ContentResolver
import android.content.Context
import android.database.Cursor
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.util.Log
import android.webkit.MimeTypeMap
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import com.anggrayudi.storage.callback.FileCallback
import com.anggrayudi.storage.callback.FolderCallback
import com.anggrayudi.storage.file.copyFileTo
import com.anggrayudi.storage.file.copyFolderTo
import com.anggrayudi.storage.file.getAbsolutePath
import com.anggrayudi.storage.file.moveFileTo
import com.anggrayudi.storage.file.moveTo
import com.deniscerri.ytdlnis.App
import com.deniscerri.ytdlnis.R
import com.yausername.youtubedl_android.YoutubeDLRequest
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@ -56,6 +56,8 @@ object FileUtil {
var dataValue = path
if (dataValue.startsWith("/storage/")) return dataValue
dataValue = dataValue.replace("content://com.android.externalstorage.documents/tree/", "")
dataValue = dataValue.replace("^/document/".toRegex(), "")
dataValue = dataValue.replace("^primary:".toRegex(), "primary/")
dataValue = dataValue.replace("%3A".toRegex(), "/")
try {
dataValue = URLDecoder.decode(dataValue, StandardCharsets.UTF_8.name())

View file

@ -118,7 +118,7 @@ class InfoUtil(private val context: Context) {
for (i in 0 until dataArray.length()){
val obj = dataArray.getJSONObject(i)
val itm = createVideoFromPipedJSON(obj, "https://youtube.com" + obj.getString("url"))
itm?.playlistTitle = "YTDLnis"
itm?.playlistTitle = res.getString("name") + "[${i+1}]"
items.add(itm)
}
if (nextpage == "null") nextpage = ""
@ -382,7 +382,6 @@ class InfoUtil(private val context: Context) {
if (v == null || v.thumb.isEmpty()) {
continue
}
v.playlistTitle = context.getString(R.string.trendingPlaylist)
items.add(v)
}
return items
@ -398,7 +397,6 @@ class InfoUtil(private val context: Context) {
element.put("uploader", element.getString("uploaderName"))
val v = createVideoFromPipedJSON(element, "https://youtube.com" + element.getString("url"))
if (v == null || v.thumb.isEmpty()) continue
v.playlistTitle = context.getString(R.string.trendingPlaylist)
items.add(v)
}
} catch (e: Exception) {
@ -627,6 +625,11 @@ class InfoUtil(private val context: Context) {
var playlistTitle: String? = ""
if (jsonObject.has("playlist_title")) playlistTitle = jsonObject.getString("playlist_title")
if(playlistTitle.equals(query)) playlistTitle = ""
if (playlistTitle?.isNotBlank() == true){
playlistTitle += "[${jsonObject.getString("playlist_index")}]"
}
val formatsInJSON = if (jsonObject.has("formats") && jsonObject.get("formats") is JSONArray) jsonObject.getJSONArray("formats") else null
val formats : ArrayList<Format> = parseYTDLFormats(formatsInJSON)
@ -967,6 +970,19 @@ class InfoUtil(private val context: Context) {
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)
if (aria2) {
@ -1017,15 +1033,13 @@ class InfoUtil(private val context: Context) {
}
}
if(downloadItem.title.isNotBlank()){
request.addCommands(listOf("--replace-in-metadata","title",".*.",downloadItem.title.take(200)))
request.addOption("--parse-metadata", downloadItem.title.take(200) + ":%(title)s")
}
if (downloadItem.author.isNotBlank()){
request.addCommands(listOf("--replace-in-metadata","uploader",".*.",downloadItem.author.take(25)))
downloadItem.customFileNameTemplate = downloadItem.customFileNameTemplate.replace("%(uploader)s", downloadItem.author.take(25))
request.addOption("--parse-metadata", downloadItem.author.take(25) + ":%(artist)s")
}
request.addCommands(listOf("--replace-in-metadata","uploader"," - Topic$",""))
request.addCommands(listOf("--replace-in-metadata","artist"," - Topic$",""))
if (downloadItem.downloadSections.isNotBlank()){
downloadItem.downloadSections.split(";").forEach {
@ -1319,6 +1333,8 @@ class InfoUtil(private val context: Context) {
)
}
else -> {}
}
return request
@ -1333,7 +1349,15 @@ class InfoUtil(private val context: Context) {
}
}
return java.lang.String.join(" ", arr).replace("\"\"", "\" \"")
var final = java.lang.String.join(" ", arr).replace("\"\"", "\" \"")
val ppas = "--config(-locations)? \"(.*?)\"".toRegex().findAll(final)
ppas.forEach {
val path = "\"(.*?)\"".toRegex().find(it.value)?.value?.replace("\"", "")
final = final.replace(it.value, "")
final += " ${File(path ?: "").readText()}"
}
return final
}
private val pipedURL = sharedPreferences.getString("piped_instance", defaultPipedURL)?.ifEmpty { defaultPipedURL }?.removeSuffix("/")

View file

@ -8,6 +8,7 @@ import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.graphics.Color
import android.graphics.Typeface
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.OvalShape
import android.net.Uri
@ -22,7 +23,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.inputmethod.InputMethodManager
import android.webkit.MimeTypeMap
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
@ -40,6 +40,7 @@ import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.App
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.DownloadItem
@ -76,6 +77,7 @@ import java.util.Calendar
import java.util.Locale
import java.util.regex.Pattern
object UiUtil {
@SuppressLint("SetTextI18n")
fun populateFormatCard(context: Context, formatCard : MaterialCardView, chosenFormat: Format, audioFormats: List<Format>?){
@ -118,6 +120,7 @@ object UiUtil {
fun populateCommandCard(card: MaterialCardView, item: CommandTemplate){
card.findViewById<TextView>(R.id.title).text = item.title
card.findViewById<TextView>(R.id.content).text = item.content
card.alpha = 1f
card.tag = item.id
}
@ -812,7 +815,7 @@ object UiUtil {
val entries = context.resources.getStringArray(R.array.sponsorblock_settings_entries)
val checkedItems : ArrayList<Boolean> = arrayListOf()
values.forEach {
if (items[0].videoPreferences.sponsorBlockFilters.contains(it) && items.size == 1) {
if (items.all{ i -> i.videoPreferences.sponsorBlockFilters.contains(it)}) {
checkedItems.add(true)
}else{
checkedItems.add(false)
@ -1034,7 +1037,7 @@ object UiUtil {
val entries = context.resources.getStringArray(R.array.sponsorblock_settings_entries)
val checkedItems : ArrayList<Boolean> = arrayListOf()
values.forEach {
if (items[0].audioPreferences.sponsorBlockFilters.contains(it) && items.size == 1) {
if (items.all{ i -> i.audioPreferences.sponsorBlockFilters.contains(it)}) {
checkedItems.add(true)
}else{
checkedItems.add(false)

View file

@ -49,17 +49,15 @@ class UpdateUtil(var context: Context) {
}
val useBeta = sharedPreferences.getBoolean("update_beta", false)
val v: GithubRelease? = runCatching {
res.firstOrNull {
if (useBeta) it.tag_name.contains("beta", true)
else !it.tag_name.contains("beta", true)
}
}.getOrNull()
var v: GithubRelease?
if (useBeta){
v = res.firstOrNull { it.tag_name.contains("beta", true) && res.indexOf(it) == 0 }
if (v == null) v = res.first()
}else{
v = res.first { !it.tag_name.contains("beta", true) }
}
if (
(useBeta && v == null) ||
BuildConfig.VERSION_NAME == v!!.tag_name.removePrefix("v").removeSuffix("-beta")
){
if (BuildConfig.VERSION_NAME == v.tag_name.removePrefix("v")){
result(context.getString(R.string.you_are_in_latest_version))
return
}

View file

@ -35,7 +35,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.File
import java.util.Calendar
import kotlin.random.Random
@ -231,8 +230,6 @@ class DownloadWorker(
if (logDownloads){
logRepo.update(it.out, logItem.id)
}
if (items.size <= 1) WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id)
}
}.onFailure {

View file

@ -86,6 +86,20 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/filesize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="20dp"
android:paddingTop="10dp"
android:visibility="gone"
android:text="@string/file_size"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/downloadMultipleRecyclerview"
android:layout_width="match_parent"

View file

@ -29,6 +29,7 @@
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/command_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:id="@+id/textview"
android:textStyle="bold"
android:fontFamily="monospace"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</TextView>

View file

@ -16,7 +16,6 @@
<string name="audio_format">অডিও ফরম্যাট</string>
<string name="title">শিরোনাম</string>
<string name="adjust_audio">অডিও সামঞ্জস্য করুন</string>
<string name="workmanager_updated">এই পছন্দটি কার্যকর করার জন্য আপনাকে অ্যাপটি পুনরায় চালু করতে হবে এবং কোনও সক্রিয় ডাউনলোড নেই!</string>
<string name="dont_ask_again">আবার জিজ্ঞাসা করবেন না</string>
<string name="music_directory">মিউজিক ফোল্ডার</string>
<string name="warning">সতর্কতা</string>

View file

@ -10,7 +10,6 @@
<string name="in_queue">Di Antrean</string>
<string name="split_by_chapters">Kapisah dening Undhang-undhang</string>
<string name="adjust_audio">Nyetel Audio</string>
<string name="workmanager_updated">Sampeyan kudu miwiti maneh app lan ora duwe download aktif supaya pilihan iki ditrapake!</string>
<string name="dont_ask_again">Aja takon maneh</string>
<string name="music_directory">Folder musik</string>
<string name="warning">Pènget</string>

View file

@ -144,12 +144,14 @@
<string-array name="download_types">
<item>@string/auto</item>
<item>@string/audio</item>
<item>@string/video</item>
<item>@string/command</item>
</string-array>
<string-array name="download_types_values">
<item>auto</item>
<item>audio</item>
<item>video</item>
<item>command</item>
@ -800,4 +802,5 @@
<item>h264</item>
</string-array>
<string name="auto">Auto</string>
</resources>

View file

@ -45,7 +45,7 @@
app:title="@string/socks5_proxy" />
<ListPreference
android:defaultValue="video"
android:defaultValue="auto"
android:entries="@array/download_types"
android:entryValues="@array/download_types_values"
android:icon="@drawable/ic_download_type"