pull/634/head
zaednasr 2 years ago
parent c3aa1f0f2f
commit 7bad5ad587
No known key found for this signature in database
GPG Key ID: 92B1DE23AD3D0E9E

@ -1,5 +1,28 @@
# YTDLnis Changelog
> # 1.8.1 (2024-11)
# What's Changed
- Implemented pagination in the History screen to help with large lists
- Added delete all for each page in the download queue screen
- Added accessing Terminal from the shortcuts menu of the app
- Made the download notifications grouped together
- Fixed app crashing when pressing the log of a download but the log has been deleted
- Fixed app crashing when queueing long list of items in the download queue
- Added ability to mass re-download items from the history screen
- Made the app remember the last used scheduled time so it can suggest you that time for the next download
- #618, made all preferences with a description show their values
- Fixed bug that prevented app from loading all urls from text file
# Advanced Settings
Added a new category for more advanced users and moved the extractor argument settings there.
- Added ability to make command templates usable while fetching data in the home screen. They will be appended to the data fetching command as an extra command in the end. Enable the toggle in the advanced settings to be able to configure your templates for it
- When PO Token is set, the app now adds the web extractor argument for youtube. I forgor...
(If you want to set more PO tokens for other player clients i guess set them in the other extractor argument text preference, and also modify the player client. The separate PO Token preference applies it only for the web player client)
> # 1.8.0 (2024-10)
# Notice

