- fixed error notification not being dismissed and having a progress bar
- fixed editing filename template not using multiple copies of the same tag and writing at the cursor
- fixed appending search items in the search view not working for links
- fixed terminal removing any instance of yt-dlp in the command instead of just the beginning
- added ability to long press an item in the format details sheet to see the full string, and copy it/strings
- ellipsised really long titles and authors in history/download details bottom sheet
- now u can see all available piped instances in the piped instance dialog for you to choose
- removed really long format command and replaced them with -S format sorting
- fixed app not hiding adjust templates if user unchecked it
- added ability to show the command that was used in a history item, u can also see that in a queued,cancelled ... download
- Implemented preferred Audio Language. App will automatically choose an audio with your preference if it can find it, both in the download card also if you quick downloaded it
This commit is contained in:
zaednasr 2023-12-03 11:26:24 +01:00
parent a2369a85da
commit 19a52c13ec
No known key found for this signature in database
GPG key ID: 92B1DE23AD3D0E9E
60 changed files with 992 additions and 470 deletions

View file

@ -23,7 +23,7 @@ data class DownloadItem(
var container: String,
@ColumnInfo(defaultValue = "")
var downloadSections: String,
val allFormats: MutableList<Format>,
var allFormats: MutableList<Format>,
var downloadPath: String,
var website: String,
val downloadSize: String,

View file

@ -25,5 +25,7 @@ data class Format(
@SerializedName(value = "asr", alternate = ["audioSampleRate"])
var asr: String? = "",
@SerializedName(value = "url")
var url: String? = ""
var url: String? = "",
@SerializedName(value = "language", alternate = ["audioTrackLocale"])
val lang: String? = ""
) : Parcelable

View file

@ -45,13 +45,15 @@ class LogRepository(private val logDao: LogDao) {
val log = item.content ?: ""
val lines = log.split("\n")
//clean dublicate progress + add newline
var newLine = line
if (newLine.contains("[download")){
newLine = "[download]" + line.split("[download]").last()
}
var newLine = line
if (newLine.contains("[download")){
newLine = "[download]" + line.split("[download]").last()
}
val l = lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}"
item.content = l
val l = lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}"
item.content = l
//item.content += "\n$line"
logDao.update(item)
}
}

View file

@ -33,7 +33,10 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
if (resetResults) deleteAll()
val res = infoUtil.search(inputQuery)
itemCount.value = res.size
resultDao.insertMultiple(res)
val ids = resultDao.insertMultiple(res)
ids.forEachIndexed { index, id ->
res[index]?.id = id
}
return res
}
@ -46,7 +49,10 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
}else{
v.filter { it?.playlistTitle.isNullOrBlank() }.forEach { it?.playlistTitle = "ytdlnis-Search" }
}
resultDao.insertMultiple(v)
val ids = resultDao.insertMultiple(v)
ids.forEachIndexed { index, id ->
v[index]?.id = id
}
return ArrayList(v)
}
@ -66,7 +72,10 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
nextPageToken = tmpToken
} while (true)
itemCount.value = items.size
resultDao.insertMultiple(items.toList())
val ids = resultDao.insertMultiple(items.toList())
ids.forEachIndexed { index, id ->
items[index]?.id = id
}
return items
}
@ -79,7 +88,10 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
}else{
items.filter { it?.playlistTitle.isNullOrBlank() }.forEach { it!!.playlistTitle = "ytdlnis-Search" }
}
resultDao.insertMultiple(items.toList())
val ids = resultDao.insertMultiple(items.toList())
ids.forEachIndexed { index, id ->
items[index]?.id = id
}
return items
}

View file

@ -441,6 +441,13 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val theFormats = formats.filter { it.format_note.contains("audio", ignoreCase = true) }
val requirements: MutableList<(Format) -> Boolean> = mutableListOf()
requirements.add {it: Format -> audioFormatIDPreference.contains(it.format_id)}
sharedPreferences.getString("audio_language", "")?.apply {
if (this.isNotEmpty()){
requirements.add { it: Format -> it.lang == this }
}
}
requirements.add {it: Format -> it.container == audioContainer }
requirements.add {it: Format -> "^(${audioCodec}).+$".toRegex(RegexOption.IGNORE_CASE).matches(it.acodec)}
theFormats.maxByOrNull { f -> requirements.count{req -> req(f)} } ?: throw Exception()

View file

