1.7.0
- added subtitle language codes suggestions in the settings page - made the extractor chips in history page Sentence case - added a changelog screen where you can see recent releases and you can download the apks from it too - prevented app from crashing when trying to backup from a corrupted backup - added uploader_id as fallback for author data fetching in yt-dlp in case others are empty - fixed null pointer exception when running the update multiple items formats worker - added the seconds where the cut starts on downloads with cuts in them - made autonumbers be normal numbers instead of being 5 digits - fixed filename templates in cut files and added the index in the beginning and fixed bugs if the users left the template as empty - added 240p as a generic format
This commit is contained in:
parent
f0c88dfc73
commit
fba26b50d0
26 changed files with 624 additions and 342 deletions
|
|
@ -9,8 +9,8 @@ plugins {
|
|||
|
||||
def properties = new Properties()
|
||||
def versionMajor = 1
|
||||
def versionMinor = 6
|
||||
def versionPatch = 9
|
||||
def versionMinor = 7
|
||||
def versionPatch = 0
|
||||
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
|
||||
def versionExt = ""
|
||||
|
||||
|
|
|
|||
|
|
@ -339,10 +339,6 @@ class MainActivity : BaseActivity() {
|
|||
window.statusBarColor = getColor(android.R.color.transparent)
|
||||
incognitoHeader.visibility = View.GONE
|
||||
}
|
||||
//check logs option to show in tablet navbar
|
||||
if (navigationView is NavigationView){
|
||||
(navigationView as NavigationView).menu.findItem(R.id.downloadLogListFragment).isVisible = preferences.getBoolean("log_downloads", false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.deniscerri.ytdlnis.database.models
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import java.util.Date
|
||||
|
||||
data class GithubRelease(
|
||||
@SerializedName(value = "html_url")
|
||||
|
|
@ -9,6 +10,8 @@ data class GithubRelease(
|
|||
var tag_name: String,
|
||||
@SerializedName(value = "body")
|
||||
var body: String,
|
||||
@SerializedName(value = "published_at")
|
||||
var published_at: Date,
|
||||
@SerializedName(value = "assets")
|
||||
var assets: List<GithubReleaseAsset>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
|||
val infoUtil = InfoUtil(context)
|
||||
val items : ArrayList<ResultItem?> = arrayListOf()
|
||||
do {
|
||||
val tmp = infoUtil.getPlaylist(query, nextPageToken)
|
||||
val tmp = infoUtil.getPlaylist(query, nextPageToken, if (items.isNotEmpty()) items[0]?.playlistTitle ?: "" else "")
|
||||
items.addAll(tmp.videos)
|
||||
val tmpToken = tmp.nextPageToken
|
||||
if (tmpToken.isEmpty()) break
|
||||
|
|
|
|||
|
|
@ -354,25 +354,35 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
}
|
||||
|
||||
fun switchDownloadType(list: List<DownloadItem>, type: Type) : List<DownloadItem>{
|
||||
val updatedDownloadPath : String = when(type){
|
||||
Type.audio -> sharedPreferences.getString("music_path", FileUtil.getDefaultAudioPath())!!
|
||||
Type.video -> sharedPreferences.getString("video_path", FileUtil.getDefaultVideoPath())!!
|
||||
Type.command -> sharedPreferences.getString("command_path", FileUtil.getDefaultCommandPath())!!
|
||||
else -> ""
|
||||
}
|
||||
|
||||
list.forEach {
|
||||
val format = getFormat(it.allFormats, type)
|
||||
it.format = format
|
||||
val currentDownloadPath = when(it.type){
|
||||
Type.audio -> sharedPreferences.getString("music_path", FileUtil.getDefaultAudioPath())
|
||||
Type.video -> sharedPreferences.getString("video_path", FileUtil.getDefaultVideoPath())
|
||||
Type.command -> sharedPreferences.getString("command_path", FileUtil.getDefaultCommandPath())
|
||||
else -> ""
|
||||
}
|
||||
if (it.downloadPath == currentDownloadPath) it.downloadPath = updatedDownloadPath
|
||||
|
||||
var updatedDownloadPath: String = ""
|
||||
var container: String = ""
|
||||
|
||||
when(type){
|
||||
Type.audio -> {
|
||||
updatedDownloadPath = sharedPreferences.getString("music_path", FileUtil.getDefaultAudioPath())!!
|
||||
container = sharedPreferences.getString("audio_format", "")!!
|
||||
}
|
||||
Type.video -> {
|
||||
updatedDownloadPath = sharedPreferences.getString("music_path", FileUtil.getDefaultAudioPath())!!
|
||||
container = sharedPreferences.getString("audio_format", "")!!
|
||||
}
|
||||
Type.command -> {
|
||||
updatedDownloadPath = sharedPreferences.getString("music_path", FileUtil.getDefaultAudioPath())!!
|
||||
container = ""
|
||||
}
|
||||
else -> {
|
||||
updatedDownloadPath = ""
|
||||
}
|
||||
}
|
||||
|
||||
it.downloadPath = updatedDownloadPath
|
||||
it.type = type
|
||||
it.container = container
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
|
@ -443,7 +453,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
requirements.add {it: Format -> audioFormatIDPreference.contains(it.format_id)}
|
||||
|
||||
sharedPreferences.getString("audio_language", "")?.apply {
|
||||
if (this.isNotEmpty()){
|
||||
if (this.isNotBlank()){
|
||||
requirements.add { it: Format -> it.lang == this }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -275,10 +275,13 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
|
|||
val items = mutableListOf<DownloadItem>()
|
||||
ids?.forEach {
|
||||
items.add(downloadViewModel.getItemByID(it))
|
||||
downloadViewModel.deleteDownload(it)
|
||||
}
|
||||
withContext(Dispatchers.Main){
|
||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundleOf(
|
||||
Pair("downloads", items)
|
||||
))
|
||||
}
|
||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundleOf(
|
||||
Pair("downloads", items)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -449,96 +452,100 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
|
|||
|
||||
@SuppressLint("InflateParams")
|
||||
private fun updateSearchViewItems(searchQuery: Editable?, linkYouCopied: View?) = lifecycleScope.launch(Dispatchers.Main) {
|
||||
searchSuggestionsLinearLayout!!.visibility = GONE
|
||||
searchHistoryLinearLayout!!.visibility = GONE
|
||||
searchSuggestionsLinearLayout!!.post {
|
||||
searchSuggestionsLinearLayout!!.removeAllViews()
|
||||
}
|
||||
searchHistoryLinearLayout!!.post {
|
||||
searchHistoryLinearLayout!!.removeAllViews()
|
||||
}
|
||||
|
||||
linkYouCopied!!.visibility = GONE
|
||||
|
||||
if (searchView!!.editText.text.isEmpty()){
|
||||
searchView!!.editText.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
|
||||
}else{
|
||||
searchView!!.editText.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_plus, 0)
|
||||
}
|
||||
val history = withContext(Dispatchers.IO){
|
||||
resultViewModel.getSearchHistory().map { it.query }.filter { it.contains(searchQuery!!) }
|
||||
}
|
||||
val suggestions = if (sharedPreferences!!.getBoolean("search_suggestions", false)){
|
||||
infoUtil!!.getSearchSuggestions(searchQuery.toString())
|
||||
}else{
|
||||
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
|
||||
)
|
||||
lifecycleScope.launch {
|
||||
searchSuggestionsLinearLayout!!.visibility = GONE
|
||||
searchHistoryLinearLayout!!.visibility = GONE
|
||||
searchSuggestionsLinearLayout!!.post {
|
||||
searchSuggestionsLinearLayout!!.removeAllViews()
|
||||
}
|
||||
textView.setOnClickListener {
|
||||
searchView!!.setText(s)
|
||||
initSearch(searchView!!)
|
||||
searchHistoryLinearLayout!!.post {
|
||||
searchHistoryLinearLayout!!.removeAllViews()
|
||||
}
|
||||
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)
|
||||
|
||||
linkYouCopied!!.visibility = GONE
|
||||
|
||||
if (searchView!!.editText.text.isEmpty()){
|
||||
searchView!!.editText.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
|
||||
}else{
|
||||
searchView!!.editText.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_plus, 0)
|
||||
}
|
||||
val history = withContext(Dispatchers.IO){
|
||||
resultViewModel.getSearchHistory().map { it.query }.filter { it.contains(searchQuery!!) }
|
||||
}
|
||||
val suggestions = if (sharedPreferences!!.getBoolean("search_suggestions", false)){
|
||||
withContext(Dispatchers.IO){
|
||||
infoUtil!!.getSearchSuggestions(searchQuery.toString())
|
||||
}
|
||||
deleteDialog.show()
|
||||
true
|
||||
}else{
|
||||
emptyList()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
val mb = v.findViewById<ImageButton>(R.id.set_search_query_button)
|
||||
mb.setOnClickListener {
|
||||
searchView!!.editText.setText(s)
|
||||
searchView!!.editText.setSelection(searchView!!.editText.length())
|
||||
}
|
||||
}
|
||||
textView.setOnClickListener {
|
||||
searchView!!.setText(s)
|
||||
initSearch(searchView!!)
|
||||
searchHistoryLinearLayout!!.isVisible = history.isNotEmpty()
|
||||
if (linkYouCopied.findViewById<TextView>(R.id.suggestion_text).text.isNotEmpty()){
|
||||
linkYouCopied.visibility = VISIBLE
|
||||
}
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
|
|||
} else if (item.type == DownloadViewModel.Type.video) {
|
||||
if (filePresent) btn.setImageResource(R.drawable.ic_video_downloaded) else btn.setImageResource(R.drawable.ic_video)
|
||||
}else{
|
||||
btn.setImageResource(R.drawable.ic_terminal)
|
||||
if (filePresent) btn.setImageResource(R.drawable.ic_terminal) else btn.setImageResource(R.drawable.baseline_code_off_24)
|
||||
}
|
||||
if (btn.hasOnClickListeners()) btn.setOnClickListener(null)
|
||||
btn.isClickable = filePresent
|
||||
|
|
|
|||
|
|
@ -253,9 +253,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
val ids = mutableListOf<Long>()
|
||||
runBlocking {
|
||||
items.map { it.status = DownloadRepository.Status.Saved.toString() }
|
||||
items.forEach {
|
||||
ids.add(downloadViewModel.repository.insert(it))
|
||||
}
|
||||
ids.addAll(downloadViewModel.repository.insertAll(items))
|
||||
}
|
||||
val id = System.currentTimeMillis().toInt()
|
||||
val workRequest = OneTimeWorkRequestBuilder<UpdatePlaylistFormatsWorker>()
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.deniscerri.ytdlnis.R
|
|||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||
import com.deniscerri.ytdlnis.database.models.Format
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||
import com.deniscerri.ytdlnis.util.UiUtil
|
||||
import com.facebook.shimmer.ShimmerFrameLayout
|
||||
|
|
@ -147,19 +148,30 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
|||
updateFormatsJob = launch(Dispatchers.IO) {
|
||||
//simple download
|
||||
if (items.size == 1) {
|
||||
val res = infoUtil.getFormats(items.first()!!.url)
|
||||
res.filter { it.format_note != "storyboard" }
|
||||
chosenFormats = if (items.first()?.type == Type.audio) {
|
||||
res.filter { it.format_note.contains("audio", ignoreCase = true) }
|
||||
} else {
|
||||
res
|
||||
}
|
||||
if (chosenFormats.isEmpty()) throw Exception()
|
||||
kotlin.runCatching {
|
||||
val res = infoUtil.getFormats(items.first()!!.url)
|
||||
res.filter { it.format_note != "storyboard" }
|
||||
chosenFormats = if (items.first()?.type == Type.audio) {
|
||||
res.filter { it.format_note.contains("audio", ignoreCase = true) }
|
||||
} else {
|
||||
res
|
||||
}
|
||||
if (chosenFormats.isEmpty()) throw Exception()
|
||||
|
||||
formats = listOf(res)
|
||||
withContext(Dispatchers.Main){
|
||||
listener.onFormatsUpdated(formats)
|
||||
formats = listOf(res)
|
||||
withContext(Dispatchers.Main){
|
||||
listener.onFormatsUpdated(formats)
|
||||
}
|
||||
}.onFailure { err ->
|
||||
withContext(Dispatchers.Main){
|
||||
UiUtil.handleResultResponse(requireActivity(), ResultViewModel.ResultsUiState(
|
||||
false,
|
||||
Pair(R.string.no_results, err.message.toString()),
|
||||
mutableListOf(Pair(R.string.copy_log, ResultViewModel.ResultAction.COPY_LOG))
|
||||
), closed = {})
|
||||
}
|
||||
}
|
||||
|
||||
//list format filtering
|
||||
}else{
|
||||
var progress = "0/${items.size}"
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import android.widget.Toast
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.compose.ui.text.capitalize
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.forEach
|
||||
|
|
@ -410,7 +412,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
|||
val w = websites[i]
|
||||
if (w == "null" || w.isEmpty()) continue
|
||||
val tmp = layoutinflater!!.inflate(R.layout.filter_chip, websiteGroup, false) as Chip
|
||||
tmp.text = w
|
||||
tmp.text = w.replaceFirstChar { if (it.isLowerCase()) it.titlecase(java.util.Locale.getDefault()) else it.toString() }
|
||||
tmp.id = i
|
||||
tmp.setOnClickListener {
|
||||
Log.e(TAG, tmp.isChecked.toString())
|
||||
|
|
|
|||
|
|
@ -1,19 +1,15 @@
|
|||
package com.deniscerri.ytdlnis.ui.more.settings
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
|
|
@ -84,6 +80,8 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
|||
}
|
||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
||||
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
this.startActivity(intent)
|
||||
requireActivity().finishAffinity()
|
||||
true
|
||||
|
|
@ -93,6 +91,8 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
|||
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
||||
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
this.startActivity(intent)
|
||||
requireActivity().finishAffinity()
|
||||
true
|
||||
|
|
@ -101,6 +101,8 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
|||
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
||||
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
this.startActivity(intent)
|
||||
requireActivity().finishAffinity()
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import android.os.Bundle
|
|||
import android.os.Environment
|
||||
import android.util.LayoutDirection
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.edit
|
||||
|
|
@ -36,10 +38,12 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
|||
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.deniscerri.ytdlnis.util.UiUtil
|
||||
import com.deniscerri.ytdlnis.util.UpdateUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
|
|
@ -66,7 +70,6 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
|||
private lateinit var cookieViewModel: CookieViewModel
|
||||
private lateinit var commandTemplateViewModel: CommandTemplateViewModel
|
||||
|
||||
private var version: Preference? = null
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||
|
|
@ -149,21 +152,40 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
|||
getString(R.string.ok)
|
||||
) { _: DialogInterface?, _: Int ->
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
if (checkedItems.all { !it }){
|
||||
withContext(Dispatchers.Main){
|
||||
Snackbar.make(requireView(), R.string.select_backup_categories, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
val json = JsonObject()
|
||||
json.addProperty("app", "YTDLnis_backup")
|
||||
for (i in 0 until checkedItems.size) {
|
||||
if (checkedItems[i]) {
|
||||
when(values[i]){
|
||||
"settings" -> json.add("settings", backupSettings(preferences))
|
||||
"downloads" -> json.add("downloads", backupHistory())
|
||||
"queued" -> json.add("queued", backupQueuedDownloads() )
|
||||
"cancelled" -> json.add("cancelled", backupCancelledDownloads() )
|
||||
"errored" -> json.add("errored", backupErroredDownloads() )
|
||||
"saved" -> json.add("saved", backupSavedDownloads() )
|
||||
"cookies" -> json.add("cookies", backupCookies() )
|
||||
"templates" -> json.add("templates", backupCommandTemplates() )
|
||||
"shortcuts" -> json.add("shortcuts", backupShortcuts() )
|
||||
"searchHistory" -> json.add("search_history", backupSearchHistory() )
|
||||
runCatching {
|
||||
when(values[i]){
|
||||
"settings" -> json.add("settings", backupSettings(preferences))
|
||||
"downloads" -> json.add("downloads", backupHistory())
|
||||
"queued" -> json.add("queued", backupQueuedDownloads() )
|
||||
"cancelled" -> json.add("cancelled", backupCancelledDownloads() )
|
||||
"errored" -> json.add("errored", backupErroredDownloads() )
|
||||
"saved" -> json.add("saved", backupSavedDownloads() )
|
||||
"cookies" -> json.add("cookies", backupCookies() )
|
||||
"templates" -> json.add("templates", backupCommandTemplates() )
|
||||
"shortcuts" -> json.add("shortcuts", backupShortcuts() )
|
||||
"searchHistory" -> json.add("search_history", backupSearchHistory() )
|
||||
}
|
||||
}.onFailure {err ->
|
||||
withContext(Dispatchers.Main){
|
||||
val snack = Snackbar.make(requireView(), err.message ?: requireContext().getString(R.string.errored), Snackbar.LENGTH_LONG)
|
||||
val snackbarView: View = snack.view
|
||||
val snackTextView = snackbarView.findViewById<View>(com.google.android.material.R.id.snackbar_text) as TextView
|
||||
snackTextView.maxLines = 9999999
|
||||
snack.setAction(android.R.string.copy){
|
||||
UiUtil.copyToClipboard(err.message ?: requireContext().getString(R.string.errored), requireActivity())
|
||||
}
|
||||
snack.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -176,8 +198,12 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
|||
Calendar.DAY_OF_MONTH)} [${currentTime.get(Calendar.MILLISECOND)}.json")
|
||||
saveFile.delete()
|
||||
saveFile.createNewFile()
|
||||
saveFile.writeText(Gson().toJson(json))
|
||||
Snackbar.make(requireView(), getString(R.string.backup_created_successfully), Snackbar.LENGTH_LONG).show()
|
||||
saveFile.writeText(GsonBuilder().setPrettyPrinting().create().toJson(json))
|
||||
val s = Snackbar.make(requireView(), getString(R.string.backup_created_successfully), Snackbar.LENGTH_LONG)
|
||||
s.setAction(R.string.Open_File){
|
||||
UiUtil.openFileIntent(requireContext(), saveFile.absolutePath)
|
||||
}
|
||||
s.show()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,25 +227,6 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
|||
true
|
||||
}
|
||||
|
||||
version = findPreference("version")
|
||||
val nativeLibraryDir = context?.applicationInfo?.nativeLibraryDir
|
||||
|
||||
version!!.summary = "${BuildConfig.VERSION_NAME} [${nativeLibraryDir?.split("/lib/")?.get(1)}]"
|
||||
|
||||
version!!.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
lifecycleScope.launch{
|
||||
withContext(Dispatchers.IO){
|
||||
updateUtil!!.updateApp{ msg ->
|
||||
lifecycleScope.launch(Dispatchers.Main){
|
||||
Snackbar.make(requireView(), msg, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun backupSettings(preferences: SharedPreferences) : JsonArray {
|
||||
|
|
|
|||
|
|
@ -3,22 +3,38 @@ package com.deniscerri.ytdlnis.ui.more.settings
|
|||
import android.os.Bundle
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.util.UiUtil
|
||||
|
||||
|
||||
class ProcessingSettingsFragment : BaseSettingsFragment() {
|
||||
override val title: Int = R.string.processing
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.processing_preferences, rootKey)
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
val editor = prefs.edit()
|
||||
|
||||
val preferredFormatID : EditTextPreference? = findPreference("format_id")
|
||||
val preferredFormatIDAudio : EditTextPreference? = findPreference("format_id_audio")
|
||||
val subtitleLanguages : Preference? = findPreference("subs_lang")
|
||||
|
||||
preferredFormatID?.title = "${getString(R.string.preferred_format_id)} [${getString(R.string.video)}]"
|
||||
preferredFormatID?.dialogTitle = "${getString(R.string.file_name_template)} [${getString(R.string.video)}]"
|
||||
|
||||
preferredFormatIDAudio?.title = "${getString(R.string.preferred_format_id)} [${getString(R.string.audio)}]"
|
||||
preferredFormatIDAudio?.dialogTitle = "${getString(R.string.file_name_template)} [${getString(R.string.audio)}]"
|
||||
|
||||
subtitleLanguages?.summary = prefs.getString("subs_lang", "en.*,.*-orig")!!
|
||||
subtitleLanguages?.setOnPreferenceClickListener {
|
||||
UiUtil.showSubtitleLanguagesDialog(requireActivity(), prefs.getString("subs_lang", "en.*,.*-orig")!!){
|
||||
editor.putString("subs_lang", it)
|
||||
editor.apply()
|
||||
subtitleLanguages.summary = it
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,15 +1,20 @@
|
|||
package com.deniscerri.ytdlnis.ui.more.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.deniscerri.ytdlnis.BuildConfig
|
||||
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
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
class UpdateSettingsFragment : BaseSettingsFragment() {
|
||||
|
|
@ -17,6 +22,8 @@ class UpdateSettingsFragment : BaseSettingsFragment() {
|
|||
private var updateYTDL: Preference? = null
|
||||
private var ytdlVersion: Preference? = null
|
||||
private var updateUtil: UpdateUtil? = null
|
||||
private var version: Preference? = null
|
||||
|
||||
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
|
|
@ -39,33 +46,44 @@ class UpdateSettingsFragment : BaseSettingsFragment() {
|
|||
Snackbar.make(requireView(),
|
||||
requireContext().getString(R.string.ytdl_updating_started),
|
||||
Snackbar.LENGTH_LONG).show()
|
||||
when (updateUtil!!.updateYoutubeDL()) {
|
||||
YoutubeDL.UpdateStatus.DONE -> {
|
||||
Snackbar.make(requireView(),
|
||||
requireContext().getString(R.string.ytld_update_success),
|
||||
Snackbar.LENGTH_LONG).show()
|
||||
runCatching {
|
||||
when (updateUtil!!.updateYoutubeDL()) {
|
||||
YoutubeDL.UpdateStatus.DONE -> {
|
||||
Snackbar.make(requireView(),
|
||||
requireContext().getString(R.string.ytld_update_success),
|
||||
Snackbar.LENGTH_LONG).show()
|
||||
|
||||
YoutubeDL.getInstance().version(context)?.let {
|
||||
editor.putString("ytdl-version", it)
|
||||
editor.apply()
|
||||
ytdlVersion!!.summary = it
|
||||
YoutubeDL.getInstance().version(context)?.let {
|
||||
editor.putString("ytdl-version", it)
|
||||
editor.apply()
|
||||
ytdlVersion!!.summary = it
|
||||
}
|
||||
}
|
||||
YoutubeDL.UpdateStatus.ALREADY_UP_TO_DATE -> Snackbar.make(requireView(),
|
||||
requireContext().getString(R.string.you_are_in_latest_version),
|
||||
Snackbar.LENGTH_LONG).show()
|
||||
else -> {
|
||||
Snackbar.make(requireView(),
|
||||
requireContext().getString(R.string.errored),
|
||||
Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
YoutubeDL.UpdateStatus.ALREADY_UP_TO_DATE -> Snackbar.make(requireView(),
|
||||
requireContext().getString(R.string.you_are_in_latest_version),
|
||||
Snackbar.LENGTH_LONG).show()
|
||||
else -> {
|
||||
Snackbar.make(requireView(),
|
||||
requireContext().getString(R.string.errored),
|
||||
Snackbar.LENGTH_LONG).show()
|
||||
}.onFailure {
|
||||
val msg = it.message ?: requireContext().getString(R.string.errored)
|
||||
val snackBar = Snackbar.make(requireView(), msg, Snackbar.LENGTH_LONG)
|
||||
snackBar.setAction(android.R.string.copy){
|
||||
UiUtil.copyToClipboard(msg, requireActivity())
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
val pipedInstance = findPreference<Preference>("piped_instance")
|
||||
pipedInstance?.setOnPreferenceClickListener {
|
||||
findPreference<Preference>("piped_instance")?.setOnPreferenceClickListener {
|
||||
UiUtil.showPipedInstancesDialog(requireActivity(), preferences.getString("piped_instance", "")!!){
|
||||
editor.putString("piped_instance", it)
|
||||
editor.apply()
|
||||
|
|
@ -73,6 +91,32 @@ class UpdateSettingsFragment : BaseSettingsFragment() {
|
|||
true
|
||||
}
|
||||
|
||||
findPreference<Preference>("changelog")?.setOnPreferenceClickListener {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
updateUtil?.showChangeLog(requireActivity())
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
version = findPreference("version")
|
||||
val nativeLibraryDir = context?.applicationInfo?.nativeLibraryDir
|
||||
version!!.summary = "${BuildConfig.VERSION_NAME} [${nativeLibraryDir?.split("/lib/")?.get(1)}]"
|
||||
version!!.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
lifecycleScope.launch{
|
||||
withContext(Dispatchers.IO){
|
||||
updateUtil!!.updateApp{ msg ->
|
||||
lifecycleScope.launch(Dispatchers.Main){
|
||||
Snackbar.make(requireView(), msg, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import android.util.DisplayMetrics
|
|||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.ScrollView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.neo.highlight.core.Highlight
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class InfoUtil(private val context: Context) {
|
|||
}
|
||||
|
||||
@Throws(JSONException::class)
|
||||
fun getPlaylist(id: String, nextPageToken: String): PlaylistTuple {
|
||||
fun getPlaylist(id: String, nextPageToken: String, playlistName: String): PlaylistTuple {
|
||||
try{
|
||||
val items = arrayListOf<ResultItem?>()
|
||||
// -------------- PIPED API FUNCTION -------------------
|
||||
|
|
@ -117,7 +117,7 @@ class InfoUtil(private val context: Context) {
|
|||
kotlin.runCatching {
|
||||
val obj = dataArray.getJSONObject(i)
|
||||
val itm = createVideoFromPipedJSON(obj, "https://youtube.com" + obj.getString("url"))
|
||||
itm?.playlistTitle = res.getString("name")
|
||||
itm?.playlistTitle = runCatching { res.getString("name") }.getOrElse { playlistName }
|
||||
itm?.playlistURL = "https://www.youtube.com/playlist?list=$id"
|
||||
itm?.playlistIndex = (i+1)
|
||||
items.add(itm)
|
||||
|
|
@ -425,65 +425,61 @@ class InfoUtil(private val context: Context) {
|
|||
val p = Pattern.compile("^(https?)://(www.)?youtu(.be)?")
|
||||
val m = p.matcher(url)
|
||||
val formatSource = sharedPreferences.getString("formats_source", "yt-dlp")
|
||||
return try {
|
||||
if(m.find() && formatSource == "piped"){
|
||||
if (m.find() && formatSource == "piped"){
|
||||
return try {
|
||||
val id = getIDFromYoutubeURL(url)
|
||||
val res = genericRequest("$pipedURL/streams/$id")
|
||||
if (res.length() == 0) getFromYTDL(url)[0]!!
|
||||
val item = createVideoFromPipedJSON(res, "https://youtube.com/watch?v=$id")
|
||||
item!!.formats
|
||||
}else{
|
||||
getFormatsFromYTDL(url)
|
||||
}catch(e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
return getFormatsFromYTDL(url)
|
||||
}
|
||||
}catch (e: Exception){
|
||||
if (e is CancellationException) throw e
|
||||
getFormatsFromYTDL(url)
|
||||
}else{
|
||||
return getFormatsFromYTDL(url)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFormatsFromYTDL(url: String) : List<Format> {
|
||||
try {
|
||||
val request = YoutubeDLRequest(url)
|
||||
request.addOption("--print", "%(formats)s")
|
||||
request.addOption("--print", "%(duration)s")
|
||||
request.addOption("--skip-download")
|
||||
request.addOption("-R", "1")
|
||||
request.addOption("--compat-options", "manifest-filesize-approx")
|
||||
request.addOption("--socket-timeout", "5")
|
||||
val request = YoutubeDLRequest(url)
|
||||
request.addOption("--print", "%(formats)s")
|
||||
request.addOption("--print", "%(duration)s")
|
||||
request.addOption("--skip-download")
|
||||
request.addOption("-R", "1")
|
||||
request.addOption("--compat-options", "manifest-filesize-approx")
|
||||
request.addOption("--socket-timeout", "5")
|
||||
|
||||
if (sharedPreferences.getBoolean("use_cookies", false)){
|
||||
FileUtil.getCookieFile(context){
|
||||
request.addOption("--cookies", it)
|
||||
}
|
||||
|
||||
val useHeader = sharedPreferences.getBoolean("use_header", false)
|
||||
val header = sharedPreferences.getString("useragent_header", "")
|
||||
if (useHeader && !header.isNullOrBlank()){
|
||||
request.addOption("--add-header","User-Agent:${header}")
|
||||
}
|
||||
if (sharedPreferences.getBoolean("use_cookies", false)){
|
||||
FileUtil.getCookieFile(context){
|
||||
request.addOption("--cookies", it)
|
||||
}
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()) {
|
||||
request.addOption("--proxy", proxy)
|
||||
val useHeader = sharedPreferences.getBoolean("use_header", false)
|
||||
val header = sharedPreferences.getString("useragent_header", "")
|
||||
if (useHeader && !header.isNullOrBlank()){
|
||||
request.addOption("--add-header","User-Agent:${header}")
|
||||
}
|
||||
|
||||
|
||||
|
||||
val res = YoutubeDL.getInstance().execute(request)
|
||||
val results: Array<String?> = try {
|
||||
val lineSeparator = System.getProperty("line.separator")
|
||||
res.out.split(lineSeparator!!).toTypedArray()
|
||||
} catch (e: Exception) {
|
||||
arrayOf(res.out)
|
||||
}
|
||||
val json = results[0]
|
||||
val jsonArray = JSONArray(json)
|
||||
|
||||
return parseYTDLFormats(jsonArray)
|
||||
}catch (e: Exception){
|
||||
return listOf()
|
||||
}
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()) {
|
||||
request.addOption("--proxy", proxy)
|
||||
}
|
||||
|
||||
|
||||
|
||||
val res = YoutubeDL.getInstance().execute(request)
|
||||
val results: Array<String?> = try {
|
||||
val lineSeparator = System.getProperty("line.separator")
|
||||
res.out.split(lineSeparator!!).toTypedArray()
|
||||
} catch (e: Exception) {
|
||||
arrayOf(res.out)
|
||||
}
|
||||
val json = results[0]
|
||||
val jsonArray = JSONArray(json)
|
||||
|
||||
return parseYTDLFormats(jsonArray)
|
||||
}
|
||||
|
||||
fun getFormatsMultiple(urls: List<String>, progress: (progress: List<Format>) -> Unit){
|
||||
|
|
@ -548,8 +544,9 @@ class InfoUtil(private val context: Context) {
|
|||
urls.forEach {
|
||||
val id = getIDFromYoutubeURL(it)
|
||||
val res = genericRequest("$pipedURL/streams/$id")
|
||||
val vid = createVideoFromPipedJSON(res, it)
|
||||
progress(vid!!.formats)
|
||||
createVideoFromPipedJSON(res, it)?.apply {
|
||||
progress(this.formats)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -629,6 +626,9 @@ class InfoUtil(private val context: Context) {
|
|||
author = if (jsonObject.has("channel")) jsonObject.getString("channel") else ""
|
||||
if (author.isEmpty() || author == "null"){
|
||||
author = if (jsonObject.has("playlist_uploader")) jsonObject.getString("playlist_uploader") else ""
|
||||
if (author.isEmpty() || author == "null"){
|
||||
author = if (jsonObject.has("uploader_id")) jsonObject.getString("uploader_id") else ""
|
||||
}
|
||||
}
|
||||
}
|
||||
var duration = ""
|
||||
|
|
@ -791,6 +791,9 @@ class InfoUtil(private val context: Context) {
|
|||
author = if (jsonObject.has("channel")) jsonObject.getString("channel") else ""
|
||||
if (author.isEmpty() || author == "null"){
|
||||
author = if (jsonObject.has("playlist_uploader")) jsonObject.getString("playlist_uploader") else ""
|
||||
if (author.isEmpty() || author == "null"){
|
||||
author = if (jsonObject.has("uploader_id")) jsonObject.getString("uploader_id") else ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -837,7 +840,6 @@ class InfoUtil(private val context: Context) {
|
|||
|
||||
fun getSearchSuggestions(query: String): ArrayList<String> {
|
||||
val url = "https://suggestqueries.google.com/complete/search?client=youtube&ds=yt&client=firefox&q=$query"
|
||||
// invidousURL + "search/suggestions?q=" + query
|
||||
val res = genericArrayRequest(url)
|
||||
Log.e("aa", res.toString())
|
||||
if (res.length() == 0) return ArrayList()
|
||||
|
|
@ -1086,8 +1088,10 @@ class InfoUtil(private val context: Context) {
|
|||
if(downloadItem.title.isNotBlank()){
|
||||
request.addCommands(listOf("--replace-in-metadata", "title", ".+", downloadItem.title))
|
||||
}
|
||||
|
||||
request.addOption("--parse-metadata", "%(channel,uploader,creator,artist|null)s:%(uploader)s")
|
||||
request.addOption("--parse-metadata", "%(uploader)s:%(album_artist)s")
|
||||
if (downloadItem.author.isNotBlank()){
|
||||
request.addOption("--parse-metadata", "%(artist,uploader,creator,channel|null)s:%(uploader)s")
|
||||
request.addCommands(listOf("--replace-in-metadata", "uploader", ".+", downloadItem.author))
|
||||
request.addCommands(listOf("--replace-in-metadata","uploader"," - Topic$",""))
|
||||
}
|
||||
|
|
@ -1104,9 +1108,13 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("--force-keyframes-at-cuts")
|
||||
}
|
||||
}
|
||||
filenameTemplate += " %(section_title|)s "
|
||||
if (filenameTemplate.isBlank()){
|
||||
filenameTemplate = "%(section_title& {}|)s %(title)s"
|
||||
}else{
|
||||
filenameTemplate += " %(section_title& {}|)s "
|
||||
}
|
||||
if (downloadItem.downloadSections.split(";").size > 1){
|
||||
filenameTemplate += "%(autonumber)s"
|
||||
filenameTemplate = "%(autonumber)d. %(section_start& {}|)s $filenameTemplate"
|
||||
}
|
||||
request.addOption("--output-na-placeholder", " ")
|
||||
}
|
||||
|
|
@ -1145,7 +1153,7 @@ 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]
|
||||
val aCodecPref = runCatching { context.getStringArray(R.array.audio_codec_values_ytdlp)[aCodecPrefIndex] }.getOrElse { "" }
|
||||
|
||||
when(type){
|
||||
DownloadViewModel.Type.audio -> {
|
||||
|
|
@ -1162,14 +1170,14 @@ class InfoUtil(private val context: Context) {
|
|||
val ext = downloadItem.container
|
||||
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"
|
||||
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("-f", "ba[language^=$preferredLanguage]/ba/b")
|
||||
}
|
||||
}
|
||||
request.addOption("-x")
|
||||
|
|
@ -1272,15 +1280,17 @@ class InfoUtil(private val context: Context) {
|
|||
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")
|
||||
// with audio removed
|
||||
if (audioF.isBlank()){
|
||||
f.append("$videoF/bv/b")
|
||||
}else{
|
||||
//with audio
|
||||
audioF = "+$audioF"
|
||||
f.append("$videoF$audioF/$videoF+ba/$videoF/b")
|
||||
|
||||
if (audioF.count("+") > 1){
|
||||
request.addOption("--audio-multistreams")
|
||||
if (audioF.count("+") > 1){
|
||||
request.addOption("--audio-multistreams")
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if (videoF == context.resources.getString(R.string.best_quality) || videoF == "best") {
|
||||
|
|
@ -1306,8 +1316,7 @@ class InfoUtil(private val context: Context) {
|
|||
.filter { it.isNotBlank() }
|
||||
.ifEmpty {
|
||||
val list = mutableListOf<String>()
|
||||
if (preferredAudioCodec.isNotBlank()) list.add("ba[acodec~='^($preferredAudioCodec)']")
|
||||
if (preferredAudioLanguage.isNotEmpty()) list.add("ba[language=$preferredAudioLanguage]")
|
||||
if (preferredAudioLanguage.isNotEmpty()) list.add("ba[language^=$preferredAudioLanguage]")
|
||||
list.add(audioF)
|
||||
list
|
||||
}.apply {
|
||||
|
|
@ -1333,7 +1342,7 @@ class InfoUtil(private val context: Context) {
|
|||
if (!downloadItem.videoPreferences.removeAudio){
|
||||
//build format with audio with preferred language
|
||||
if (preferredAudioLanguage.isNotBlank()){
|
||||
val al = "$v+ba[language=$preferredAudioLanguage]/"
|
||||
val al = "$v+ba[language^=$preferredAudioLanguage]/"
|
||||
if (!f.contains(al)) f.append(al)
|
||||
}
|
||||
//build format with best audio
|
||||
|
|
@ -1348,12 +1357,14 @@ class InfoUtil(private val context: Context) {
|
|||
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())
|
||||
StringBuilder().apply {
|
||||
if (vCodecPref.isNotBlank()) append(",vcodec:$vCodecPref")
|
||||
if (aCodecPref.isNotBlank()) append(",acodec:$aCodecPref")
|
||||
if (cont.isNotBlank()) append(",ext:$cont")
|
||||
if (this.isNotBlank()){
|
||||
request.addOption("-S", "+hasaud$this")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -896,7 +896,7 @@ object UiUtil {
|
|||
)
|
||||
}
|
||||
|
||||
private fun showSubtitleLanguagesDialog(context: Activity, currentValue: String, ok: (newValue: String) -> Unit){
|
||||
fun showSubtitleLanguagesDialog(context: Activity, currentValue: String, ok: (newValue: String) -> Unit){
|
||||
val builder = MaterialAlertDialogBuilder(context)
|
||||
builder.setTitle(context.getString(R.string.subtitle_languages))
|
||||
val view = context.layoutInflater.inflate(R.layout.subtitle_dialog, null)
|
||||
|
|
@ -966,7 +966,7 @@ object UiUtil {
|
|||
}
|
||||
|
||||
|
||||
private fun copyToClipboard(text: String, activity: Activity){
|
||||
fun copyToClipboard(text: String, activity: Activity){
|
||||
val clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(text, text)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
|
|
@ -1454,6 +1454,21 @@ object UiUtil {
|
|||
errDialog.show()
|
||||
}
|
||||
|
||||
fun showErrorDialog(context: Context, it: String){
|
||||
val title = context.getString(R.string.errored)
|
||||
|
||||
val errDialog = MaterialAlertDialogBuilder(context)
|
||||
.setTitle(title)
|
||||
.setMessage(it)
|
||||
.setPositiveButton(android.R.string.copy) { d:DialogInterface?, _:Int ->
|
||||
val clipboard: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
clipboard.setText(it)
|
||||
d?.dismiss()
|
||||
}
|
||||
|
||||
errDialog.show()
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun handleDownloadsResponse(context: Activity, lifecycleScope: CoroutineScope, supportFragmentManager: FragmentManager, it: DownloadViewModel.DownloadsUiState, downloadViewModel: DownloadViewModel, historyViewModel: HistoryViewModel){
|
||||
val downloadAnywayAction = it.actions?.first { it.second == DownloadViewModel.DownloadsAction.DOWNLOAD_ANYWAY}
|
||||
|
|
@ -1732,7 +1747,7 @@ object UiUtil {
|
|||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val chipGroup = view.findViewById<ChipGroup>(R.id.filename_suggested_chipgroup)
|
||||
val chips = mutableListOf<Chip>()
|
||||
val instances = InfoUtil(context).getPipedInstances()
|
||||
val instances = InfoUtil(context).getPipedInstances().ifEmpty { return@launch }
|
||||
instances.forEach { s ->
|
||||
val tmp = context.layoutInflater.inflate(R.layout.filter_chip, chipGroup, false) as Chip
|
||||
tmp.text = s
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.deniscerri.ytdlnis.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.DownloadManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
|
|
@ -13,8 +14,11 @@ import android.os.Bundle
|
|||
import android.os.Environment
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.text.format.DateFormat
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.util.Log
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ScrollView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
|
|
@ -22,6 +26,10 @@ import androidx.preference.PreferenceManager
|
|||
import com.deniscerri.ytdlnis.BuildConfig
|
||||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.models.GithubRelease
|
||||
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.chip.ChipGroup
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
|
|
@ -36,6 +44,8 @@ import java.io.File
|
|||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
class UpdateUtil(var context: Context) {
|
||||
|
|
@ -152,6 +162,75 @@ class UpdateUtil(var context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun showChangeLog(activity: Activity){
|
||||
runCatching {
|
||||
val scrollView = ScrollView(activity)
|
||||
|
||||
val linearLayout = LinearLayout(activity)
|
||||
linearLayout.orientation = LinearLayout.VERTICAL
|
||||
|
||||
val layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
|
||||
layoutParams.setMargins(20, 20, 20, 0)
|
||||
scrollView.layoutParams = layoutParams
|
||||
scrollView.addView(linearLayout)
|
||||
|
||||
getGithubReleases().forEach {
|
||||
(activity.layoutInflater.inflate(R.layout.changelog_item, null) as MaterialCardView).apply {
|
||||
this.layoutParams = layoutParams
|
||||
findViewById<TextView>(R.id.version).text = it.tag_name
|
||||
findViewById<TextView>(R.id.date).text = SimpleDateFormat(
|
||||
DateFormat.getBestDateTimePattern(
|
||||
Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault()).format(it.published_at.time)
|
||||
|
||||
val mdText = findViewById<TextView>(R.id.content)
|
||||
val mw = Markwon.builder(context).usePlugin(object: AbstractMarkwonPlugin() {
|
||||
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
|
||||
builder.linkResolver { view, link ->
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
|
||||
startActivity(context, browserIntent, Bundle())
|
||||
}
|
||||
}
|
||||
}).build()
|
||||
mw.setMarkdown(mdText, it.body)
|
||||
|
||||
|
||||
val assetGroup = findViewById<ChipGroup>(R.id.assets)
|
||||
it.assets.forEachIndexed { idx, c ->
|
||||
val tmp = activity.layoutInflater.inflate(R.layout.suggestion_chip, assetGroup, false) as Chip
|
||||
tmp.text = c.name
|
||||
tmp.id = idx
|
||||
tmp.setOnClickListener {
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(c.browser_download_url))
|
||||
startActivity(context, browserIntent, Bundle())
|
||||
}
|
||||
assetGroup!!.addView(tmp)
|
||||
}
|
||||
|
||||
linearLayout.addView(this)
|
||||
}
|
||||
}
|
||||
|
||||
val changeLogDialog = MaterialAlertDialogBuilder(context)
|
||||
.setTitle(activity.getString(R.string.changelog))
|
||||
.setView(scrollView)
|
||||
.setIcon(R.drawable.ic_chapters)
|
||||
.setNegativeButton(context.resources.getString(R.string.cancel)) { _: DialogInterface?, _: Int -> }
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
changeLogDialog.show()
|
||||
}
|
||||
|
||||
}.onFailure {
|
||||
if (it.message != null){
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
UiUtil.showErrorDialog(context, it.message!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGithubReleases(): List<GithubRelease> {
|
||||
val url = "https://api.github.com/repos/deniscerri/ytdlnis/releases"
|
||||
val conn: HttpURLConnection
|
||||
|
|
@ -188,15 +267,8 @@ class UpdateUtil(var context: Context) {
|
|||
"master" to YoutubeDL.UpdateChannel._MASTER,
|
||||
)
|
||||
val channel = sharedPreferences.getString("ytdlp_source", "nightly")
|
||||
|
||||
try {
|
||||
YoutubeDL.getInstance().updateYoutubeDL(context, channelMap[channel] ?: YoutubeDL.UpdateChannel._NIGHTLY).apply {
|
||||
updatingYTDL = false
|
||||
}
|
||||
}catch (e: Exception){
|
||||
e.printStackTrace()
|
||||
YoutubeDL.getInstance().updateYoutubeDL(context, channelMap[channel] ?: YoutubeDL.UpdateChannel._NIGHTLY).apply {
|
||||
updatingYTDL = false
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class UpdatePlaylistFormatsWorker(
|
|||
|
||||
runBlocking {
|
||||
resDao.update(r)
|
||||
dao.delete(d.id)
|
||||
dao.update(d)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
5
app/src/main/res/drawable/baseline_code_off_24.xml
Normal file
5
app/src/main/res/drawable/baseline_code_off_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="?android:colorAccent" android:pathData="M19.17,12l-4.58,-4.59L16,6l6,6l-3.59,3.59L17,14.17L19.17,12zM1.39,4.22l4.19,4.19L2,12l6,6l1.41,-1.41L4.83,12L7,9.83l12.78,12.78l1.41,-1.41L2.81,2.81L1.39,4.22z"/>
|
||||
</vector>
|
||||
62
app/src/main/res/layout/changelog_item.xml
Normal file
62
app/src/main/res/layout/changelog_item.xml
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="10dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/version"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:fontFamily="monospace"
|
||||
android:text="1.6.9"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/date"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:text="1.1.1970"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/version" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="## WHATS NEW"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/date" />
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/assets"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:chipSpacingVertical="-7dp"
|
||||
app:chipSpacingHorizontal="5dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/content"
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
|
@ -1112,6 +1112,7 @@
|
|||
|
||||
<!--below is for preferred audio language, not related to app's language-->
|
||||
<string-array name="language_codes">
|
||||
<item></item>
|
||||
<item>af</item> <!-- Afrikaans -->
|
||||
<item>sq</item> <!-- Albanian -->
|
||||
<item>am</item> <!-- Amharic -->
|
||||
|
|
@ -1222,6 +1223,7 @@
|
|||
</string-array>
|
||||
|
||||
<string-array name="language_names">
|
||||
<item>@string/defaultValue</item>
|
||||
<item>Afrikaans</item>
|
||||
<item>Albanian</item>
|
||||
<item>Amharic</item>
|
||||
|
|
|
|||
|
|
@ -327,4 +327,7 @@
|
|||
<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>
|
||||
<string name="changelog">Changelog</string>
|
||||
<string name="source">Source</string>
|
||||
<string name="app">App</string>
|
||||
</resources>
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
app:summary="@string/save_subs_desc"
|
||||
app:title="@string/save_subs" />
|
||||
|
||||
<EditTextPreference
|
||||
<Preference
|
||||
android:icon="@drawable/baseline_subject_24"
|
||||
app:key="subs_lang"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
|
|
|
|||
|
|
@ -100,10 +100,6 @@
|
|||
android:data="https://github.com/deniscerri/ytdlnis" />
|
||||
</Preference>
|
||||
|
||||
<Preference
|
||||
app:icon="@drawable/ic_info"
|
||||
app:key="version"
|
||||
app:title="@string/version"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,86 +2,104 @@
|
|||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<Preference
|
||||
app:icon="@drawable/ic_update"
|
||||
app:key="update_ytdl"
|
||||
app:summary="@string/ytdl_update_hint"
|
||||
app:title="@string/update_ytdl" />
|
||||
<PreferenceCategory android:title="@string/source">
|
||||
|
||||
<Preference
|
||||
app:icon="@drawable/ic_info"
|
||||
app:key="ytdl-version"
|
||||
app:title="@string/ytdl_version"/>
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="nightly"
|
||||
android:entries="@array/ytdlp_source"
|
||||
android:entryValues="@array/ytdlp_source_values"
|
||||
android:icon="@drawable/baseline_source_24"
|
||||
app:key="ytdlp_source"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
app:title="@string/ytdl_source" />
|
||||
|
||||
<Preference
|
||||
app:icon="@drawable/ic_update"
|
||||
app:key="update_ytdl"
|
||||
app:summary="@string/ytdl_update_hint"
|
||||
app:title="@string/update_ytdl" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="true"
|
||||
app:icon="@drawable/ic_update"
|
||||
android:key="auto_update_ytdlp"
|
||||
app:title="@string/auto_update_ytdlp" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="yt-dlp"
|
||||
android:entries="@array/formats_source"
|
||||
android:entryValues="@array/formats_source_values"
|
||||
android:icon="@drawable/baseline_manage_search_24"
|
||||
app:key="formats_source"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
app:title="@string/format_source" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="filesize"
|
||||
android:entries="@array/format_ordering"
|
||||
android:entryValues="@array/format_ordering_values"
|
||||
android:icon="@drawable/baseline_sort_24"
|
||||
app:key="format_order"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
app:title="@string/format_order" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="true"
|
||||
android:icon="@drawable/ic_format"
|
||||
android:key="update_formats"
|
||||
app:summary="@string/update_formats_summary"
|
||||
app:title="@string/update_formats" />
|
||||
|
||||
<Preference
|
||||
app:key="piped_instance"
|
||||
app:defaultValue=""
|
||||
android:summary="@string/piped_instance_summary"
|
||||
app:title="@string/piped_instance" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/app">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="false"
|
||||
android:icon="@drawable/ic_update_app"
|
||||
android:key="update_app"
|
||||
app:summary="@string/update_app_summary"
|
||||
app:title="@string/update_app" />
|
||||
|
||||
<EditTextPreference
|
||||
app:isPreferenceVisible="false"
|
||||
app:key="skip_updates"
|
||||
app:defaultValue="" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="false"
|
||||
android:icon="@drawable/ic_update_app"
|
||||
android:key="update_beta"
|
||||
app:summary="@string/update_app_beta_summary"
|
||||
app:title="@string/update_app_beta" />
|
||||
|
||||
<Preference
|
||||
app:icon="@drawable/ic_chapters"
|
||||
app:key="changelog"
|
||||
app:title="@string/changelog" />
|
||||
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="true"
|
||||
app:icon="@drawable/ic_update"
|
||||
android:key="auto_update_ytdlp"
|
||||
app:title="@string/auto_update_ytdlp" />
|
||||
|
||||
<Preference
|
||||
app:icon="@drawable/ic_info"
|
||||
app:key="ytdl-version"
|
||||
app:title="@string/ytdl_version"/>
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="nightly"
|
||||
android:entries="@array/ytdlp_source"
|
||||
android:entryValues="@array/ytdlp_source_values"
|
||||
android:icon="@drawable/baseline_source_24"
|
||||
app:key="ytdlp_source"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
app:title="@string/ytdl_source" />
|
||||
<Preference
|
||||
app:icon="@drawable/ic_info"
|
||||
app:key="version"
|
||||
app:title="@string/version"/>
|
||||
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="true"
|
||||
android:icon="@drawable/ic_format"
|
||||
android:key="update_formats"
|
||||
app:summary="@string/update_formats_summary"
|
||||
app:title="@string/update_formats" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="yt-dlp"
|
||||
android:entries="@array/formats_source"
|
||||
android:entryValues="@array/formats_source_values"
|
||||
android:icon="@drawable/baseline_manage_search_24"
|
||||
app:key="formats_source"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
app:title="@string/format_source" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="filesize"
|
||||
android:entries="@array/format_ordering"
|
||||
android:entryValues="@array/format_ordering_values"
|
||||
android:icon="@drawable/baseline_sort_24"
|
||||
app:key="format_order"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
app:title="@string/format_order" />
|
||||
|
||||
<Preference
|
||||
app:key="piped_instance"
|
||||
app:defaultValue=""
|
||||
android:summary="@string/piped_instance_summary"
|
||||
app:title="@string/piped_instance" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="false"
|
||||
android:icon="@drawable/ic_update_app"
|
||||
android:key="update_app"
|
||||
app:summary="@string/update_app_summary"
|
||||
app:title="@string/update_app" />
|
||||
|
||||
<EditTextPreference
|
||||
app:isPreferenceVisible="false"
|
||||
app:key="skip_updates"
|
||||
app:defaultValue="" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="false"
|
||||
android:icon="@drawable/ic_update_app"
|
||||
android:key="update_beta"
|
||||
app:summary="@string/update_app_beta_summary"
|
||||
app:title="@string/update_app_beta" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
Loading…
Reference in a new issue