@ -10,7 +10,7 @@ plugins {
def properties = new Properties()
def versionMajor = 1
def versionMinor = 8
def versionPatch = 0
def versionPatch = 1
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
def isBeta = false

@ -35,6 +35,10 @@ class HistoryRepository(private val historyDao: HistoryDao) {
return historyDao.getAllHistoryByURL(url)
}
fun getAllByIDs(ids: List<Long>) : List<HistoryItem> {
return historyDao.getAllHistoryByIDs(ids)
}
data class HistoryIDsAndPaths(
val id: Long,
val downloadPath: List<String>

@ -225,6 +225,14 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
ytExtractorResult.getOrElse { items }
}else{
val res = ytdlpUtil.getFromYTDL(url)
//TODO REMOVE THIS WHEN YT-DLP FIXES ISSUE #10827
res.forEach {
it.apply {
playlistTitle = ""
playlistURL = ""
playlistIndex = 0
}
}
val ids = resultDao.insertMultiple(res)
ids.forEachIndexed { index, id ->
res[index].id = id

@ -616,6 +616,39 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
processingItemsJob = job
}
fun turnHistoryItemsToProcessingDownloads(itemIDs: List<Long>, downloadNow: Boolean = false) = viewModelScope.launch(Dispatchers.IO) {
val job = viewModelScope.launch(Dispatchers.IO) {
repository.deleteProcessing()
processingItems.emit(true)
try {
val toInsert = mutableListOf<DownloadItem>()
itemIDs.forEach {
val item = historyRepository.getItem(it)
val downloadItem = createDownloadItemFromHistory(item)
downloadItem.status = DownloadRepository.Status.Processing.toString()
if (processingItemsJob?.isCancelled == true) {
throw CancellationException()
}
if (downloadNow) {
downloadItem.status = DownloadRepository.Status.Queued.toString()
queueDownloads(listOf(downloadItem))
}else{
toInsert.add(downloadItem)
//repository.insert(downloadItem)
}
}
repository.insertAll(toInsert)
processingItems.emit(false)
} catch (e: Exception) {
deleteProcessing()
processingItems.emit(false)
}
}
processingItemsJob = job
}
fun turnResultItemsToProcessingDownloads(itemIDs: List<Long>, downloadNow: Boolean = false) = viewModelScope.launch(Dispatchers.IO) {
val job = viewModelScope.launch(Dispatchers.IO) {

@ -146,7 +146,11 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
val ids = repository.getFilteredIDs(queryFilter.value, typeFilter.value, websiteFilter.value, sortType.value, sortOrder.value, statusFilter.value)
val firstIndex = ids.indexOf(firstID)
val secondIndex = ids.indexOf(secondID)
return ids.filterIndexed {index, _ -> index in (firstIndex + 1) until secondIndex }
return if (firstIndex > secondIndex) {
ids.filterIndexed {index, _ -> index < firstIndex && index > secondIndex }
}else {
ids.filterIndexed {index, _ -> index > firstIndex && index < secondIndex }
}
}
fun getItemIDsNotPresentIn(not: List<Long>) : List<Long> {

@ -14,28 +14,28 @@ import kotlinx.coroutines.withContext
class PauseDownloadNotificationReceiver : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
// val result = goAsync()
// val id = intent.getIntExtra("itemID", 0)
// if (id != 0) {
// runCatching {
// val title = intent.getStringExtra("title")
// val notificationUtil = NotificationUtil(c)
// notificationUtil.cancelDownloadNotification(id)
// YoutubeDL.getInstance().destroyProcessById(id.toString())
// val dbManager = DBManager.getInstance(c)
// CoroutineScope(Dispatchers.IO).launch{
// try {
// val item = dbManager.downloadDao.getDownloadById(id.toLong())
// item.status = DownloadRepository.Status.ActivePaused.toString()
// dbManager.downloadDao.update(item)
// }finally {
// withContext(Dispatchers.Main){
// notificationUtil.createResumeDownload(id, title)
// result.finish()
// }
// }
// }
// }
// }
val result = goAsync()
val id = intent.getIntExtra("itemID", 0)
if (id != 0) {
runCatching {
val title = intent.getStringExtra("title")
val notificationUtil = NotificationUtil(c)
notificationUtil.cancelDownloadNotification(id)
YoutubeDL.getInstance().destroyProcessById(id.toString())
val dbManager = DBManager.getInstance(c)
CoroutineScope(Dispatchers.IO).launch{
try {
val item = dbManager.downloadDao.getDownloadById(id.toLong())
item.status = DownloadRepository.Status.Paused.toString()
dbManager.downloadDao.update(item)
}finally {
withContext(Dispatchers.Main){
notificationUtil.createResumeDownload(id, title)
result.finish()
}
}
}
}
}
}
}

@ -3,6 +3,7 @@ package com.deniscerri.ytdl.ui.downloads
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.content.SharedPreferences
import android.graphics.Canvas
import android.graphics.Color
import android.os.Bundle
@ -85,6 +86,7 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
private lateinit var uiHandler: Handler
private lateinit var noResults: RelativeLayout
private lateinit var selectionChips: LinearLayout
private lateinit var sharedPreferences: SharedPreferences
private var websiteList: MutableList<String> = mutableListOf()
private var totalCount = 0
private var actionMode : ActionMode? = null
@ -104,6 +106,7 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
fragmentContext = context
layoutinflater = LayoutInflater.from(context)
topAppBar = view.findViewById(R.id.history_toolbar)
@ -629,6 +632,31 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
}
true
}
R.id.redownload -> {
lifecycleScope.launch {
val selectedObjects = getSelectedIDs()
historyAdapter.clearCheckedItems()
actionMode?.finish()
if (selectedObjects.size == 1) {
val tmp = withContext(Dispatchers.IO) {
historyViewModel.getByID(selectedObjects.first())
}
findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf(
Pair("result", downloadViewModel.createResultItemFromHistory(tmp)),
Pair("type", tmp.type)
))
}else {
val showDownloadCard = sharedPreferences.getBoolean("download_card", true)
downloadViewModel.turnHistoryItemsToProcessingDownloads(selectedObjects, downloadNow = !showDownloadCard)
actionMode?.finish()
if (showDownloadCard){
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
}
}
}
true
}
R.id.select_all -> {
historyAdapter.checkAll()
val selectedCount = historyAdapter.getSelectedObjectsCount(totalCount)

@ -8,6 +8,7 @@ import android.provider.Settings
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.interaction.DragInteraction
import androidx.core.content.edit
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceManager
@ -176,6 +177,92 @@ class DownloadSettingsFragment : BaseSettingsFragment() {
}
true
}
findPreference<EditTextPreference>("proxy")?.apply {
val s = getString(R.string.socks5_proxy_summary)
summary = if (text.isNullOrBlank()) {
s
}else {
"${s}\n[${text}]"
}
setOnPreferenceChangeListener { _, newValue ->
summary = if ((newValue as String?).isNullOrBlank()) {
s
}else {
"${s}\n[${newValue}]"
}
true
}
}
findPreference<ListPreference>("preferred_download_type")?.apply {
val s = getString(R.string.preferred_download_type_summary)
summary = if (value.isNullOrBlank()) {
s
}else {
"${s}\n[${entries[entryValues.indexOf(value)]}]"
}
setOnPreferenceChangeListener { _, newValue ->
summary = if ((newValue as String?).isNullOrBlank()) {
s
}else {
"${s}\n[${entries[entryValues.indexOf(newValue)]}]"
}
true
}
}
findPreference<EditTextPreference>("limit_rate")?.apply {
val s = getString(R.string.limit_rate_summary)
summary = if (text.isNullOrBlank()) {
s
}else {
"${s}\n[${text}]"
}
setOnPreferenceChangeListener { _, newValue ->
summary = if ((newValue as String?).isNullOrBlank()) {
s
}else {
"${s}\n[${newValue}]"
}
true
}
}
findPreference<EditTextPreference>("buffer_size")?.apply {
val s = getString(R.string.limit_rate_summary)
summary = if (text.isNullOrBlank()) {
s
}else {
"${s}\n[${text}]"
}
setOnPreferenceChangeListener { _, newValue ->
summary = if ((newValue as String?).isNullOrBlank()) {
s
}else {
"${s}\n[${newValue}]"
}
true
}
}
findPreference<EditTextPreference>("socket_timeout")?.apply {
val s = getString(R.string.limit_rate_summary)
summary = if (text.isNullOrBlank()) {
s
}else {
"${s}\n[${text}]"
}
setOnPreferenceChangeListener { _, newValue ->
summary = if ((newValue as String?).isNullOrBlank()) {
s
}else {
"${s}\n[${newValue}]"
}
true
}
}
}
private var archivePathResultLauncher = registerForActivityResult(

@ -18,9 +18,13 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.widget.PopupMenu
import androidx.core.content.res.ResourcesCompat
import androidx.core.os.LocaleListCompat
import androidx.core.text.HtmlCompat
import androidx.core.text.parseAsHtml
import androidx.core.view.forEach
import androidx.navigation.fragment.findNavController
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import androidx.preference.SwitchPreferenceCompat
@ -35,6 +39,7 @@ import com.deniscerri.ytdl.databinding.NavOptionsItemBinding
import com.deniscerri.ytdl.ui.adapter.NavBarOptionsAdapter
import com.deniscerri.ytdl.util.NavbarUtil
import com.deniscerri.ytdl.util.ThemeUtil
import com.deniscerri.ytdl.util.ThemeUtil.getThemeColor
import com.deniscerri.ytdl.util.UiUtil
import com.deniscerri.ytdl.util.UpdateUtil
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -43,15 +48,6 @@ import java.util.Locale
class GeneralSettingsFragment : BaseSettingsFragment() {
override val title: Int = R.string.general
private var language: ListPreference? = null
private var theme: ListPreference? = null
private var accent: ListPreference? = null
private var highContrast: SwitchPreferenceCompat? = null
private var locale: ListPreference? = null
private var showTerminalShareIcon: SwitchPreferenceCompat? = null
private var ignoreBatteryOptimization: Preference? = null
private var displayOverApps: SwitchPreferenceCompat? = null
private lateinit var preferences: SharedPreferences
private var updateUtil: UpdateUtil? = null
@ -71,20 +67,16 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
}
}
language = findPreference("app_language")
theme = findPreference("ytdlnis_theme")
accent = findPreference("theme_accent")
highContrast = findPreference("high_contrast")
locale = findPreference("locale")
showTerminalShareIcon = findPreference("show_terminal")
if(language!!.value == null) language!!.value = Locale.getDefault().language
language!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
findPreference<ListPreference>("app_language")?.apply {
if (value == null) {
value = Locale.getDefault().language
summary = Locale.getDefault().displayLanguage
}
setOnPreferenceChangeListener { _, newValue ->
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue.toString()))
true
}
}
findPreference<Preference>("label_visibility")?.apply {
isVisible = !resources.getBoolean(R.bool.uses_side_nav)
@ -96,6 +88,9 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
findPreference<Preference>("navigation_bar")?.apply {
isVisible = !resources.getBoolean(R.bool.uses_side_nav)
if (isVisible) {
summary = NavbarUtil.getNavBarItems(requireContext()).filter { it.isVisible }.map { it.title }.joinToString(", ")
}
setOnPreferenceClickListener {
val binding = requireActivity().layoutInflater.inflate(R.layout.simple_options_recycler, null)
val options = NavbarUtil.getNavBarItems(requireContext())
@ -162,6 +157,7 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
.setPositiveButton(R.string.ok) { _, _ ->
NavbarUtil.setNavBarItems(adapter.items, requireContext())
NavbarUtil.setStartFragment(adapter.selectedHomeTabId)
summary = adapter.items.filter { it.isVisible }.map { it.title }.joinToString(", ")
ThemeUtil.recreateMain()
}
.setNegativeButton(R.string.cancel, null)
@ -170,38 +166,44 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
}
}
theme!!.summary = theme!!.entry
theme!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
when(newValue){
findPreference<ListPreference>("ytdlnis_theme")?.apply {
summary = entry
setOnPreferenceChangeListener { _, newValue ->
summary = when(newValue){
"System" -> {
theme!!.summary = getString(R.string.system)
getString(R.string.system)
}
"Dark" -> {
theme!!.summary = getString(R.string.dark)
getString(R.string.dark)
}
else -> {
theme!!.summary = getString(R.string.light)
getString(R.string.light)
}
}
ThemeUtil.updateThemes()
true
}
accent!!.summary = accent!!.entry
accent!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
}
findPreference<ListPreference>("theme_accent")?.apply {
summary = entry
setOnPreferenceChangeListener { _, _ ->
ThemeUtil.updateThemes()
true
}
highContrast!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
}
findPreference<SwitchPreferenceCompat>("high_contrast")?.apply {
setOnPreferenceChangeListener { _, _ ->
ThemeUtil.updateThemes()
true
}
}
showTerminalShareIcon!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { pref: Preference?, _: Any ->
findPreference<SwitchPreferenceCompat>("show_terminal")?.apply {
setOnPreferenceChangeListener { pref, _ ->
val packageManager = requireContext().packageManager
val aliasComponentName = ComponentName(requireContext(), "com.deniscerri.ytdl.terminalShareAlias")
if ((pref as SwitchPreferenceCompat).isChecked){
@ -215,11 +217,11 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
}
true
}
}
displayOverApps = findPreference("display_over_apps")
displayOverApps?.isChecked = Settings.canDrawOverlays(requireContext())
displayOverApps?.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
findPreference<SwitchPreferenceCompat>("display_over_apps")?.apply {
isChecked = Settings.canDrawOverlays(requireContext())
setOnPreferenceChangeListener { _, _ ->
runCatching {
val i = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
@ -231,16 +233,17 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
}
true
}
}
ignoreBatteryOptimization = findPreference("ignore_battery")
ignoreBatteryOptimization!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
findPreference<Preference>("ignore_battery")?.apply {
setOnPreferenceClickListener {
val intent = Intent()
intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
intent.data = Uri.parse("package:" + requireContext().packageName)
startActivity(intent)
true
}
}
findPreference<Preference>("piped_instance")?.setOnPreferenceClickListener {
UiUtil.showPipedInstancesDialog(requireActivity(), preferences.getString("piped_instance", "")!!){
@ -249,13 +252,87 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
}
true
}
findPreference<MultiSelectListPreference>("hide_thumbnails")?.apply {
summary = values.joinToString(", ") { entries[entryValues.indexOf(it)] }
setOnPreferenceChangeListener { _, newValues ->
summary = (newValues as Set<*>).joinToString(", ") { entries[entryValues.indexOf(it)] }
true
}
}
findPreference<MultiSelectListPreference>("modify_download_card")?.apply {
summary = values.joinToString(", ") { entries[entryValues.indexOf(it)] }
setOnPreferenceChangeListener { _, newValues ->
summary = (newValues as Set<*>).joinToString(", ") { entries[entryValues.indexOf(it)] }
true
}
}
findPreference<EditTextPreference>("api_key")?.apply {
val s = getString(R.string.api_key_summary)
summary = if (text.isNullOrBlank()) {
s
}else {
"${s}\n[${text}]"
}
setOnPreferenceChangeListener { _, newValue ->
summary = if ((newValue as String?).isNullOrBlank()) {
s
}else {
"${s}\n[${newValue}]"
}
true
}
}
findPreference<ListPreference>("search_engine")?.apply {
val s = getString(R.string.preferred_search_engine_summary)
summary = if (value.isNullOrBlank()) {
s
}else {
"${s}\n[${entries[entryValues.indexOf(value)]}]"
}
setOnPreferenceChangeListener { _, newValue ->
summary = if ((newValue as String?).isNullOrBlank()) {
s
}else {
"${s}\n[${entries[entryValues.indexOf(newValue)]}]"
}
true
}
}
findPreference<MultiSelectListPreference>("swipe_gesture")?.apply {
val s = getString(R.string.preferred_search_engine_summary)
if (values.size == entries.size) {
summary = "${s}\n[${getString(R.string.all)}]"
}else if (values.size > 0) {
val indexes = entryValues.mapIndexed { index, _ -> index }
summary = "${s}\n[${entries.filterIndexed { index, _ -> indexes.contains(index) }.joinToString(", ")}]"
}else{
summary = s
}
setOnPreferenceChangeListener { _, newValue ->
val newValues = newValue as Set<*>
if (newValues.size == entries.size) {
summary = "${s}\n[${getString(R.string.all)}]"
}else if (newValues.isNotEmpty()) {
val indexes = newValues.mapIndexed { index, _ -> index }
summary = "${s}\n[${entries.filterIndexed { index, _ -> indexes.contains(index) }.joinToString(", ")}]"
}else{
summary = s
}
true
}
}
}
override fun onResume() {
val packageName: String = requireContext().packageName
val pm = requireContext().applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager
if (pm.isIgnoringBatteryOptimizations(packageName)) {
ignoreBatteryOptimization!!.isVisible = false
findPreference<Preference>("ignore_battery")?.isVisible = false
}
super.onResume()
}

@ -91,6 +91,40 @@ class ProcessingSettingsFragment : BaseSettingsFragment() {
true
}
}
findPreference<EditTextPreference>("format_id")?.apply {
val s = getString(R.string.preferred_format_id_summary)
summary = if (text.isNullOrBlank()) {
s
}else {
"${s}\n[${text}]"
}
setOnPreferenceChangeListener { _, newValue ->
summary = if ((newValue as String?).isNullOrBlank()) {
s
}else {
"${s}\n[${newValue}]"
}
true
}
}
findPreference<EditTextPreference>("format_id_audio")?.apply {
val s = getString(R.string.preferred_format_id_summary)
summary = if (text.isNullOrBlank()) {
s
}else {
"${s}\n[${text}]"
}
setOnPreferenceChangeListener { _, newValue ->
summary = if ((newValue as String?).isNullOrBlank()) {
s
}else {
"${s}\n[${newValue}]"
}
true
}
}
}