@ -23,6 +23,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.os.bundleOf
import androidx.core.view.children
import androidx.core.view.forEach
import androidx.core.view.isVisible
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
@ -89,6 +90,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
private var shimmerCards: ShimmerFrameLayout? = null
private var searchBar: SearchBar? = null
private var searchView: SearchView? = null
private var providersChipGroup: ChipGroup? = null
private var chipGroupDivider: View? = null
private var linkYouCopied: ConstraintLayout? = null
private var queriesChipGroup: ChipGroup? = null
private var recyclerView: RecyclerView? = null
@ -280,9 +283,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
}
if (searchView?.currentTransitionState == SearchView.TransitionState.SHOWN){
lifecycleScope.launch {
updateSearchViewItems(searchView?.editText?.text, linkYouCopied)
}
updateSearchViewItems(searchView?.editText?.text, linkYouCopied)
}
}
@ -292,7 +293,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
val queriesInitStartBtn = queriesConstraint.findViewById<MaterialButton>(R.id.init_search_query)
val isRightToLeft = resources.getBoolean(R.bool.is_right_to_left)
val providersChipGroup = searchView!!.findViewById<ChipGroup>(R.id.providers)
providersChipGroup = searchView!!.findViewById(R.id.providers)
val providers = resources.getStringArray(R.array.search_engines)
val providersValues = resources.getStringArray(R.array.search_engines_values).toMutableList()
@ -311,27 +312,27 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
}
providersChipGroup!!.addView(tmp)
providersChipGroup?.addView(tmp)
}
val chipGroupDivider : View? = requireView().findViewById(R.id.chipGroupDivider)
chipGroupDivider = requireView().findViewById(R.id.chipGroupDivider)
searchView!!.addTransitionListener { _, _, newState ->
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) {
providersChipGroup?.children?.forEach {
val tmp = providersChipGroup?.findViewById<Chip>(it.id)
if (tmp?.tag?.equals(currentProvider) == true) {
tmp.isChecked = true
return@forEach
}
}
if (Patterns.WEB_URL.matcher(searchBar!!.text.toString()).matches() && searchBar!!.text.isNotBlank()){
providersChipGroup.visibility = GONE
providersChipGroup?.visibility = GONE
chipGroupDivider?.visibility = GONE
}else{
providersChipGroup.visibility = VISIBLE
providersChipGroup?.visibility = VISIBLE
chipGroupDivider?.visibility = VISIBLE
}
@ -371,9 +372,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
}else{
linkYouCopied!!.visibility = GONE
}
lifecycleScope.launch {
updateSearchViewItems(searchView!!.editText.text, linkYouCopied)
}
updateSearchViewItems(searchView!!.editText.text, linkYouCopied)
}catch (e: Exception){
e.printStackTrace()
linkYouCopied!!.visibility = GONE
@ -383,16 +382,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
searchView!!.editText.doAfterTextChanged {
if (searchView!!.currentTransitionState != SearchView.TransitionState.SHOWN) return@doAfterTextChanged
lifecycleScope.launch {
updateSearchViewItems(it, linkYouCopied)
}
if (Patterns.WEB_URL.matcher(it.toString()).matches()){
providersChipGroup.visibility = GONE
chipGroupDivider?.visibility = GONE
}else{
providersChipGroup.visibility = VISIBLE
chipGroupDivider?.visibility = VISIBLE
}
updateSearchViewItems(it, linkYouCopied)
}
searchView!!.editText.setOnTouchListener(OnTouchListener { _, event ->
@ -450,7 +440,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
queriesChipGroup!!.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
if (queriesChipGroup!!.childCount == 0) queriesConstraint.visibility = GONE
else queriesConstraint.visibility = VISIBLE
searchView!!.editText.setText("")
}
queriesInitStartBtn.setOnClickListener {
@ -459,11 +448,15 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
}
@SuppressLint("InflateParams")
private suspend fun updateSearchViewItems(searchQuery: Editable?, linkYouCopied: View?){
private fun updateSearchViewItems(searchQuery: Editable?, linkYouCopied: View?) = lifecycleScope.launch(Dispatchers.Main) {
searchSuggestionsLinearLayout!!.visibility = GONE
searchHistoryLinearLayout!!.visibility = GONE
searchSuggestionsLinearLayout!!.removeAllViews()
searchHistoryLinearLayout!!.removeAllViews()
searchSuggestionsLinearLayout!!.post {
searchSuggestionsLinearLayout!!.removeAllViews()
}
searchHistoryLinearLayout!!.post {
searchHistoryLinearLayout!!.removeAllViews()
}
linkYouCopied!!.visibility = GONE
@ -472,76 +465,82 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
}else{
searchView!!.editText.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_plus, 0)
}
val suggestions = withContext(Dispatchers.IO){
resultViewModel.getSearchHistory().map { it.query }.filter { it.contains(searchQuery!!) } +
if (sharedPreferences!!.getBoolean("search_suggestions", false)){
infoUtil!!.getSearchSuggestions(searchQuery.toString())
}else{
emptyList()
}
val history = withContext(Dispatchers.IO){
resultViewModel.getSearchHistory().map { it.query }.filter { it.contains(searchQuery!!) }
}
if (searchQuery!!.isEmpty()){
for (i in suggestions.indices) {
val v = LayoutInflater.from(fragmentContext)
.inflate(R.layout.search_suggestion_item, null)
val textView = v.findViewById<TextView>(R.id.suggestion_text)
textView.text = suggestions[i]
textView.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_restore, 0, 0, 0)
Handler(Looper.getMainLooper()).post {
searchHistoryLinearLayout!!.addView(
v
)
}
textView.setOnClickListener {
searchView!!.setText(textView.text)
initSearch(searchView!!)
}
textView.setOnLongClickListener {
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + textView.text + "\"!")
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
searchHistoryLinearLayout!!.removeView(v)
resultViewModel.removeSearchQueryFromHistory(textView.text.toString())
}
deleteDialog.show()
true
}
val mb = v.findViewById<ImageButton>(R.id.set_search_query_button)
mb.setOnClickListener {
searchView!!.setText(textView.text)
searchView!!.editText.setSelection(searchView!!.editText.length())
}
}
searchHistoryLinearLayout!!.visibility = VISIBLE
if (linkYouCopied.findViewById<TextView>(R.id.suggestion_text).text.isNotEmpty()){
linkYouCopied.visibility = VISIBLE
}
val suggestions = if (sharedPreferences!!.getBoolean("search_suggestions", false)){
infoUtil!!.getSearchSuggestions(searchQuery.toString())
}else{
for (i in suggestions.indices) {
val v = LayoutInflater.from(fragmentContext)
.inflate(R.layout.search_suggestion_item, null)
val textView = v.findViewById<TextView>(R.id.suggestion_text)
textView.text = suggestions[i]
Handler(Looper.getMainLooper()).post {
searchSuggestionsLinearLayout!!.addView(
v
)
}
textView.setOnClickListener {
searchView!!.setText(textView.text)
initSearch(searchView!!)
}
val mb = v.findViewById<ImageButton>(R.id.set_search_query_button)
mb.setOnClickListener {
searchView!!.setText(textView.text)
searchView!!.editText.setSelection(searchView!!.editText.length())
}
}
searchSuggestionsLinearLayout!!.visibility = VISIBLE
emptyList()
}
history.forEach { s ->
val v = LayoutInflater.from(fragmentContext).inflate(R.layout.search_suggestion_item, null)
val textView = v.findViewById<TextView>(R.id.suggestion_text)
textView.text = s
textView.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_restore, 0, 0, 0)
Handler(Looper.getMainLooper()).post {
searchHistoryLinearLayout!!.addView(
v
)
}
textView.setOnClickListener {
searchView!!.setText(s)
initSearch(searchView!!)
}
textView.setOnLongClickListener {
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + s + "\"!")
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
searchHistoryLinearLayout!!.removeView(v)
resultViewModel.removeSearchQueryFromHistory(s)
}
deleteDialog.show()
true
}
val mb = v.findViewById<ImageButton>(R.id.set_search_query_button)
mb.setOnClickListener {
searchView!!.editText.setText(s)
searchView!!.editText.setSelection(searchView!!.editText.length())
}
}
searchHistoryLinearLayout!!.isVisible = history.isNotEmpty()
if (linkYouCopied.findViewById<TextView>(R.id.suggestion_text).text.isNotEmpty()){
linkYouCopied.visibility = VISIBLE
}
suggestions.forEach { s ->
val v = LayoutInflater.from(fragmentContext)
.inflate(R.layout.search_suggestion_item, null)
val textView = v.findViewById<TextView>(R.id.suggestion_text)
textView.text = s
Handler(Looper.getMainLooper()).post {
searchSuggestionsLinearLayout!!.addView(
v
)
}
textView.setOnClickListener {
searchView!!.setText(s)
initSearch(searchView!!)
}
val mb = v.findViewById<ImageButton>(R.id.set_search_query_button)
mb.setOnClickListener {
searchView!!.editText.setText(s)
searchView!!.editText.setSelection(searchView!!.editText.length())
}
}
searchSuggestionsLinearLayout!!.isVisible = suggestions.isNotEmpty()
if (Patterns.WEB_URL.matcher(searchView!!.editText.text).matches()){
providersChipGroup?.visibility = GONE
chipGroupDivider?.visibility = GONE
}else{
providersChipGroup?.visibility = VISIBLE
chipGroupDivider?.visibility = VISIBLE
}
}
private fun initSearch(searchView: SearchView){

View file

@ -112,7 +112,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
val formatDetailsChip = card.findViewById<Chip>(R.id.format_note)
val sideDetails = mutableListOf<String>()
sideDetails.add(item.format.format_note.uppercase())
sideDetails.add(item.format.format_note.uppercase().replace("\n", " "))
sideDetails.add(item.container.uppercase().ifEmpty { item.format.container.uppercase() })
val fileSize = FileUtil.convertFileSize(item.format.filesize)

View file

@ -14,12 +14,14 @@ import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.util.Log
import androidx.preference.PreferenceManager
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdlnis.R
@ -205,19 +207,21 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
override fun onFormatsUpdated(allFormats: List<List<Format>>) {
lifecycleScope.launch(Dispatchers.IO) {
resultItem?.formats?.removeAll(formats.toSet())
resultItem?.formats?.addAll(allFormats.first().filter { !genericAudioFormats.contains(it) })
if (resultItem != null){
resultViewModel.update(resultItem!!)
resultItem?.apply {
this.formats.removeAll(formats.toSet())
this.formats.addAll(allFormats.first().filter { !genericAudioFormats.contains(it) })
resultViewModel.update(this)
kotlin.runCatching {
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
f1.updateUI(resultItem)
f1.updateUI(this)
}
}
}
formats = allFormats.first().filter { !genericAudioFormats.contains(it) }.toMutableList()
formats.removeAll(genericAudioFormats)
val preferredFormat = downloadViewModel.getFormat(formats, Type.audio)
downloadItem.format = preferredFormat
downloadItem.allFormats = formats
UiUtil.populateFormatCard(requireContext(), formatCard, preferredFormat, null)
}
}

View file

@ -20,6 +20,7 @@ import android.widget.Toast
import androidx.core.content.edit
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.fragment.app.commit
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
@ -46,7 +47,6 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -417,65 +417,69 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
lifecycleScope.launch {
resultViewModel.updatingData.collectLatest {
if (it){
title.visibility = View.GONE
subtitle.visibility = View.GONE
shimmerLoading.visibility = View.VISIBLE
shimmerLoadingSubtitle.visibility = View.VISIBLE
shimmerLoading.startShimmer()
shimmerLoadingSubtitle.startShimmer()
}else{
title.visibility = View.VISIBLE
subtitle.visibility = View.VISIBLE
shimmerLoading.visibility = View.GONE
shimmerLoadingSubtitle.visibility = View.GONE
shimmerLoading.stopShimmer()
shimmerLoadingSubtitle.stopShimmer()
kotlin.runCatching {
if (it){
title.visibility = View.GONE
subtitle.visibility = View.GONE
shimmerLoading.visibility = View.VISIBLE
shimmerLoadingSubtitle.visibility = View.VISIBLE
shimmerLoading.startShimmer()
shimmerLoadingSubtitle.startShimmer()
}else{
title.visibility = View.VISIBLE
subtitle.visibility = View.VISIBLE
shimmerLoading.visibility = View.GONE
shimmerLoadingSubtitle.visibility = View.GONE
shimmerLoading.stopShimmer()
shimmerLoadingSubtitle.stopShimmer()
}
}
}
}
lifecycleScope.launch {
resultViewModel.updatingFormats.collectLatest {
if (it){
delay(500)
runCatching {
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
isVisible = true
isClickable = true
setOnClickListener {
lifecycleScope.launch(Dispatchers.IO) {
resultViewModel.cancelUpdateFormatsItemData()
kotlin.runCatching {
if (it){
delay(500)
runCatching {
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
isVisible = true
isClickable = true
setOnClickListener {
lifecycleScope.launch(Dispatchers.IO) {
resultViewModel.cancelUpdateFormatsItemData()
}
}
}
}
}
runCatching {
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
isVisible = true
isClickable = true
setOnClickListener {
lifecycleScope.launch(Dispatchers.IO) {
resultViewModel.cancelUpdateFormatsItemData()
runCatching {
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
isVisible = true
isClickable = true
setOnClickListener {
lifecycleScope.launch(Dispatchers.IO) {
resultViewModel.cancelUpdateFormatsItemData()
}
}
}
}
}
}else{
runCatching {
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
isVisible = false
isClickable = false
}else{
runCatching {
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
isVisible = false
isClickable = false
}
}
}
runCatching {
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
isVisible = false
isClickable = false
runCatching {
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
isVisible = false
isClickable = false
}
}
}
}
@ -485,47 +489,50 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
lifecycleScope.launch {
resultViewModel.updateResultData.collectLatest { result ->
if (result == null) return@collectLatest
lifecycleScope.launch(Dispatchers.Main) {
if (result.isNotEmpty()){
if (result.size == 1 && result[0] != null){
fragmentAdapter.setResultItem(result[0]!!)
runCatching {
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
f.updateUI(result[0])
}
kotlin.runCatching {
lifecycleScope.launch(Dispatchers.Main) {
if (result.isNotEmpty()){
if (result.size == 1 && result[0] != null){
fragmentAdapter.setResultItem(result[0]!!)
runCatching {
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
f.updateUI(result[0])
}
runCatching {
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
f1.updateUI(result[0])
}
runCatching {
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
f1.updateUI(result[0])
}
title.visibility = View.VISIBLE
subtitle.visibility = View.VISIBLE
shimmerLoading.visibility = View.GONE
shimmerLoadingSubtitle.visibility = View.GONE
shimmerLoading.stopShimmer()
shimmerLoadingSubtitle.stopShimmer()
title.visibility = View.VISIBLE
subtitle.visibility = View.VISIBLE
shimmerLoading.visibility = View.GONE
shimmerLoadingSubtitle.visibility = View.GONE
shimmerLoading.stopShimmer()
shimmerLoadingSubtitle.stopShimmer()
val usingGenericFormatsOrEmpty = result[0]!!.formats.isEmpty() || result[0]!!.formats.any { it.format_note.contains("ytdlnisgeneric") }
fragmentAdapter.setResultItem(result[0]!!)
arguments?.putParcelable("result", result[0]!!)
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false)){
initUpdateFormats(result[0]!!)
}
}else{
//open multi download card instead
if (activity is ShareActivity){
val preferredType = DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!)
findNavController().navigate(R.id.action_downloadBottomSheetDialog_to_selectPlaylistItemsDialog, bundleOf(
Pair("results", result),
Pair("type", preferredType),
))
val usingGenericFormatsOrEmpty = result[0]!!.formats.isEmpty() || result[0]!!.formats.any { it.format_note.contains("ytdlnisgeneric") }
fragmentAdapter.setResultItem(result[0]!!)
arguments?.putParcelable("result", result[0]!!)
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false)){
initUpdateFormats(result[0]!!)
}
}else{
dismiss()
//open multi download card instead
if (activity is ShareActivity){
val preferredType = DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!)
findNavController().navigate(R.id.action_downloadBottomSheetDialog_to_selectPlaylistItemsDialog, bundleOf(
Pair("results", result),
Pair("type", preferredType),
))
}else{
dismiss()
}
}
}
resultViewModel.updateResultData.emit(null)
}
resultViewModel.updateResultData.emit(null)
}
}
}
@ -533,40 +540,37 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
lifecycleScope.launch {
resultViewModel.updateFormatsResultData.collectLatest { formats ->
if (formats == null) return@collectLatest
lifecycleScope.launch {
withContext(Dispatchers.Main){
runCatching {
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
resultItem.formats = formats
fragmentAdapter.setResultItem(resultItem)
f1.updateUI(resultItem)
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
kotlin.runCatching {
lifecycleScope.launch {
withContext(Dispatchers.Main){
runCatching {
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
resultItem.formats = formats
fragmentAdapter.setResultItem(resultItem)
f1.updateUI(resultItem)
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
}
runCatching {
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
resultItem.formats = formats
fragmentAdapter.setResultItem(resultItem)
f1.updateUI(resultItem)
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
}
}
runCatching {
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
resultItem.formats = formats
fragmentAdapter.setResultItem(resultItem)
f1.updateUI(resultItem)
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
}
}
if (formats.isNotEmpty()){
result.formats = formats
if (formats.isNotEmpty()){
result.formats = formats
}
resultViewModel.updateFormatsResultData.emit(null)
}
resultViewModel.updateFormatsResultData.emit(null)
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem{
return when(selectedTabPosition){
0 -> {
@ -614,21 +618,25 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
}
private fun initUpdateData() {
if (result.url.isBlank()) {
dismiss()
return
}
if (resultViewModel.updatingData.value) return
kotlin.runCatching {
if (result.url.isBlank()) {
dismiss()
return
}
if (resultViewModel.updatingData.value) return
CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
resultViewModel.updateItemData(result)
CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
resultViewModel.updateItemData(result)
}
}
}
private fun initUpdateFormats(res: ResultItem){
if (resultViewModel.updatingFormats.value) return
CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
resultViewModel.updateFormatItemData(res)
kotlin.runCatching {
if (resultViewModel.updatingFormats.value) return
CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
resultViewModel.updateFormatItemData(res)
}
}
}
}

View file

@ -66,7 +66,7 @@ class DownloadCommandFragment(private val resultItem: ResultItem? = null, privat
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
shownFields = preferences.getStringSet("modify_download_card", setOf())!!.toList().ifEmpty { requireContext().getStringArray(R.array.modify_download_card_values).toList() }
shownFields = preferences.getStringSet("modify_download_card", requireContext().getStringArray(R.array.modify_download_card_values).toSet())!!.toList()
return fragmentView
}

View file

@ -470,6 +470,13 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
}
)
bottomSheet.show()
val displayMetrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
bottomSheet.behavior.peekHeight = displayMetrics.heightPixels
bottomSheet.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
DownloadViewModel.Type.video -> {
val bottomSheet = BottomSheetDialog(requireContext())
@ -530,6 +537,13 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
)
bottomSheet.show()
val displayMetrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
bottomSheet.behavior.peekHeight = displayMetrics.heightPixels
bottomSheet.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
DownloadViewModel.Type.command -> {
}

View file

@ -223,13 +223,13 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
override fun onFormatsUpdated(allFormats: List<List<Format>>) {
lifecycleScope.launch {
withContext(Dispatchers.IO){
resultItem?.formats?.removeAll(formats)
resultItem?.formats?.addAll(allFormats.first().filter { !genericVideoFormats.contains(it) })
if (resultItem != null){
resultViewModel.update(resultItem!!)
resultItem?.apply {
this.formats.removeAll(formats)
this.formats.addAll(allFormats.first().filter { !genericVideoFormats.contains(it) })
resultViewModel.update(this)
kotlin.runCatching {
val f1 = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
f1.updateUI(resultItem)
f1.updateUI(this)
}
}
}
@ -237,6 +237,8 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
formats = allFormats.first().filter { !genericVideoFormats.contains(it) }.toMutableList()
val preferredFormat = downloadViewModel.getFormat(formats, Type.video)
val preferredAudioFormats = downloadViewModel.getPreferredAudioFormats(formats)
downloadItem.format = preferredFormat
downloadItem.allFormats = formats
UiUtil.populateFormatCard(requireContext(), formatCard, preferredFormat, formats.filter { preferredAudioFormats.contains(it.format_id) })
}

View file

@ -11,23 +11,9 @@ class ProcessingSettingsFragment : BaseSettingsFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.processing_preferences, rootKey)
val preferredVideoCodec : ListPreference? = findPreference("video_codec")
val preferredAudioCodec : ListPreference? = findPreference("audio_codec")
val preferredFormatID : EditTextPreference? = findPreference("format_id")
val preferredFormatIDAudio : EditTextPreference? = findPreference("format_id_audio")
preferredVideoCodec?.summary = preferredVideoCodec?.entry
preferredVideoCodec?.setOnPreferenceChangeListener { preference, newValue ->
preferredVideoCodec.summary = preferredVideoCodec.entries[preferredVideoCodec.findIndexOfValue(newValue.toString())]
true
}
preferredAudioCodec?.summary = preferredAudioCodec?.entry
preferredAudioCodec?.setOnPreferenceChangeListener { preference, newValue ->
preferredAudioCodec.summary = preferredAudioCodec.entries[preferredAudioCodec.findIndexOfValue(newValue.toString())]
true
}
preferredFormatID?.title = "${getString(R.string.preferred_format_id)} [${getString(R.string.video)}]"
preferredFormatID?.dialogTitle = "${getString(R.string.file_name_template)} [${getString(R.string.video)}]"

View file

@ -5,6 +5,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UpdateUtil
import com.google.android.material.snackbar.Snackbar
import com.yausername.youtubedl_android.YoutubeDL
@ -63,6 +64,15 @@ class UpdateSettingsFragment : BaseSettingsFragment() {
true
}
val pipedInstance = findPreference<Preference>("piped_instance")
pipedInstance?.setOnPreferenceClickListener {
UiUtil.showPipedInstancesDialog(requireActivity(), preferences.getString("piped_instance", "")!!){
editor.putString("piped_instance", it)
editor.apply()
}
true
}
}

View file

@ -202,7 +202,7 @@ class TerminalFragment : Fragment() {
showCancelFab()
imm.hideSoftInputFromWindow(input?.windowToken, 0)
lifecycleScope.launch {
val command = input!!.text.toString().replace("yt-dlp", "")
val command = input!!.text.toString().replaceFirst("yt-dlp", "")
downloadID = withContext(Dispatchers.IO){
terminalViewModel.insert(TerminalItem(command = command, log = output!!.text.toString()))
}

View file

@ -98,12 +98,14 @@ object Extensions {
}
fun File.getMediaDuration(context: Context): Int {
if (!exists()) return 0
val retriever = MediaMetadataRetriever()
retriever.setDataSource(context, Uri.parse(absolutePath))
val duration = retriever.extractMetadata(METADATA_KEY_DURATION)
retriever.release()
return kotlin.runCatching {
if (!exists()) return 0
val retriever = MediaMetadataRetriever()
retriever.setDataSource(context, Uri.parse(absolutePath))
val duration = retriever.extractMetadata(METADATA_KEY_DURATION)
retriever.release()
return duration?.toIntOrNull()?.div(1000) ?: 0
duration?.toIntOrNull()?.div(1000) ?: 0
}.getOrElse { 0 }
}
}

View file

@ -10,6 +10,7 @@ import android.util.Patterns
import android.widget.Toast
import androidx.preference.PreferenceManager
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.anggrayudi.storage.extension.count
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.ChapterItem
import com.deniscerri.ytdlnis.database.models.DownloadItem
@ -1045,13 +1046,11 @@ class InfoUtil(private val context: Context) {
request.addOption("--keep-fragments")
}
val preferredAudioCodec = sharedPreferences.getString("audio_codec", "")!!
val aCodecPref = "ba[acodec~='^($preferredAudioCodec)']"
val embedMetadata = sharedPreferences.getBoolean("embed_metadata", true)
var filenameTemplate = downloadItem.customFileNameTemplate
if(downloadItem.type != DownloadViewModel.Type.command){
request.addOption("--trim-filenames", downDir.absolutePath.length + 117)
request.addOption("--trim-filenames", 254 - downDir.absolutePath.length)
if (downloadItem.SaveThumb) {
request.addOption("--write-thumbnail")
@ -1144,31 +1143,52 @@ class InfoUtil(private val context: Context) {
}
}
val preferredAudioCodec = sharedPreferences.getString("audio_codec", "")!!
val aCodecPrefIndex = context.getStringArray(R.array.audio_codec_values).indexOf(preferredAudioCodec)
val aCodecPref = context.getStringArray(R.array.audio_codec_values_ytdlp)[aCodecPrefIndex]
when(type){
DownloadViewModel.Type.audio -> {
val supportedContainers = context.resources.getStringArray(R.array.audio_containers)
var audioQualityId : String = downloadItem.format.format_id
if (audioQualityId.isBlank() || listOf("0", context.getString(R.string.best_quality), "best", "").contains(audioQualityId)){
if (audioQualityId.isBlank() || listOf("0", context.getString(R.string.best_quality), "ba", "best", "").contains(audioQualityId)){
audioQualityId = ""
if (preferredAudioCodec.isNotBlank()){
audioQualityId = "${aCodecPref}/bestaudio"
}
}else if (listOf(context.getString(R.string.worst_quality), "worst").contains(audioQualityId)){
}else if (listOf(context.getString(R.string.worst_quality), "wa", "worst").contains(audioQualityId)){
audioQualityId = "worstaudio"
}
val ext = downloadItem.container
if (audioQualityId.isNotBlank()) request.addOption("-f", audioQualityId)
if (audioQualityId.isNotBlank()) {
if (audioQualityId.contains("-") && !downloadItem.format.lang.isNullOrBlank() && downloadItem.format.lang != "None"){
audioQualityId = "ba[format_id~='^(${audioQualityId.split("-")[0]})'][language=${downloadItem.format.lang}]/ba/b"
}
request.addOption("-f", audioQualityId)
}else{
//enters here if generic or quick downloaded with ba format
val preferredLanguage = sharedPreferences.getString("audio_language","")!!
if (preferredLanguage.isNotBlank()){
request.addOption("-f", "ba[language=$preferredLanguage]/ba/b")
}
}
request.addOption("-x")
val formatSorting = StringBuilder("hasaud")
if (aCodecPref.isNotBlank()){
formatSorting.append(",acodec:$aCodecPref")
}
if(ext.isNotBlank()){
if(!ext.matches("(webm)|(Default)|(${context.getString(R.string.defaultValue)})".toRegex()) && supportedContainers.contains(ext)){
request.addOption("--audio-format", ext)
formatSorting.append(",ext:$ext")
}
}
request.addOption("-P", downDir.absolutePath)
request.addOption("-S", formatSorting.toString())
if (downloadItem.audioPreferences.splitByChapters && downloadItem.downloadSections.isBlank()){
request.addOption("--split-chapters")
@ -1242,111 +1262,99 @@ class InfoUtil(private val context: Context) {
}
//format logic
var videof = downloadItem.format.format_id
var audiof = "ba"
if (downloadItem.videoPreferences.audioFormatIDs.isNotEmpty()){
audiof = downloadItem.videoPreferences.audioFormatIDs.joinToString("+")
}
if (downloadItem.videoPreferences.removeAudio) audiof = ""
var videoF = downloadItem.format.format_id
var audioF = downloadItem.videoPreferences.audioFormatIDs.joinToString("+").ifBlank { "ba" }
val preferredAudioLanguage = sharedPreferences.getString("audio_language", "")!!
if (downloadItem.videoPreferences.removeAudio) audioF = ""
val defaultFormats = context.resources.getStringArray(R.array.video_formats_values)
val usingGenericFormat = defaultFormats.contains(videof) || downloadItem.allFormats.isEmpty() || downloadItem.allFormats == getGenericVideoFormats(context.resources)
val f = StringBuilder()
if(!usingGenericFormat){
if (audiof.isBlank()){
f.append("$videof/bv/best")
}else{
f.append("$videof+$audiof/")
if (preferredAudioCodec.isNotBlank())
f.append("$videof+$aCodecPref/")
f.append("$videof/best")
val defaultFormats = context.resources.getStringArray(R.array.video_formats_values)
val usingGenericFormat = defaultFormats.contains(videoF) || downloadItem.allFormats.isEmpty() || downloadItem.allFormats == getGenericVideoFormats(context.resources)
if (!usingGenericFormat){
if (audioF.isNotBlank()) audioF = "+$audioF"
f.append("$videoF$audioF/")
if (preferredAudioCodec.isNotBlank()){
f.append("$videoF+$aCodecPref/")
}
f.append("$videoF/bv/b")
if (audiof.contains("+")){
if (audioF.count("+") > 1){
request.addOption("--audio-multistreams")
}
}else{
if (videof == context.resources.getString(R.string.best_quality) || videof == "best") {
videof = "bv"
}else if (videof == context.resources.getString(R.string.worst_quality) || videof == "worst") {
videof = "worst"
}else if (defaultFormats.contains(videof)) {
videof = "bv[height<="+videof.split("_")[0].dropLast(1)+"]"
if (videoF == context.resources.getString(R.string.best_quality) || videoF == "best") {
videoF = "bv"
}else if (videoF == context.resources.getString(R.string.worst_quality) || videoF == "worst") {
videoF = "worst"
}else if (defaultFormats.contains(videoF)) {
videoF = "bv[height<="+videoF.split("_")[0].dropLast(1)+"]"
}
val preferredFormatIDs = sharedPreferences.getString("format_id", "").toString()
.split(",")
.filter { it.isNotEmpty() }
.ifEmpty { listOf(videof) }.toMutableList()
if (!preferredFormatIDs.contains(videof)){
preferredFormatIDs.add(0, videof)
.ifEmpty { listOf(videoF) }.toMutableList()
if (!preferredFormatIDs.contains(videoF)){
preferredFormatIDs.add(0, videoF)
}
// ^ [videoF, preferredID1, preferredID2...]
val preferredAudioFormatIDs = sharedPreferences.getString("format_id_audio", "")
.toString()
.split(",")
.filter { it.isNotEmpty() }
.filter { it.isNotBlank() }
.ifEmpty {
val list = mutableListOf<String>()
if (preferredAudioCodec.isNotBlank()) list.add("ba[acodec~='^($preferredAudioCodec)']")
list.add(audiof)
if (preferredAudioLanguage.isNotEmpty()) list.add("ba[language=$preferredAudioLanguage]")
list.add(audioF)
list
}
}.apply {
val preferredAudioFormats = if (downloadItem.videoPreferences.audioFormatIDs.isEmpty()) {
preferredAudioFormatIDs
} else listOf(audiof)
if (preferredAudioFormats.any{it.contains("+")}){
}.toMutableList()
if (!preferredAudioFormatIDs.contains(audioF) && audioF != "ba"){
preferredAudioFormatIDs.add(0, audioF)
}
// ^ [audioF, preferredAID1, preferredAID2, ....]
if (preferredAudioFormatIDs.any{it.contains("+")}){
request.addOption("--audio-multistreams")
}
val preferredCodec = sharedPreferences.getString("video_codec", "")
val extPref = if (cont.isNotBlank()) "[ext=$cont]" else ""
val vCodecPref = if (preferredCodec!!.isNotBlank()) "[vcodec~='^($preferredCodec)']" else ""
val vCodecPrefIndex = context.getStringArray(R.array.video_codec_values).indexOf(preferredCodec)
val vCodecPref = context.getStringArray(R.array.video_codec_values_ytdlp)[vCodecPrefIndex]
preferredFormatIDs.forEach { v ->
//build format with extension and vcodec
preferredAudioFormats.forEach { a ->
preferredAudioFormatIDs.forEach { a ->
val aa = if (a.isNotBlank()) "+$a" else ""
f.append("$v$extPref$vCodecPref$aa/")
f.append("$v$aa/")
}
//build format with vcodec
if (extPref.isNotBlank()){
preferredAudioFormats.forEach { a ->
val aa = if (a.isNotBlank()) "+$a" else ""
f.append("$v$vCodecPref$aa/")
}
}
//build format with audio
if (vCodecPref.isNotBlank()){
preferredAudioFormats.forEach {a ->
val aa = if (a.isNotBlank()) "+$a" else ""
f.append("$v$aa/")
}
}
//build format with just videoformatid and audio if remove audio is not checked
if (!downloadItem.videoPreferences.removeAudio){
//build format with audio with preferred language
if (preferredAudioLanguage.isNotBlank()){
val al = "$v+ba[language=$preferredAudioLanguage]/"
if (!f.contains(al)) f.append(al)
}
//build format with best audio
if(!f.contains("$v+ba/")){
f.append("$v+ba/")
}
//build formats with standalone video
if(!f.contains("/$v/")){
f.append("$v/")
}
if (!f.contains("$v+ba/")) f.append("$v+ba/")
//build format with standalone video
f.append("$v/")
}
}
if(!f.endsWith("best/")){
if(!f.endsWith("b/")){
//last fallback
f.append("best")
f.append("b")
}
val formatSorting = StringBuilder("+hasaud")
if (vCodecPref.isNotBlank()) formatSorting.append(",vcodec:$vCodecPref")
if (aCodecPref.isNotBlank()) formatSorting.append(",acodec:$aCodecPref")
if (cont.isNotBlank()) formatSorting.append(",ext:$cont")
request.addOption("-S", formatSorting.toString())
}
@ -1404,6 +1412,18 @@ class InfoUtil(private val context: Context) {
return request
}
fun getPipedInstances() : List<String> {
kotlin.runCatching {
val res = genericArrayRequest("https://piped-instances.kavin.rocks/")
val list = mutableListOf<String>()
for (i in 0 until res.length()) {
val element = res.getJSONObject(i)
list.add(element.getString("api_url"))
}
return list
}
return listOf()
}
fun parseYTDLRequestString(request : YoutubeDLRequest) : String {
val arr = request.buildCommand().toMutableList()

View file

@ -35,6 +35,8 @@ class NotificationUtil(var context: Context) {
private val workerNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_WORKER_CHANNEL_ID)
private val commandDownloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID)
private val finishedDownloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_FINISHED_CHANNEL_ID)
private val erroredDownloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_ERRORED_CHANNEL_ID)
private val notificationManager: NotificationManager = context.getSystemService(NotificationManager::class.java)
private val resources: Resources = context.resources
@ -68,7 +70,7 @@ class NotificationUtil(var context: Context) {
channel.description = description
notificationManager.createNotificationChannel(channel)
//finished or errored downloads
//finished downloads
name = resources.getString(R.string.finished_download_notification_channel_name)
description =
resources.getString(R.string.finished_download_notification_channel_description)
@ -76,7 +78,7 @@ class NotificationUtil(var context: Context) {
channel.description = description
notificationManager.createNotificationChannel(channel)
//finished or errored downloads
//errored downloads
name = resources.getString(R.string.errored_downloads)
description =
resources.getString(R.string.errored_download_notification_channel_description)
@ -99,6 +101,7 @@ class NotificationUtil(var context: Context) {
COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID -> { return commandDownloadNotificationBuilder }
DOWNLOAD_FINISHED_CHANNEL_ID -> { return finishedDownloadNotificationBuilder }
DOWNLOAD_WORKER_CHANNEL_ID -> { return workerNotificationBuilder }
DOWNLOAD_ERRORED_CHANNEL_ID -> { return erroredDownloadNotificationBuilder }
}
return downloadNotificationBuilder
}
@ -554,11 +557,11 @@ class NotificationUtil(var context: Context) {
const val DOWNLOAD_MISC_CHANNEL_ID = "4"
const val DOWNLOAD_ERRORED_CHANNEL_ID = "6"
const val DOWNLOAD_FINISHED_NOTIFICATION_ID = 3
const val DOWNLOAD_RESUME_NOTIFICATION_ID = 40000
const val DOWNLOAD_UPDATING_NOTIFICATION_ID = 5
const val FORMAT_UPDATING_FINISHED_NOTIFICATION_ID = 7
const val DOWNLOAD_ERRORED_NOTIFICATION_ID = 6
const val DOWNLOAD_FINISHED_NOTIFICATION_ID = 30000
const val DOWNLOAD_RESUME_NOTIFICATION_ID = 40000
const val DOWNLOAD_UPDATING_NOTIFICATION_ID = 50000
const val DOWNLOAD_ERRORED_NOTIFICATION_ID = 60000
const val FORMAT_UPDATING_FINISHED_NOTIFICATION_ID = 70000
private const val PROGRESS_MAX = 100
private const val PROGRESS_CURR = 0

View file

@ -8,9 +8,6 @@ import android.content.ClipboardManager
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.OvalShape
import android.net.Uri
import android.os.Build
import android.text.Editable
@ -19,7 +16,6 @@ import android.text.format.DateFormat
import android.util.DisplayMetrics
import android.util.Log
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.Window
@ -36,13 +32,12 @@ import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.FileProvider
import androidx.core.view.children
import androidx.core.view.isNotEmpty
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.CommandTemplate
@ -57,6 +52,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.ui.downloadcard.ConfigureDownloadBottomSheetDialog
import com.deniscerri.ytdlnis.ui.downloadcard.VideoCutListener
import com.deniscerri.ytdlnis.util.Extensions.enableTextHighlight
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton
@ -69,23 +65,19 @@ import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.materialswitch.MaterialSwitch
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputLayout
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import com.neo.highlight.core.Highlight
import com.neo.highlight.util.listener.HighlightTextWatcher
import com.neo.highlight.util.scheme.ColorScheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import me.zhanghai.android.fastscroll.FastScrollerBuilder
import java.io.File
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import java.util.regex.Pattern
object UiUtil {
@ -481,10 +473,18 @@ object UiUtil {
val bottomSheet = BottomSheetDialog(context)
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet.setContentView(R.layout.history_item_details_bottom_sheet)
val title = bottomSheet.findViewById<TextView>(R.id.bottom_sheet_title)
title!!.text = item.title.ifEmpty { "`${context.getString(R.string.defaultValue)}`" }
val author = bottomSheet.findViewById<TextView>(R.id.bottom_sheet_author)
author!!.text = item.author.ifEmpty { "`${context.getString(R.string.defaultValue)}`" }
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_title)?.apply {
text = item.title.ifEmpty { "`${context.getString(R.string.defaultValue)}`" }
setOnClickListener{
showFullTextDialog(context, text.toString(), context.getString(R.string.title))
}
}
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_author)?.apply {
text = item.author.ifEmpty { "`${context.getString(R.string.defaultValue)}`" }
setOnClickListener{
showFullTextDialog(context, text.toString(), context.getString(R.string.author))
}
}
// BUTTON ----------------------------------
val btn = bottomSheet.findViewById<FloatingActionButton>(R.id.download_button_type)
@ -507,6 +507,7 @@ object UiUtil {
val container = bottomSheet.findViewById<Chip>(R.id.container_chip)
val codec = bottomSheet.findViewById<Chip>(R.id.codec)
val fileSize = bottomSheet.findViewById<Chip>(R.id.file_size)
val command = bottomSheet.findViewById<Chip>(R.id.command)
when(status){
DownloadRepository.Status.Queued, DownloadRepository.Status.QueuedPaused -> {
@ -527,9 +528,13 @@ object UiUtil {
}
}
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
View.GONE
else formatNote!!.text = item.format.format_note
if (item.type != DownloadViewModel.Type.command){
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
View.GONE
else formatNote!!.text = item.format.format_note
}else{
formatNote?.isVisible = false
}
if (item.format.container != "") {
container!!.text = item.format.container.uppercase()
@ -557,6 +562,12 @@ object UiUtil {
if (fileSizeReadable == "?") fileSize!!.visibility = View.GONE
else fileSize!!.text = fileSizeReadable
val infoUtil = InfoUtil(context)
command?.setOnClickListener {
showGeneratedCommand(context, infoUtil.parseYTDLRequestString(infoUtil.buildYoutubeDLRequest(item)))
}
val link = bottomSheet.findViewById<Button>(R.id.bottom_sheet_link)
val url = item.url
link!!.text = url
@ -631,16 +642,25 @@ object UiUtil {
val bottomSheet = BottomSheetDialog(context)
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet.setContentView(R.layout.history_item_details_bottom_sheet)
val title = bottomSheet.findViewById<TextView>(R.id.bottom_sheet_title)
title!!.text = item!!.title
val author = bottomSheet.findViewById<TextView>(R.id.bottom_sheet_author)
author!!.text = item.author
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_title)?.apply {
text = item!!.title
setOnClickListener{
showFullTextDialog(context, text.toString(), context.getString(R.string.title))
}
}
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_author)?.apply {
text = item!!.author
setOnClickListener{
showFullTextDialog(context, text.toString(), context.getString(R.string.author))
}
}
// BUTTON ----------------------------------
val btn = bottomSheet.findViewById<FloatingActionButton>(R.id.download_button_type)
val typeImageResource: Int =
if (item.type == DownloadViewModel.Type.audio) {
if (item!!.type == DownloadViewModel.Type.audio) {
if (isPresent) {
R.drawable.ic_music_downloaded
} else {
@ -668,6 +688,7 @@ object UiUtil {
val container = bottomSheet.findViewById<Chip>(R.id.container_chip)
val codec = bottomSheet.findViewById<TextView>(R.id.codec)
val fileSize = bottomSheet.findViewById<TextView>(R.id.file_size)
val command = bottomSheet.findViewById<Chip>(R.id.command)
val file = File(item.downloadPath)
val calendar = Calendar.getInstance()
@ -675,9 +696,13 @@ object UiUtil {
time!!.text = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault()).format(calendar.time)
time.isClickable = false
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
View.GONE
else formatNote!!.text = item.format.format_note
if (item.type != DownloadViewModel.Type.command){
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
View.GONE
else formatNote!!.text = item.format.format_note
}else{
formatNote?.isVisible = false
}
if (item.format.container != "") {
container!!.text = if (file.exists()) file.extension.uppercase() else item.format.container.uppercase()
@ -705,6 +730,11 @@ object UiUtil {
if (fileSizeReadable == "?") fileSize!!.visibility = View.GONE
else fileSize!!.text = fileSizeReadable
command?.setOnClickListener {
showGeneratedCommand(context, item.command)
}
val link = bottomSheet.findViewById<Button>(R.id.bottom_sheet_link)
val url = item.url
link!!.text = url
@ -770,29 +800,44 @@ object UiUtil {
val fpsParent = bottomSheet.findViewById<ConstraintLayout>(R.id.fps_parent)
val asrParent = bottomSheet.findViewById<ConstraintLayout>(R.id.asr_parent)
val clicker = View.OnClickListener {
copyToClipboard(((it as ConstraintLayout).getChildAt(1) as TextView).text.toString(), activity)
}
val longClicker = View.OnLongClickListener {
val txt = ((it as ConstraintLayout).getChildAt(1) as TextView).text.toString()
val snackbar = Snackbar.make(bottomSheet.findViewById(android.R.id.content)!!, txt, Snackbar.LENGTH_LONG)
snackbar.setAction(android.R.string.copy){
copyToClipboard(txt, activity)
}
val snackbarView: View = snackbar.view
val snackTextView = snackbarView.findViewById<View>(com.google.android.material.R.id.snackbar_text) as TextView
snackTextView.maxLines = 9999999
snackbar.show()
true
}
if (format.format_id.isBlank()) formatIdParent?.visibility = View.GONE
else {
formatIdParent?.findViewById<TextView>(R.id.format_id_value)?.text = format.format_id
formatIdParent?.setOnClickListener {
copyToClipboard(format.format_id, activity)
}
formatIdParent?.setOnClickListener(clicker)
formatIdParent?.setOnLongClickListener(longClicker)
}
if (format.url.isNullOrBlank()) formatURLParent?.visibility = View.GONE
else {
formatURLParent?.findViewById<TextView>(R.id.format_url_value)?.text = format.url
formatURLParent?.setOnClickListener {
copyToClipboard(format.url!!, activity)
}
formatURLParent?.setOnClickListener(clicker)
formatURLParent?.setOnLongClickListener(longClicker)
}
if (format.container.isBlank()) containerParent?.visibility = View.GONE
else {
containerParent?.findViewById<TextView>(R.id.container_value)?.text = format.container
containerParent?.setOnClickListener {
copyToClipboard(format.container, activity)
}
containerParent?.setOnClickListener(clicker)
containerParent?.setOnLongClickListener(longClicker)
}
val codecField =
@ -807,41 +852,36 @@ object UiUtil {
if (codecField.isBlank()) codecParent?.visibility = View.GONE
else {
codecParent?.findViewById<TextView>(R.id.codec_value)?.text = codecField
codecParent?.setOnClickListener {
copyToClipboard(codecField, activity)
}
codecParent?.setOnClickListener(clicker)
codecParent?.setOnLongClickListener(longClicker)
}
if (format.filesize != 0L) filesizeParent?.visibility = View.GONE
else {
filesizeParent?.findViewById<TextView>(R.id.filesize_value)?.text = FileUtil.convertFileSize(format.filesize)
filesizeParent?.setOnClickListener {
copyToClipboard(FileUtil.convertFileSize(format.filesize), activity)
}
filesizeParent?.setOnClickListener(clicker)
filesizeParent?.setOnLongClickListener(longClicker)
}
if (format.format_note.isBlank()) formatnoteParent?.visibility = View.GONE
else {
formatnoteParent?.findViewById<TextView>(R.id.format_note_value)?.text = format.format_note
formatnoteParent?.setOnClickListener {
copyToClipboard(format.format_note, activity)
}
formatnoteParent?.setOnClickListener(clicker)
formatnoteParent?.setOnLongClickListener(longClicker)
}
if (format.fps.isNullOrBlank() || format.fps == "0") fpsParent?.visibility = View.GONE
else {
fpsParent?.findViewById<TextView>(R.id.fps_value)?.text = format.fps
fpsParent?.setOnClickListener {
copyToClipboard(format.fps!!, activity)
}
fpsParent?.setOnClickListener(clicker)
fpsParent?.setOnLongClickListener(longClicker)
}
if (format.asr.isNullOrBlank()) asrParent?.visibility = View.GONE
else {
asrParent?.findViewById<TextView>(R.id.asr_value)?.text = format.asr
asrParent?.setOnClickListener {
copyToClipboard(format.asr!!, activity)
}
asrParent?.setOnClickListener(clicker)
asrParent?.setOnLongClickListener(longClicker)
}
@ -1627,12 +1667,9 @@ object UiUtil {
tmp.setOnClickListener {
val c = it as Chip
if(!c.isChecked){
editText.setText(editText.text.toString().replace(c.text.toString(), ""))
editText.setSelection(editText.text.length)
}else{
editText.append(c.text)
}
val selectionStart = editText.selectionStart
editText.text.insert(selectionStart, c.text.toString())
editText.setSelection(selectionStart + c.text.length)
}
chips.add(tmp)
@ -1643,9 +1680,138 @@ object UiUtil {
it.isChecked = editText.text.contains(it.text)
chipGroup!!.addView(it)
}
editText.doOnTextChanged { text, start, before, count ->
chips.forEach {
it.isChecked = editText.text.contains(it.text)
}
}
}
}
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).gravity = Gravity.START
}
fun showPipedInstancesDialog(context: Activity, currentInstance: String, instanceSelected: (f: String) -> Unit){
val builder = MaterialAlertDialogBuilder(context)
builder.setTitle(context.getString(R.string.piped_instance))
val view = context.layoutInflater.inflate(R.layout.filename_template_dialog, null)
val editText = view.findViewById<EditText>(R.id.filename_edittext)
view.findViewById<TextInputLayout>(R.id.filename).hint = context.getString(R.string.piped_instance)
editText.setText(currentInstance)
editText.setSelection(editText.text.length)
builder.setView(view)
builder.setPositiveButton(
context.getString(R.string.ok)
) { _: DialogInterface?, _: Int ->
instanceSelected(editText.text.toString())
}
// handle the negative button of the alert dialog
builder.setNegativeButton(
context.getString(R.string.cancel)
) { _: DialogInterface?, _: Int -> }
builder.setNeutralButton("?") { _: DialogInterface?, _: Int ->
val browserIntent =
Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/TeamPiped/Piped/wiki/Instances"))
context.startActivity(browserIntent)
}
view.findViewById<View>(R.id.suggested).visibility = View.GONE
val dialog = builder.create()
dialog.show()
val imm = context.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
editText!!.postDelayed({
editText.requestFocus()
imm.showSoftInput(editText, 0)
}, 300)
//handle suggestion chips
CoroutineScope(Dispatchers.IO).launch {
val chipGroup = view.findViewById<ChipGroup>(R.id.filename_suggested_chipgroup)
val chips = mutableListOf<Chip>()
val instances = InfoUtil(context).getPipedInstances()
instances.forEach { s ->
val tmp = context.layoutInflater.inflate(R.layout.filter_chip, chipGroup, false) as Chip
tmp.text = s
tmp.setOnClickListener {
val c = it as Chip
c.toggle()
editText.setText(c.text.toString())
editText.setSelection(c.text.length)
}
chips.add(tmp)
}
withContext(Dispatchers.Main){
view.findViewById<View>(R.id.suggested).visibility = View.VISIBLE
chips.forEach {
it.isChecked = editText.text.contains(it.text)
chipGroup!!.addView(it)
}
editText.doOnTextChanged { text, start, before, count ->
chips.forEach {
it.isChecked = editText.text == it.text
}
}
}
}
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).gravity = Gravity.START
}
private fun showGeneratedCommand(context: Activity, command: String){
val builder = MaterialAlertDialogBuilder(context)
builder.setTitle(context.getString(R.string.command))
val view = context.layoutInflater.inflate(R.layout.command_dialog, null)
val text = view.findViewById<TextView>(R.id.commandText)
text.text = command
text.isLongClickable = true
text.setTextIsSelectable(true)
text.enableTextHighlight()
builder.setView(view)
builder.setPositiveButton(
context.getString(android.R.string.copy)
) { _: DialogInterface?, _: Int ->
copyToClipboard(command, context)
}
// handle the negative button of the alert dialog
builder.setNegativeButton(
context.getString(R.string.cancel)
) { _: DialogInterface?, _: Int -> }
val dialog = builder.create()
dialog.show()
}
private fun showFullTextDialog(context: Activity, txt: String, title: String){
val builder = MaterialAlertDialogBuilder(context)
builder.setTitle(title)
val view = context.layoutInflater.inflate(R.layout.command_dialog, null)
val text = view.findViewById<TextView>(R.id.commandText)
text.text = txt
text.isLongClickable = true
text.setTextIsSelectable(true)
builder.setView(view)
builder.setPositiveButton(
context.getString(android.R.string.copy)
) { _: DialogInterface?, _: Int ->
copyToClipboard(txt, context)
}
// handle the negative button of the alert dialog
builder.setNegativeButton(
context.getString(R.string.cancel)
) { _: DialogInterface?, _: Int -> }
val dialog = builder.create()
dialog.show()
}
}

View file

@ -91,9 +91,8 @@ class TerminalDownloadWorker(
val logItem = LogItem(
0,
"Terminal Download",
"Downloading:\n" +
"Terminal Download\n" +
"Terminal Task",
"Terminal Task\n" +
"Command: ${command}\n\n",
Format(),
DownloadViewModel.Type.command,

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:padding="10dp"
android:layout_height="match_parent">
<TextView
android:id="@+id/commandText"
android:layout_width="match_parent"
android:focusable="true"
android:clickable="true"
android:textIsSelectable="true"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -31,7 +31,8 @@
android:id="@+id/bottom_sheet_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:maxLines="3"
android:ellipsize="end"
android:textColor="?attr/colorOnSurface"
android:textSize="25sp" />
@ -40,7 +41,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:text="@string/app_name"
android:maxLines="2"
android:ellipsize="end"
android:textSize="15sp" />
</LinearLayout>
@ -139,6 +141,20 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.chip.Chip
android:id="@+id/command"
style="@style/Widget.Material3.FloatingActionButton.Large.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:minWidth="30dp"
app:cornerRadius="10dp"
android:text="@string/command"
app:chipIcon="@drawable/ic_baseline_keyboard_arrow_right_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</com.google.android.material.chip.ChipGroup>
</HorizontalScrollView>

View file

@ -32,7 +32,7 @@
<string name="version">الإصدار</string>
<string name="update_app">التحقق من وجود تحديثات</string>
<string name="command_directory">مجلد الأوامر المخصصة</string>
<string name="couldnt_find_apk">لم يتم العثور على APK صالح.</string>
<string name="couldnt_find_apk">لم يتم العثور على APK صالح</string>
<string name="best_quality">أفضل جودة متاحة</string>
<string name="downloading">التحميل</string>
<string name="limit_rate">حد السرعة</string>

View file

@ -14,7 +14,7 @@
<string name="update_ytdl">yt-dlp yeni versiyasın quraşdır</string>
<string name="source_code">Mənbə Kodu</string>
<string name="search_hint">Axtar və ya URL Əlavə Et</string>
<string name="failed_download">Faylı yükləmək mümkün olmadı.</string>
<string name="failed_download">Faylı yükləmək mümkün olmadı</string>
<string name="download_notification_channel_name">Fayl Yükləmələri</string>
<string name="download_notification_channel_description">Səs və video faylların yüklənilmə irəliləyişin göstərən bildiriş</string>
<string name="you_are_in_latest_version">Ən son versiya işlədilir</string>
@ -32,7 +32,7 @@
<string name="update_app">Yeniləmələr üçün yoxla</string>
<string name="version">Versiya</string>
<string name="command_directory">Şəxsi Əmr Qovluğu</string>
<string name="couldnt_find_apk">Uyğun APK tapılmadı.</string>
<string name="couldnt_find_apk">Uyğun APK tapılmadı</string>
<string name="downloading_update">Yeni versiya yüklənilir</string>
<string name="audio">Səs</string>
<string name="video">Video</string>

View file

@ -5,7 +5,7 @@
<string name="download_notification_channel_name">Preuzimanja fajlova</string>
<string name="update_ytdl">Instaliraj novu verziju yt-dlp(a)</string>
<string name="source_code">Izvorni kod</string>
<string name="failed_download">Ne može se preuzeti fajl.</string>
<string name="failed_download">Ne može se preuzeti fajl</string>
<string name="search">Pretraga</string>
<string name="remove_all">Ukloniti sve</string>
<string name="remove_results">Obriši rezultate</string>

View file

@ -12,7 +12,7 @@
<string name="updating">Aktualizuji</string>
<string name="home">Domů</string>
<string name="remove_results">Odstranit výsledky</string>
<string name="failed_download">Soubor se nepodařilo stáhnout.</string>
<string name="failed_download">Soubor se nepodařilo stáhnout</string>
<string name="search">Hledat</string>
<string name="settings">Nastavení</string>
<string name="worst_quality">Nejhorší kvalita</string>
@ -24,7 +24,7 @@
<string name="cancel_download">Zrušit stahování</string>
<string name="audio_quality">Kvalita audia</string>
<string name="audio_format">Audio formát</string>
<string name="couldnt_find_apk">Nepodařilo se najít vhodný APK soubor.</string>
<string name="couldnt_find_apk">Nepodařilo se najít vhodný APK soubor</string>
<string name="ytdl_updating_started">Stahování nové verze</string>
<string name="command_download_notification_channel_description">Upozornění ukazující stahování za pomoci yt-dlp příkazu</string>
<string name="add_chapters">Kapitoly ve videích</string>

View file

@ -27,7 +27,7 @@
<string name="update_app">Nach Aktualisierungen suchen</string>
<string name="version">Version</string>
<string name="command_directory">Benutzerdefinierter Befehlsordner</string>
<string name="couldnt_find_apk">Es konnte keine passende APK gefunden werden.</string>
<string name="couldnt_find_apk">Es konnte keine passende APK gefunden werden</string>
<string name="audio">Audio</string>
<string name="video">Video</string>
<string name="add_chapters_summary">Markiere YouTube-/SponsorBlock-Segmente als Kapitel für das Video</string>
@ -61,7 +61,7 @@
<string name="delete_files_too">Lösche die Dateien vom Gerät</string>
<string name="search">Suchen</string>
<string name="home">Startseite</string>
<string name="failed_download">Datei konnte nicht heruntergeladen werden.</string>
<string name="failed_download">Datei konnte nicht heruntergeladen werden</string>
<string name="download_notification_channel_name">Datei-Downloads</string>
<string name="you_are_in_latest_version">Du verwendest die neueste Version</string>
<string name="downloading_update">Neuste Version wird heruntergeladen</string>

View file

@ -127,7 +127,7 @@
<string name="about">Περί</string>
<string name="source_code">Πηγαίος Κώδικας</string>
<string name="search_hint">Αναζήτηση ή Εισαγωγή URL</string>
<string name="failed_download">Αδύνατη η λήψη του αρχείου.</string>
<string name="failed_download">Αδύνατη η λήψη του αρχείου</string>
<string name="download_notification_channel_name">Λήψεις Αρχείων</string>
<string name="download_notification_channel_description">Ειδοποίηση που δείχνει την πρόοδο της λήψης αρχείων ήχου και εικόνας</string>
<string name="ytld_update_success">Νέα έκδοση εγκαταστάθηκε</string>
@ -141,7 +141,7 @@
<string name="more">Περισσότερα</string>
<string name="update_app">Έλεγχος για ενημερώσεις</string>
<string name="command_directory">Φάκελος Προσαρμοσμένων Εντολών</string>
<string name="couldnt_find_apk">Δεν ήταν δυνατή η εύρεση κατάλληλου APK.</string>
<string name="couldnt_find_apk">Δεν ήταν δυνατή η εύρεση κατάλληλου APK</string>
<string name="downloading_update">Λήψη νέας έκδοσης…</string>
<string name="audio">Ήχος</string>
<string name="video">Βίντεο</string>

View file

@ -12,7 +12,7 @@
<string name="about">Acerca de</string>
<string name="source_code">Código Fuente</string>
<string name="search_hint">Buscar o Insertar URL</string>
<string name="failed_download">No se pudo descargar el archivo.</string>
<string name="failed_download">No se pudo descargar el archivo</string>
<string name="download_notification_channel_name">Descargas de Archivos</string>
<string name="download_notification_channel_description">Notificación que muestra el progreso de las descargas de archivos de audio y video</string>
<string name="you_are_in_latest_version">Estás usando la última versión</string>
@ -28,7 +28,7 @@
<string name="update_app">Buscar actualizaciones</string>
<string name="version">Versión</string>
<string name="command_directory">Directorio de Comandos Personalizados</string>
<string name="couldnt_find_apk">No se pudo encontrar un instalador APK correcto.</string>
<string name="couldnt_find_apk">No se pudo encontrar un instalador APK correcto</string>
<string name="downloading_update">Descargando una nueva versión</string>
<string name="audio">Audio</string>
<string name="video">Video</string>

View file

@ -14,7 +14,7 @@
<string name="ytdl_update_hint">Utile si vous rencontrez des problèmes de téléchargement</string>
<string name="source_code">Code source</string>
<string name="search_hint">Rechercher sur YouTube ou insérer un URL</string>
<string name="failed_download">Impossible de télécharger le fichier.</string>
<string name="failed_download">Impossible de télécharger le fichier</string>
<string name="download_notification_channel_name">Téléchargements de fichiers</string>
<string name="download_notification_channel_description">Notification indiquant la progression du téléchargement des fichiers audio et vidéo</string>
<string name="you_are_in_latest_version">Utiliser la dernière version</string>
@ -32,7 +32,7 @@
<string name="update_app">Vérifier les mises à jour</string>
<string name="version">Version</string>
<string name="command_directory">Dossier pour la commande personnalisée</string>
<string name="couldnt_find_apk">Impossible de trouver un APK approprié.</string>
<string name="couldnt_find_apk">Impossible de trouver un APK approprié</string>
<string name="downloading_update">Téléchargement de la nouvelle version…</string>
<string name="audio">Audio</string>
<string name="video">Vidéo</string>

View file

@ -11,7 +11,7 @@
<string name="audio_format">Audio formátum</string>
<string name="adjust_audio">Adjust Audio</string>
<string name="music_directory">Zene mappa</string>
<string name="couldnt_find_apk">Nem találhat megfelelő APK-t.</string>
<string name="couldnt_find_apk">Nem találhat megfelelő APK-t</string>
<string name="ytdl_updating_started">Új verzió letöltése</string>
<string name="new_template">Új sablon</string>
<string name="update_ytdl_nightly">Az yt-dlp éjszakai verziója</string>
@ -77,7 +77,7 @@
<string name="download_notification_channel_name">Fájl letöltve</string>
<string name="remove_results">Minden eredmény törlése</string>
<string name="adjust_templates">Adjust sablonok</string>
<string name="failed_download">Nem lehet letölteni a fájlt.</string>
<string name="failed_download">Nem lehet letölteni a fájlt</string>
<string name="edit_selected">Edit kiválasztott</string>
<string name="sponsorblock_api_url">Sponsorblock API URL</string>
<string name="update_app">Frissítés keresése</string>

View file

@ -11,7 +11,7 @@
<string name="download_all">Unduh Semua</string>
<string name="settings">Setelan</string>
<string name="search">Telusuri</string>
<string name="failed_download">Tidak bisa mengunduh berkas.</string>
<string name="failed_download">Tidak bisa mengunduh berkas</string>
<string name="download_notification_channel_name">Unduhan Berkas</string>
<string name="download_notification_channel_description">Notifikasi yang memperlihatkan kemajuan pengunduhan berkas audio dan video</string>
<string name="home">Beranda</string>
@ -35,7 +35,7 @@
<string name="command_directory">Folder Perintah Kustom</string>
<string name="downloading">Mengunduh</string>
<string name="video">Video</string>
<string name="couldnt_find_apk">Tidak bisa menemukan APK yang cocok.</string>
<string name="couldnt_find_apk">Tidak bisa menemukan APK yang cocok</string>
<string name="audio">Audio</string>
<string name="concurrent_fragments_summary">Jumlah fragmen sebuah video DASH/HLS asal untuk diunduh secara berbarengan</string>
<string name="limit_rate_summary">Laju unduhan maksimal (dalam satuan bita per detik), mis. 50K atau 4,2M</string>

View file

@ -34,7 +34,7 @@
<string name="save_thumb">Salva miniatura</string>
<string name="update_ytdl">Installa nuova versione di yt-dlp</string>
<string name="search_hint">Cerca o inserisci URL</string>
<string name="failed_download">Impossibile scaricare il file.</string>
<string name="failed_download">Impossibile scaricare il file</string>
<string name="download_notification_channel_description">Notifica che mostra l\'avanzamento del download di file audio e video</string>
<string name="download_notification_channel_name">Download di file</string>
<string name="you_are_in_latest_version">Stai utilizzando l\'ultima versione</string>
@ -50,7 +50,7 @@
<string name="more">Di più</string>
<string name="update_app">Controlla gli aggiornamenti</string>
<string name="command_directory">Cartella comandi personalizzata</string>
<string name="couldnt_find_apk">Impossibile trovare un APK adatto.</string>
<string name="couldnt_find_apk">Impossibile trovare un APK adatto</string>
<string name="audio">Audio</string>
<string name="video">Video</string>
<string name="concurrent_fragments">Frammenti Concorrenti</string>

View file

@ -10,7 +10,7 @@
<string name="title">כותרת</string>
<string name="music_directory">תיקיית המוזיקה</string>
<string name="warning">אזהרה</string>
<string name="couldnt_find_apk">לא מצליח למצוא יישום מתאים.</string>
<string name="couldnt_find_apk">לא מצליח למצוא יישום מתאים</string>
<string name="ytdl_updating_started">מוריד גירסה חדשה</string>
<string name="concurrent_downloads_summary">מספר הורדות בו זמנית</string>
<string name="embed_subtitles">כתוביות מוטמעות</string>
@ -60,7 +60,7 @@
<string name="delete_files_too">מחק את הקבצים מהמכשיר</string>
<string name="remove_results">הסר תוצאות חיפוש</string>
<string name="download_card_summary">הגדרות איך להוריד פריטים</string>
<string name="failed_download">אין אפשרות להוריד את הקובץ.</string>
<string name="failed_download">אין אפשרות להוריד את הקובץ</string>
<string name="edit_selected">ערוך נבחרים</string>
<string name="update_app">חפש עדכונים</string>
<string name="search">חיפוש</string>

View file

@ -9,7 +9,7 @@
<string name="music_directory">音楽のフォルダ</string>
<string name="video_directory">動画のフォルダ</string>
<string name="update_ytdl">最新の yt-dlp をインストール</string>
<string name="failed_download">ファイルをダウンロードできませんでした</string>
<string name="failed_download">ファイルをダウンロードできませんでした</string>
<string name="download_notification_channel_name">ファイルのダウンロード</string>
<string name="you_are_in_latest_version">最新版を使用中です</string>
<string name="ytld_update_success">最新版をインストールしました</string>
@ -23,7 +23,7 @@
<string name="more">詳細</string>
<string name="version">バージョン</string>
<string name="command_directory">コマンド指定用のフォルダ</string>
<string name="couldnt_find_apk">適切な APK がありませんでした</string>
<string name="couldnt_find_apk">適切な APK がありませんでした</string>
<string name="downloading_update">新しいバージョンのダウンロード</string>
<string name="video">動画</string>
<string name="concurrent_fragments_summary">DASH/HLS ネイティブ動画の同時分割ダウンロード数</string>

View file

@ -13,7 +13,7 @@
<string name="dont_ask_again">Aja takon maneh</string>
<string name="music_directory">Folder musik</string>
<string name="warning">Pènget</string>
<string name="couldnt_find_apk">Ora bisa nemokake APK sing cocog.</string>
<string name="couldnt_find_apk">Ora bisa nemokake APK sing cocog</string>
<string name="ytdl_updating_started">Download versi anyar</string>
<string name="downloads_running_try_later">Ngundhuh buruh lagi mlaku. Coba maneh mengko</string>
<string name="new_template">Cithakan anyar</string>
@ -100,7 +100,7 @@
<string name="confirm_terminate">Mungkasi kabeh undhuhan lan nutup aplikasi kanthi paksa</string>
<string name="errored">Kesalahan</string>
<string name="download_card_summary">Setelan carane ndownload item</string>
<string name="failed_download">Ora bisa ngundhuh file.</string>
<string name="failed_download">Ora bisa ngundhuh file</string>
<string name="cancelled">dibatalake</string>
<string name="edit_selected">Suntingan sing dipilih</string>
<string name="log_downloads_summary">Nggawe file log kanggo saben download</string>

View file

@ -38,14 +38,14 @@
<string name="video_format">비디오 형식</string>
<string name="about">개발자 정보</string>
<string name="audio_format">오디오 형식</string>
<string name="failed_download">파일을 다운로드할 수 없습니다.</string>
<string name="failed_download">파일을 다운로드할 수 없습니다</string>
<string name="download_notification_channel_name">파일 다운로드</string>
<string name="ok">확인</string>
<string name="confirm_delete_history">확인</string>
<string name="search_history_hint">기록에서 검색</string>
<string name="no_results">결과 없음</string>
<string name="update_app">업데이트 확인</string>
<string name="couldnt_find_apk">적합한 APK를 찾을 수 없습니다.</string>
<string name="couldnt_find_apk">적합한 APK를 찾을 수 없습니다</string>
<string name="audio">오디오</string>
<string name="concurrent_fragments_summary">동시에 다운로드할 DASH/HLS 네이티브 비디오의 조각 수</string>
<string name="version">버전</string>

View file

@ -14,7 +14,7 @@
<string name="about">Par</string>
<string name="source_code">Pirmkods</string>
<string name="search_hint">Meklējiet vai Ievadiet URL</string>
<string name="failed_download">Nevar lejupielādēt failu.</string>
<string name="failed_download">Nevar lejupielādēt failu</string>
<string name="download_notification_channel_name">Failu Lejupielādes</string>
<string name="download_notification_channel_description">Paziņojums par audio un video failu lejupielādes gaitu</string>
<string name="you_are_in_latest_version">Tiek izmantota jaunākā versija</string>
@ -32,7 +32,7 @@
<string name="update_app">Pārbaudīt atjauninājumus</string>
<string name="version">Versija</string>
<string name="command_directory">Pielāgotā Komandu Mape</string>
<string name="couldnt_find_apk">Nevarēja atrast piemērotu APK.</string>
<string name="couldnt_find_apk">Nevarēja atrast piemērotu APK</string>
<string name="downloading_update">Lejupielādē jaunāko versiju…</string>
<string name="audio">Audio</string>
<string name="video">Video</string>

View file

@ -16,7 +16,7 @@
<string name="update_ytdl">Pasang versi baharu yt-dlp</string>
<string name="source_code">Kod Sumber</string>
<string name="search_hint">Cari atau Masukkan URL</string>
<string name="failed_download">Tidak boleh muat turun fail.</string>
<string name="failed_download">Tidak boleh muat turun fail</string>
<string name="you_are_in_latest_version">Sedang menggunakan versi terbaru</string>
<string name="download_notification_channel_description">Notifikasi yang menunjukkan tahap muat turun bagi fail audio dan video</string>
<string name="ytld_update_success">Versi baharu telah dipasang</string>
@ -31,7 +31,7 @@
<string name="more">Lagi</string>
<string name="update_app">Semak kewujudan kemas kini</string>
<string name="version">Versi</string>
<string name="couldnt_find_apk">Tidak dapat mencari APK yang sesuai.</string>
<string name="couldnt_find_apk">Tidak dapat mencari APK yang sesuai</string>
<string name="downloading_update">Memuat turun versi baharu...</string>
<string name="audio">Audio</string>
<string name="video">Video</string>

View file

@ -5,7 +5,7 @@
<string name="video_directory">Videomappe</string>
<string name="ytdl_update_hint">Nyttig hvis du har nedlastingsproblemer</string>
<string name="update_ytdl">Installer ny versjon av yt-dlp</string>
<string name="failed_download">Kunne ikke laste ned filen.</string>
<string name="failed_download">Kunne ikke laste ned filen</string>
<string name="download_notification_channel_name">Filnedlastinger</string>
<string name="ytld_update_success">Ny versjon installert</string>
<string name="cancel">Avbryt</string>
@ -14,7 +14,7 @@
<string name="confirm_delete_history_desc">Slett hele historikken for godt.</string>
<string name="update_app">Se etter nye versjoner av programmet</string>
<string name="command_directory">Mappe for egendefinerte kommandoer</string>
<string name="couldnt_find_apk">Fant ingen passende APK.</string>
<string name="couldnt_find_apk">Fant ingen passende APK</string>
<string name="downloading_update">Laster ned ny versjon …</string>
<string name="aria2_summary">Bruk aria2c som nedlaster (istedenfor forvalget)</string>
<string name="concurrent_fragments">Parallell nedlasting</string>

View file

@ -10,7 +10,7 @@
<string name="title">Titel</string>
<string name="music_directory">Muziek Map</string>
<string name="warning">Waarschuwing</string>
<string name="couldnt_find_apk">Kon geen geschikte APK vinden.</string>
<string name="couldnt_find_apk">Kon geen geschikte APK vinden</string>
<string name="ytdl_updating_started">Nieuwe versie aan het downloaden</string>
<string name="command_download_notification_channel_description">Melding die de voortgang laat zien van het downloaden met een yt-dlp commando</string>
<string name="add_chapters">Hoofdstukken in Videos</string>
@ -61,7 +61,7 @@
<string name="download_notification_channel_name">Bestand Downloads</string>
<string name="delete_files_too">Verwijder de bestanden van het apparaat</string>
<string name="remove_results">Verwijder Resultaten</string>
<string name="failed_download">Kan bestand niet downloaden.</string>
<string name="failed_download">Kan bestand niet downloaden</string>
<string name="update_app">Controleren op updates</string>
<string name="search">Zoeken</string>
<string name="video">Video</string>

View file

@ -152,7 +152,7 @@
<string name="music_directory">Lydmappe</string>
<string name="updating">Oppdaterer</string>
<string name="update_ytdl">Legg inn ei ny utgåve av yt-dlp</string>
<string name="failed_download">Greidde ikkje å hente fila.</string>
<string name="failed_download">Greidde ikkje å hente fila</string>
<string name="ytdl_already_updating">Legg inn den nyaste utgåva</string>
<string name="cancel">Avbryt</string>
<string name="ok">Greitt</string>
@ -160,7 +160,7 @@
<string name="command_directory">Mappe for eigne påbod</string>
<string name="audio">Lyd</string>
<string name="video">Video</string>
<string name="couldnt_find_apk">Fann ingen høvande APK.</string>
<string name="couldnt_find_apk">Fann ingen høvande APK</string>
<string name="concurrent_fragments_summary">Mengd bitar av DASH/HLS-videoar å hente samstundes</string>
<string name="downloading">Hentar</string>
<string name="limit_rate_summary">Toppsnøggleiken til hentingar (i bytar i sekundet), t.d. 50K, eller 4.2M</string>

View file

@ -33,7 +33,7 @@
<string name="exit_app">Wyjdź z aplikacji</string>
<string name="search">Szukaj</string>
<string name="directories">Foldery</string>
<string name="failed_download">Nie można pobrać pliku.</string>
<string name="failed_download">Nie można pobrać pliku</string>
<string name="ytdl_update_hint">Przydatne, jeśli masz problem z pobieraniem</string>
<string name="download_notification_channel_description">Powiadomienie pokazujące postęp pobierania plików audio i wideo</string>
<string name="you_are_in_latest_version">Korzystasz z najnowszej wersji</string>
@ -59,7 +59,7 @@
<string name="search_history_hint">Wyszukaj z historii przeglądania</string>
<string name="version">Wersja</string>
<string name="command_directory">Własny folder poleceń</string>
<string name="couldnt_find_apk">Nie można znaleźć odpowiedniego pliku APK.</string>
<string name="couldnt_find_apk">Nie można znaleźć odpowiedniego pliku APK</string>
<string name="video">Wideo</string>
<string name="audio">Audio</string>
<string name="concurrent_fragments">Jednoczesne fragmenty</string>

View file

@ -14,7 +14,7 @@
<string name="about">Sobre</string>
<string name="source_code">Código fonte</string>
<string name="search_hint">Digitar um URL ou pesquisar</string>
<string name="failed_download">Não foi possível baixar o arquivo.</string>
<string name="failed_download">Não foi possível baixar o arquivo</string>
<string name="download_notification_channel_name">Arquivos baixados</string>
<string name="you_are_in_latest_version">Você já possui a atualização mais recente</string>
<string name="ytld_update_success">Nova versão instalada</string>
@ -26,7 +26,7 @@
<string name="search_history_hint">Pesquisar no histórico</string>
<string name="no_results">Nenhum resultado</string>
<string name="command_directory">Pasta de comando personalizado</string>
<string name="couldnt_find_apk">Não foi possível encontrar o apk adequado.</string>
<string name="couldnt_find_apk">Não foi possível encontrar o apk adequado</string>
<string name="downloading_update">Baixando nova versão</string>
<string name="audio">Áudio</string>
<string name="video">Vídeo</string>

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="search_hint">Pesquisar ou inserir URL</string>
<string name="failed_download">Não foi possível descarregar o ficheiro.</string>
<string name="failed_download">Não foi possível descarregar o ficheiro</string>
<string name="ytdl_updating_started">A descarregar nova versão</string>
<string name="video">Vídeo</string>
<string name="download_notification_channel_name">Ficheiros descarregados</string>
@ -55,7 +55,7 @@
<string name="sort_by">Ordem</string>
<string name="Open_File">Abrir ficheiro</string>
<string name="download_all">Descarregar tudo</string>
<string name="couldnt_find_apk">Não foi possível encontrar um APK adequado.</string>
<string name="couldnt_find_apk">Não foi possível encontrar um APK adequado</string>
<string name="downloading_update">A descarregar nova versão</string>
<string name="audio">Áudio</string>
<string name="save_thumb">Guardar miniatura</string>

View file

@ -13,7 +13,7 @@
<string name="update_ytdl">Instalează o nouă versiune de yt-dlp</string>
<string name="source_code">Codul sursă</string>
<string name="search_hint">Caută sau Introduce adresa URL</string>
<string name="failed_download">Nu s-a putut descărca fișierul.</string>
<string name="failed_download">Nu s-a putut descărca fișierul</string>
<string name="download_notification_channel_name">Fișiere Descărcate</string>
<string name="worst_quality">Cea mai scăzută calitate</string>
<string name="search">Căutare</string>
@ -35,7 +35,7 @@
<string name="update_app">Verifică pentru actualizări</string>
<string name="version">Versiune</string>
<string name="command_directory">Dosar de comandă personalizat</string>
<string name="couldnt_find_apk">Nu s-a putut găsi un APK potrivit.</string>
<string name="couldnt_find_apk">Nu s-a putut găsi un APK potrivit</string>
<string name="downloading_update">Se descarcă versiunea nouă</string>
<string name="audio">Audio</string>
<string name="video">Video</string>

View file

@ -8,7 +8,7 @@
<string name="source_code">Исходный код</string>
<string name="search_hint">Найдите или вставьте URL-адрес</string>
<string name="update_ytdl">Установить новую версию yt-dlp</string>
<string name="failed_download">Невозможно загрузить файл.</string>
<string name="failed_download">Невозможно загрузить файл</string>
<string name="download_notification_channel_name">Загрузка файлов</string>
<string name="you_are_in_latest_version">Используется последняя версия</string>
<string name="ytld_update_success">Установлена новая версия</string>
@ -22,7 +22,7 @@
<string name="more">Больше</string>
<string name="update_app">Проверить обновления</string>
<string name="version">Версия</string>
<string name="couldnt_find_apk">Не удалось найти подходящий APK.</string>
<string name="couldnt_find_apk">Не удалось найти подходящий APK</string>
<string name="downloading_update">Загрузка новой версии</string>
<string name="audio">Аудио</string>
<string name="video">Видео</string>

View file

@ -6,7 +6,7 @@
<string name="audio_quality">Kvalita Zvuku</string>
<string name="audio_format">Formát Zvuku</string>
<string name="music_directory">Hudobný Priečinok</string>
<string name="couldnt_find_apk">Nepodarilo sa nájsť vhodné APK.</string>
<string name="couldnt_find_apk">Nepodarilo sa nájsť vhodné APK</string>
<string name="ytdl_updating_started">Sťahuje sa nová verzia</string>
<string name="command_download_notification_channel_description">Oznámenie zobrazujúce priebeh sťahovania pomocou yt-dlp príkazu</string>
<string name="add_chapters">Kapitoly vo Videách</string>
@ -45,7 +45,7 @@
<string name="home">Domov</string>
<string name="cancel">Zrušiť</string>
<string name="remove_results">Vymazať Výsledky</string>
<string name="failed_download">Nepodarilo sa stiahnuť súbor.</string>
<string name="failed_download">Nepodarilo sa stiahnuť súbor</string>
<string name="update_app">Skontrolovať aktualizácie</string>
<string name="search">Hľadať</string>
<string name="video">Video</string>

View file

@ -17,7 +17,7 @@
<string name="about">Rreth</string>
<string name="source_code">Kodi</string>
<string name="search_hint">Kërko ose Vendos URL</string>
<string name="failed_download">Dështim në shkarkim.</string>
<string name="failed_download">Dështim në shkarkim</string>
<string name="download_notification_channel_name">Shkarkimet e Skedarëve</string>
<string name="download_notification_channel_description">Njoftimi i cili tregon progresin e shkarkimeve te skedarëve audio dhe video</string>
<string name="you_are_in_latest_version">Je ne versionin e fundit</string>
@ -35,7 +35,7 @@
<string name="update_app">Kontrollo për përditësime</string>
<string name="version">Versioni</string>
<string name="command_directory">Direktoria e Komandës</string>
<string name="couldnt_find_apk">Nuk u gjet një APK e vlefshme.</string>
<string name="couldnt_find_apk">Nuk u gjet një APK e vlefshme</string>
<string name="downloading_update">Po shkarkohet versioni i ri</string>
<string name="audio">Audio</string>
<string name="video">Video</string>

View file

@ -34,7 +34,7 @@
<string name="dont_ask_again">Не питај поново</string>
<string name="music_directory">Фолдер за аудио снимке</string>
<string name="warning">Упозорење</string>
<string name="couldnt_find_apk">Није могуће пронаћи одговарајући APK.</string>
<string name="couldnt_find_apk">Није могуће пронаћи одговарајући APK</string>
<string name="video_recommendations_summary">Добијте препоручене YouTube видео снимке на почетном екрану с PIPED API-ја</string>
<string name="codec">Кодек</string>
<string name="select_backup_categories">Изаберите категорије резервне копије</string>
@ -228,7 +228,7 @@
<string name="errored">Неисправно</string>
<string name="download_card_summary">Подешавања за преузимање предмета</string>
<string name="preferred_format_id_summary">Изаберите формат са овим ID-ом на картици за преузимање</string>
<string name="failed_download">Није могуће преузети фајл.</string>
<string name="failed_download">Није могуће преузети фајл</string>
<string name="Theme">Тема</string>
<string name="cancelled">Отказано</string>
<string name="edit_selected">Измени изабрано</string>

View file

@ -14,7 +14,7 @@
<string name="update_ytdl">yt-dlp\'nin yeni sürümünü yükle</string>
<string name="source_code">Kaynak Kodu</string>
<string name="search_hint">Ara veya URL Ekle</string>
<string name="failed_download"> Dosya indirilemedi.</string>
<string name="failed_download">" Dosya indirilemedi"</string>
<string name="download_notification_channel_name">Dosya İndirmeleri</string>
<string name="download_notification_channel_description">Ses ve video dosyaların indirilmesindeki ilerlemeyi gösteren bildirim</string>
<string name="you_are_in_latest_version">Son sürümü kullanıyorsun</string>
@ -32,7 +32,7 @@
<string name="update_app">Güncellemeleri kontrol et</string>
<string name="version">Sürüm</string>
<string name="command_directory">Özel Komut Klasörü</string>
<string name="couldnt_find_apk">Uygun APK bulunamadı.</string>
<string name="couldnt_find_apk">Uygun APK bulunamadı</string>
<string name="downloading_update">Yeni sürüm indiriliyor</string>
ile sınırla
<string name="audio">Ses</string>

View file

@ -22,7 +22,7 @@
<string name="source_code">Вихідний код</string>
<string name="update_ytdl">Встановити нову версію yt-dlp</string>
<string name="about">Про застосунок</string>
<string name="failed_download">Не вдалося завантажити файл.</string>
<string name="failed_download">Не вдалося завантажити файл</string>
<string name="download_notification_channel_name">Завантаження файлів</string>
<string name="download_notification_channel_description">Сповіщення про хід завантаження аудіо- та відеофайлів</string>
<string name="exit_app">Вийти зі застосунку</string>
@ -44,7 +44,7 @@
<string name="update_app">Перевірка наявності оновлення</string>
<string name="limit_rate_summary">Максимальна швидкість завантаження (у байтах за секунду), наприклад, 50K або 4.2M</string>
<string name="version">Версія</string>
<string name="couldnt_find_apk">Не вдалося знайти відповідний APK.</string>
<string name="couldnt_find_apk">Не вдалося знайти відповідний APK</string>
<string name="limit_rate">Обмеження швидкості завантаження</string>
<string name="downloading">Завантаження</string>
<string name="downloading_update">Завантаження нової версії</string>

View file

@ -2,7 +2,7 @@
<resources>
<string name="update_ytdl">Cài đặt phiên bản mới của yt-dlp</string>
<string name="search">Tìm kiếm</string>
<string name="couldnt_find_apk">Không thể tìm thấy APK phù hợp.</string>
<string name="couldnt_find_apk">Không thể tìm thấy APK phù hợp</string>
<string name="remove_all">Loại bỏ tất cả</string>
<string name="remove_results">Dọn dẹp Kết quả</string>
<string name="download_all">Tải xuống tất cả</string>
@ -14,7 +14,7 @@
<string name="ytdl_update_hint">Hữu ích nếu bạn gặp sự cố khi tải xuống</string>
<string name="source_code">Mã nguồn</string>
<string name="search_hint">Tìm kiếm hoặc chèn URL</string>
<string name="failed_download">Không thể tải xuống tệp.</string>
<string name="failed_download">Không thể tải xuống tệp</string>
<string name="download_notification_channel_name">Tệp tải xuống</string>
<string name="you_are_in_latest_version">Sử dụng phiên bản mới nhất</string>
<string name="ytld_update_success">Phiên bản mới được cài đặt</string>

View file

@ -6,7 +6,7 @@
<string name="home">首页</string>
<string name="directories">文件夹</string>
<string name="music_directory">音乐文件夹</string>
<string name="failed_download">无法下载文件</string>
<string name="failed_download">无法下载文件</string>
<string name="download_notification_channel_name">文件下载</string>
<string name="you_are_in_latest_version">使用最新版本</string>
<string name="ytld_update_success">已安装新版本</string>
@ -44,7 +44,7 @@
<string name="add_chapters">添加视频章节</string>
<string name="cancel">取消</string>
<string name="confirm_delete_history_desc">永久删除全部历史记录</string>
<string name="couldnt_find_apk">找不到合适的APK</string>
<string name="couldnt_find_apk">找不到合适的APK</string>
<string name="downloading_update">正在下载新版本</string>
<string name="audio">音频</string>
<string name="embed_thumb">缩略图封面</string>

View file

@ -150,10 +150,10 @@
<string name="search">搜尋</string>
<string name="remove_results">清除結果</string>
<string name="download_all">全部下載</string>
<string name="failed_download">無法下載檔案</string>
<string name="failed_download">無法下載檔案</string>
<string name="you_are_in_latest_version">使用最新版本</string>
<string name="ytld_update_success">已安裝新版本</string>
<string name="couldnt_find_apk">找不到適合的 APK</string>
<string name="couldnt_find_apk">找不到適合的 APK</string>
<string name="update_app">檢查更新</string>
<string name="more">更多</string>
<string name="version">版本</string>

View file

@ -855,6 +855,14 @@
<item>hev|h265</item>
</string-array>
<string-array name="video_codec_values_ytdlp">
<item></item>
<item>av01</item>
<item>vp9</item>
<item>h264</item>
<item>h265</item>
</string-array>
<string-array name="audio_codec">
<item>@string/defaultValue</item>
@ -868,6 +876,12 @@
<item>opus</item>
</string-array>
<string-array name="audio_codec_values_ytdlp">
<item></item>
<item>aac</item>
<item>opus</item>
</string-array>
<string-array name="filename_templates">
<item>%(uploader)s___(string): Full name of the video uploader)</item>
<item>%(title)s___(string): Video title)</item>
@ -1083,7 +1097,7 @@
</array>
<array name="ytdlp_source">
<item>@string/defaultValue</item>
<item>@string/update_ytdl_stable</item>
<item>@string/update_ytdl_nightly</item>
<item>@string/update_ytdl_master</item>
</array>
@ -1095,4 +1109,226 @@
<item>master</item>
</array>
<!--below is for preferred audio language, not related to app's language-->
<string-array name="language_codes">
<item>af</item> <!-- Afrikaans -->
<item>sq</item> <!-- Albanian -->
<item>am</item> <!-- Amharic -->
<item>ar</item> <!-- Arabic -->
<item>hy</item> <!-- Armenian -->
<item>az</item> <!-- Azerbaijani -->
<item>eu</item> <!-- Basque -->
<item>be</item> <!-- Belarusian -->
<item>bn</item> <!-- Bengali -->
<item>bs</item> <!-- Bosnian -->
<item>bg</item> <!-- Bulgarian -->
<item>ca</item> <!-- Catalan -->
<item>ceb</item> <!-- Cebuano -->
<item>ny</item> <!-- Chichewa -->
<item>zh</item> <!-- Chinese (Simplified) -->
<item>co</item> <!-- Corsican -->
<item>hr</item> <!-- Croatian -->
<item>cs</item> <!-- Czech -->
<item>da</item> <!-- Danish -->
<item>nl</item> <!-- Dutch -->
<item>en</item> <!-- English -->
<item>eo</item> <!-- Esperanto -->
<item>et</item> <!-- Estonian -->
<item>tl</item> <!-- Filipino -->
<item>fi</item> <!-- Finnish -->
<item>fr</item> <!-- French -->
<item>fy</item> <!-- Frisian -->
<item>gl</item> <!-- Galician -->
<item>ka</item> <!-- Georgian -->
<item>de</item> <!-- German -->
<item>el</item> <!-- Greek -->
<item>gu</item> <!-- Gujarati -->
<item>ht</item> <!-- Haitian Creole -->
<item>ha</item> <!-- Hausa -->
<item>haw</item> <!-- Hawaiian -->
<item>iw</item> <!-- Hebrew -->
<item>he</item> <!-- Hebrew (legacy) -->
<item>hi</item> <!-- Hindi -->
<item>hmn</item> <!-- Hmong -->
<item>hu</item> <!-- Hungarian -->
<item>is</item> <!-- Icelandic -->
<item>ig</item> <!-- Igbo -->
<item>id</item> <!-- Indonesian -->
<item>ga</item> <!-- Irish -->
<item>it</item> <!-- Italian -->
<item>ja</item> <!-- Japanese -->
<item>jw</item> <!-- Javanese -->
<item>kn</item> <!-- Kannada -->
<item>kk</item> <!-- Kazakh -->
<item>km</item> <!-- Khmer -->
<item>rw</item> <!-- Kinyarwanda -->
<item>ko</item> <!-- Korean -->
<item>ku</item> <!-- Kurdish (Kurmanji) -->
<item>ky</item> <!-- Kyrgyz -->
<item>lo</item> <!-- Lao -->
<item>la</item> <!-- Latin -->
<item>lv</item> <!-- Latvian -->
<item>lt</item> <!-- Lithuanian -->
<item>lb</item> <!-- Luxembourgish -->
<item>mk</item> <!-- Macedonian -->
<item>mg</item> <!-- Malagasy -->
<item>ms</item> <!-- Malay -->
<item>ml</item> <!-- Malayalam -->
<item>mt</item> <!-- Maltese -->
<item>mi</item> <!-- Maori -->
<item>mr</item> <!-- Marathi -->
<item>mn</item> <!-- Mongolian -->
<item>my</item> <!-- Burmese -->
<item>ne</item> <!-- Nepali -->
<item>no</item> <!-- Norwegian -->
<item>or</item> <!-- Oriya -->
<item>ps</item> <!-- Pashto -->
<item>fa</item> <!-- Persian -->
<item>pl</item> <!-- Polish -->
<item>pt</item> <!-- Portuguese -->
<item>pa</item> <!-- Punjabi -->
<item>ro</item> <!-- Romanian -->
<item>ru</item> <!-- Russian -->
<item>sm</item> <!-- Samoan -->
<item>gd</item> <!-- Scots Gaelic -->
<item>sr</item> <!-- Serbian -->
<item>st</item> <!-- Sesotho -->
<item>sn</item> <!-- Shona -->
<item>sd</item> <!-- Sindhi -->
<item>si</item> <!-- Sinhalese -->
<item>sk</item> <!-- Slovak -->
<item>sl</item> <!-- Slovenian -->
<item>so</item> <!-- Somali -->
<item>es</item> <!-- Spanish -->
<item>su</item> <!-- Sundanese -->
<item>sw</item> <!-- Swahili -->
<item>sv</item> <!-- Swedish -->
<item>tg</item> <!-- Tajik -->
<item>ta</item> <!-- Tamil -->
<item>te</item> <!-- Telugu -->
<item>th</item> <!-- Thai -->
<item>tr</item> <!-- Turkish -->
<item>uk</item> <!-- Ukrainian -->
<item>ur</item> <!-- Urdu -->
<item>ug</item> <!-- Uyghur -->
<item>uz</item> <!-- Uzbek -->
<item>vi</item> <!-- Vietnamese -->
<item>cy</item> <!-- Welsh -->
<item>xh</item> <!-- Xhosa -->
<item>yi</item> <!-- Yiddish -->
<item>yo</item> <!-- Yoruba -->
<item>zu</item> <!-- Zulu -->
</string-array>
<string-array name="language_names">
<item>Afrikaans</item>
<item>Albanian</item>
<item>Amharic</item>
<item>Arabic</item>
<item>Armenian</item>
<item>Azerbaijani</item>
<item>Basque</item>
<item>Belarusian</item>
<item>Bengali</item>
<item>Bosnian</item>
<item>Bulgarian</item>
<item>Catalan</item>
<item>Cebuano</item>
<item>Chichewa</item>
<item>Chinese (Simplified)</item>
<item>Corsican</item>
<item>Croatian</item>
<item>Czech</item>
<item>Danish</item>
<item>Dutch</item>
<item>English</item>
<item>Esperanto</item>
<item>Estonian</item>
<item>Filipino</item>
<item>Finnish</item>
<item>French</item>
<item>Frisian</item>
<item>Galician</item>
<item>Georgian</item>
<item>German</item>
<item>Greek</item>
<item>Gujarati</item>
<item>Haitian Creole</item>
<item>Hausa</item>
<item>Hawaiian</item>
<item>Hebrew</item>
<item>Hebrew (legacy)</item>
<item>Hindi</item>
<item>Hmong</item>
<item>Hungarian</item>
<item>Icelandic</item>
<item>Igbo</item>
<item>Indonesian</item>
<item>Irish</item>
<item>Italian</item>
<item>Japanese</item>
<item>Javanese</item>
<item>Kannada</item>
<item>Kazakh</item>
<item>Khmer</item>
<item>Kinyarwanda</item>
<item>Korean</item>
<item>Kurdish (Kurmanji)</item>
<item>Kyrgyz</item>
<item>Lao</item>
<item>Latin</item>
<item>Latvian</item>
<item>Lithuanian</item>
<item>Luxembourgish</item>
<item>Macedonian</item>
<item>Malagasy</item>
<item>Malay</item>
<item>Malayalam</item>
<item>Maltese</item>
<item>Maori</item>
<item>Marathi</item>
<item>Mongolian</item>
<item>Burmese</item>
<item>Nepali</item>
<item>Norwegian</item>
<item>Oriya</item>
<item>Pashto</item>
<item>Persian</item>
<item>Polish</item>
<item>Portuguese</item>
<item>Punjabi</item>
<item>Romanian</item>
<item>Russian</item>
<item>Samoan</item>
<item>Scots Gaelic</item>
<item>Serbian</item>
<item>Sesotho</item>
<item>Shona</item>
<item>Sindhi</item>
<item>Sinhalese</item>
<item>Slovak</item>
<item>Slovenian</item>
<item>Somali</item>
<item>Spanish</item>
<item>Sundanese</item>
<item>Swahili</item>
<item>Swedish</item>
<item>Tajik</item>
<item>Tamil</item>
<item>Telugu</item>
<item>Thai</item>
<item>Turkish</item>
<item>Ukrainian</item>
<item>Urdu</item>
<item>Uyghur</item>
<item>Uzbek</item>
<item>Vietnamese</item>
<item>Welsh</item>
<item>Xhosa</item>
<item>Yiddish</item>
<item>Yoruba</item>
<item>Zulu</item>
</string-array>
</resources>

View file

@ -17,7 +17,7 @@
<string name="about">About</string>
<string name="source_code">Source Code</string>
<string name="search_hint">Search or Insert URL</string>
<string name="failed_download">Could not download file.</string>
<string name="failed_download">Could not download file</string>
<string name="download_notification_channel_name">File Downloads</string>
<string name="download_notification_channel_description">Notification showing the progress of downloading audio and video files</string>
<string name="you_are_in_latest_version">Using the latest version</string>
@ -36,7 +36,7 @@
<string name="update_app">Check for updates</string>
<string name="version">Version</string>
<string name="command_directory">Custom Command Folder</string>
<string name="couldnt_find_apk">Could not find a suitable APK.</string>
<string name="couldnt_find_apk">Could not find a suitable APK</string>
<string name="downloading_update">Downloading new version</string>
<string name="audio">Audio</string>
<string name="video">Video</string>
@ -323,6 +323,8 @@
<string name="export_file">Export File</string>
<string name="ytdl_source">yt-dlp Source</string>
<string name="update_ytdl_master">Master Version of yt-dlp</string>
<string name="update_ytdl_stable">Stable Version of yt-dlp</string>
<string name="buffer_size">Buffer Size</string>
<string name="buffer_size_summary">Size of download buffer, e.g. 1024 or 16K (default is 1024)</string>
<string name="preferred_audio_language">Preferred Audio Language</string>
</resources>

View file

@ -153,6 +153,7 @@
android:entries="@array/video_codec"
android:entryValues="@array/video_codec_values"
android:icon="@drawable/ic_code"
app:useSimpleSummaryProvider="true"
app:key="video_codec"
app:title="@string/preferred_video_codec" />
@ -161,9 +162,19 @@
android:entries="@array/audio_codec"
android:entryValues="@array/audio_codec_values"
android:icon="@drawable/ic_code"
app:useSimpleSummaryProvider="true"
app:key="audio_codec"
app:title="@string/preferred_audio_codec" />
<ListPreference
app:icon="@drawable/ic_language"
app:dialogTitle="@string/preferred_audio_language"
app:entries="@array/language_names"
app:entryValues="@array/language_codes"
app:key="audio_language"
app:useSimpleSummaryProvider="true"
app:title="@string/preferred_audio_language" />
<SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="false"

View file

@ -57,7 +57,7 @@
app:useSimpleSummaryProvider="true"
app:title="@string/format_order" />
<EditTextPreference
<Preference
app:key="piped_instance"
app:defaultValue=""
android:summary="@string/piped_instance_summary"

View file

@ -15,6 +15,7 @@ buildscript {
// supports java 1.6
commonsCompressVer = '1.12'
youtubedlAndroidVer = "-SNAPSHOT"
youtubedlAndroidVerNoPyCrypto = "97bee6c0e8"
workVer = "2.8.1"
composeVer = '1.4.2'
kotlinVer = "1.7.21"