@ -120,6 +120,7 @@ class NotificationUtil(var context: Context) {
.setPriority(NotificationCompat.PRIORITY_LOW)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setGroup(DOWNLOAD_RUNNING_NOTIFICATION_ID.toString())
.setGroupSummary(true)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.clearActions()
.build()
@ -433,7 +434,7 @@ class NotificationUtil(var context: Context) {
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
.setGroup(DOWNLOAD_RUNNING_NOTIFICATION_ID.toString())
.clearActions()
//.addAction(0, resources.getString(R.string.pause), pauseNotificationPendingIntent)
.addAction(0, resources.getString(R.string.pause), pauseNotificationPendingIntent)
.addAction(0, resources.getString(R.string.cancel), cancelNotificationPendingIntent)
notificationManager.notify(id, notificationBuilder.build())
} catch (e: Exception) {

@ -164,7 +164,7 @@ object ThemeUtil {
}
private fun getThemeColor(context: Context, colorCode: Int): Int {
fun getThemeColor(context: Context, colorCode: Int): Int {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
val accent = sharedPreferences.getString("theme_accent", "blue")
return if (accent == "blue"){

@ -196,6 +196,8 @@ object UiUtil {
val shortcutsChipGroup : ChipGroup = bottomSheet.findViewById(R.id.shortcutsChipGroup)!!
val editShortcuts : Button = bottomSheet.findViewById(R.id.edit_shortcuts)!!
extraCommandsDataFetchingSwitch.isVisible = sharedPreferences.getBoolean("enable_data_fetching_extra_commands", false)
if (item != null){
title.editText!!.setText(item.title)
content.editText!!.setText(item.content)
@ -254,7 +256,7 @@ object UiUtil {
if (item != null){
preferredCommandSwitch.isChecked = item.content == sharedPreferences.getString("preferred_command_template", "")
extraCommandsDataFetchingSwitch.isChecked = item.useAsExtraCommandDataFetching
extraCommandsDataFetchingSwitch.isChecked = item.useAsExtraCommandDataFetching && extraCommandsDataFetchingSwitch.isVisible
extraCommandsSwitch.isChecked = item.useAsExtraCommand
if (item.useAsExtraCommand){

@ -1126,15 +1126,22 @@ class YTDLPUtil(private val context: Context) {
}
private fun getYoutubeExtractorArgs() : String {
val playerClient = sharedPreferences.getString("youtube_player_client", "default,mediaconnect")!!
.ifEmpty { "default,mediaconnect" }
val playerClient = sharedPreferences.getString("youtube_player_client", "default,mediaconnect")!!.split(",").filter { it.isNotBlank() }.toMutableList()
val extractorArgs = mutableListOf<String>()
extractorArgs.add("player_client=${playerClient}")
val poToken = sharedPreferences.getString("youtube_po_token", "")!!
if (poToken.isNotBlank() && !playerClient.contains("web")) {
playerClient.add("web")
}
if (playerClient.isNotEmpty()){
extractorArgs.add("player_client=${playerClient.joinToString(",")}")
}
val lang = Locale.getDefault().language
if (context.getStringArray(R.array.subtitle_langs).contains(lang)) {
extractorArgs.add("lang=$lang")
}
val poToken = sharedPreferences.getString("youtube_po_token", "")!!
if (poToken.isNotBlank()) {
extractorArgs.add("po_token=web+$poToken")
}

@ -171,9 +171,6 @@
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent" />
<View
style="@style/Divider.Horizontal"
android:layout_marginVertical="10dp" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"

@ -22,6 +22,12 @@
app:showAsAction="ifRoom"
android:title="@string/share" />
<item
android:id="@+id/redownload"
android:icon="@drawable/baseline_share_24"
app:showAsAction="ifRoom"
android:title="@string/redownload" />
<item
android:id="@+id/select_all"
android:title="@string/select_all"

@ -423,7 +423,7 @@
<string name="items">أغراض/غرض</string>
<string name="socket_timeout_description">وقت الانتظار قبل الاستسلام، في ثوانٍ</string>
<string name="advanced">متقدم</string>
<string name="data_fetching_extra_commands">أوامر إضافية لجلب البيانات</string>
<string name="data_fetching_extra_command">أوامر إضافية لجلب البيانات</string>
<string name="thumbnail">الصورة المصغرة</string>
<string name="package_name">اسم الحزمة</string>
<string name="websites">مواقع الويب</string>

@ -422,7 +422,7 @@
<string name="data_fetching_extractor_youtube">Məlumat Çıxarıcı (YouTube)</string>
<string name="items">element</string>
<string name="socket_timeout">Uzun Vaxt Çıxışı</string>
<string name="data_fetching_extra_commands">Məlumat Alınması Əlavə Əmrləri</string>
<string name="data_fetching_extra_command">Məlumat Alınması Əlavə Əmrləri</string>
<string name="thumbnail">Miniatür</string>
<string name="other_youtube_extractor_args">Digər YouTube Extractor Hissəcikləri</string>
<string name="socket_timeout_description">Bitirməzdən əvvəl saniyə ilə gözləmək vaxtı</string>

@ -433,5 +433,5 @@
<string name="status">Stav</string>
<string name="deleted">Odstraněno</string>
<string name="websites">Webové stránky</string>
<string name="data_fetching_extra_commands">Doplňkové příkazy pro načítání dat</string>
<string name="data_fetching_extra_command">Doplňkové příkazy pro načítání dat</string>
</resources>

@ -423,7 +423,7 @@
<string name="socket_timeout">Socket-Timeout</string>
<string name="socket_timeout_description">Wartezeit vor dem Aufgeben in Sekunden</string>
<string name="advanced">Erweitert</string>
<string name="data_fetching_extra_commands">Zusätzliche Befehle zum Abrufen von Daten</string>
<string name="data_fetching_extra_command">Zusätzliche Befehle zum Abrufen von Daten</string>
<string name="items">Element(e)</string>
<string name="thumbnail">Vorschaubild</string>
</resources>

@ -425,7 +425,7 @@
<string name="advanced">Avanzado</string>
<string name="other_youtube_extractor_args">Otros argumentos del extractor de YouTube</string>
<string name="socket_timeout_description">Tiempo de espera antes de abandonar, en segundos</string>
<string name="data_fetching_extra_commands">Comandos adicionales para la obtención de datos</string>
<string name="data_fetching_extra_command">Comandos adicionales para la obtención de datos</string>
<string name="thumbnail">Miniaturas</string>
<string name="pause_all">Pausar todo</string>
<string name="resume_all">Reanudar todo</string>

@ -421,7 +421,7 @@
<string name="recode_video">Przekoduj Video</string>
<string name="socket_timeout_description">Czas oczekiwania przed rezygnacją w sekundach</string>
<string name="advanced">Zaawansowane</string>
<string name="data_fetching_extra_commands">Dodatkowe polecenia pobierania danych</string>
<string name="data_fetching_extra_command">Dodatkowe polecenia pobierania danych</string>
<string name="thumbnail">Miniatura</string>
<string name="items">element(y)</string>
<string name="pause_all">Zatrzymaj wszystko</string>

@ -422,7 +422,7 @@
<string name="data_fetching_extractor_youtube">Extrator para busca de dados (YouTube)</string>
<string name="socket_timeout_description">Tempo de esperar antes de desistir, em segundos</string>
<string name="advanced">Avançado</string>
<string name="data_fetching_extra_commands">Comandos extras para busca de dados</string>
<string name="data_fetching_extra_command">Comandos extras para busca de dados</string>
<string name="other_youtube_extractor_args">Outros argumentos do YouTube Extractor</string>
<string name="items">item(s)</string>
<string name="socket_timeout">Tempo limite de Socket</string>

@ -426,7 +426,7 @@
<string name="socket_timeout_description">Время подождать, прежде чем сдаться, в секундах</string>
<string name="other_youtube_extractor_args">Другие аргументы в пользу экстрактора YouTube</string>
<string name="socket_timeout">Тайм-аут сокета</string>
<string name="data_fetching_extra_commands">Дополнительные команды получения данных</string>
<string name="data_fetching_extra_command">Дополнительные команды получения данных</string>
<string name="pause_all">Приостановить все</string>
<string name="resume_all">Возобновить все</string>
<string name="package_name">Имя пакета</string>

@ -433,5 +433,5 @@
<string name="deleted">Odstránené</string>
<string name="websites">Webové stránky</string>
<string name="thumbnail">Miniatúra</string>
<string name="data_fetching_extra_commands">Doplnkové príkazy na načítanie údajov</string>
<string name="data_fetching_extra_command">Doplnkové príkazy na načítanie údajov</string>
</resources>

@ -432,6 +432,6 @@
<string name="advanced">Avancuar</string>
<string name="socket_timeout">Timeout Socket</string>
<string name="socket_timeout_description">Koha për të pritur përpara se të dorëzohet, në sekonda</string>
<string name="data_fetching_extra_commands">Komandat Ekstra per marrjen e të dhënave</string>
<string name="data_fetching_extra_command">Komandat Ekstra per marrjen e të dhënave</string>
<string name="preferred_command_template">Modeli i komandës së preferuar</string>
</resources>

@ -422,7 +422,7 @@
<string name="data_fetching_extractor_youtube">Екстрактор прикупљања података (YouTube)</string>
<string name="socket_timeout">Временско ограничење сокета</string>
<string name="advanced">Напредно</string>
<string name="data_fetching_extra_commands">Додатне команде за прикупљање података</string>
<string name="data_fetching_extra_command">Додатне команде за прикупљање података</string>
<string name="thumbnail">Сличица</string>
<string name="preferred_command_template">Преферирани командни шаблон</string>
<string name="other_youtube_extractor_args">Остали YouTube Extractor аргументи</string>

@ -419,7 +419,7 @@
<string name="recode_video">வீடியோவை மறுபரிசீலனை செய்யுங்கள்</string>
<string name="websites">வலைத்தளங்கள்</string>
<string name="advanced">மேம்பட்ட</string>
<string name="data_fetching_extra_commands">கூடுதல் கட்டளைகளைப் பெறும் தரவு</string>
<string name="data_fetching_extra_command">கூடுதல் கட்டளைகளைப் பெறும் தரவு</string>
<string name="thumbnail">சிறுபடம்</string>
<string name="items">உருப்படி (கள்)</string>
<string name="socket_timeout">சாக்கெட் நேரம் முடிந்தது</string>

@ -426,7 +426,7 @@
<string name="socket_timeout">Тайм-аут сокету</string>
<string name="advanced">Додатково</string>
<string name="thumbnail">Мініатюра</string>
<string name="data_fetching_extra_commands">Додаткові команди отримання даних</string>
<string name="data_fetching_extra_command">Додаткові команди отримання даних</string>
<string name="preferred_command_template">Переважний шаблон команди</string>
<string name="status">Статус</string>
<string name="pause_all">Зупинити все</string>

@ -419,7 +419,7 @@
<string name="playlist_as_album">将播放列表名用作专辑元数据</string>
<string name="playlist_as_album_summary">如果专辑元数据不存在,使用播放列表名</string>
<string name="data_fetching_extractor_youtube">数据获取提取工具YouTube</string>
<string name="data_fetching_extra_commands">数据获取的额外命令</string>
<string name="data_fetching_extra_command">数据获取的额外命令</string>
<string name="thumbnail">缩略图</string>
<string name="items">项目</string>
<string name="other_youtube_extractor_args">其他 YouTube 提取器变量</string>

@ -436,4 +436,5 @@
<string name="advanced">Advanced</string>
<string name="data_fetching_extra_command">Data Fetching Extra Command</string>
<string name="thumbnail">Thumbnail</string>
<string name="data_fetching_extra_command_summary">Enable command templates to be used for data fetching as extra commands</string>
</resources>

@ -15,7 +15,7 @@
android:icon="@drawable/ic_code"
android:key="youtube_po_token"
app:useSimpleSummaryProvider="true"
android:title="PO Token" />
android:title="PO Token [Web]" />
<EditTextPreference
android:defaultValue=""
@ -25,4 +25,12 @@
android:title="@string/other_youtube_extractor_args" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/command_templates">
<SwitchPreferenceCompat
android:icon="@drawable/ic_terminal"
android:key="enable_data_fetching_extra_commands"
android:summary="@string/data_fetching_extra_command_summary"
android:title="@string/data_fetching_extra_command" />
</PreferenceCategory>
</PreferenceScreen>

@ -0,0 +1,9 @@
# What's Changed
- Download History Pagination
- Delete All option for each screen in download queue
- Added Terminal Shortcut
- Grouped Download Notifications
- Advanced Settings
- Crash Fixes
- Read the Github changelog for more info
Loading…
Cancel
Save