diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ea6ad86..8fdd821c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/app/build.gradle b/app/build.gradle index 8428e387..1200f98f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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 diff --git a/app/src/main/java/com/deniscerri/ytdl/database/repository/HistoryRepository.kt b/app/src/main/java/com/deniscerri/ytdl/database/repository/HistoryRepository.kt index 3ccb5aaf..79c83095 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/repository/HistoryRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/repository/HistoryRepository.kt @@ -35,6 +35,10 @@ class HistoryRepository(private val historyDao: HistoryDao) { return historyDao.getAllHistoryByURL(url) } + fun getAllByIDs(ids: List) : List { + return historyDao.getAllHistoryByIDs(ids) + } + data class HistoryIDsAndPaths( val id: Long, val downloadPath: List diff --git a/app/src/main/java/com/deniscerri/ytdl/database/repository/ResultRepository.kt b/app/src/main/java/com/deniscerri/ytdl/database/repository/ResultRepository.kt index 0cab5ef6..e1da36a8 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/repository/ResultRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/repository/ResultRepository.kt @@ -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 diff --git a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt index ee2216e9..02593234 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt @@ -616,6 +616,39 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel processingItemsJob = job } + fun turnHistoryItemsToProcessingDownloads(itemIDs: List, downloadNow: Boolean = false) = viewModelScope.launch(Dispatchers.IO) { + val job = viewModelScope.launch(Dispatchers.IO) { + repository.deleteProcessing() + processingItems.emit(true) + try { + val toInsert = mutableListOf() + 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, downloadNow: Boolean = false) = viewModelScope.launch(Dispatchers.IO) { val job = viewModelScope.launch(Dispatchers.IO) { diff --git a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/HistoryViewModel.kt b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/HistoryViewModel.kt index b2c45b07..dc6d2bd4 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/HistoryViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/HistoryViewModel.kt @@ -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) : List { diff --git a/app/src/main/java/com/deniscerri/ytdl/receiver/PauseDownloadNotificationReceiver.kt b/app/src/main/java/com/deniscerri/ytdl/receiver/PauseDownloadNotificationReceiver.kt index 3cf248e1..fa27601a 100644 --- a/app/src/main/java/com/deniscerri/ytdl/receiver/PauseDownloadNotificationReceiver.kt +++ b/app/src/main/java/com/deniscerri/ytdl/receiver/PauseDownloadNotificationReceiver.kt @@ -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() + } + } + } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt index 194079ff..db5f7151 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt @@ -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 = 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) diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/DownloadSettingsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/DownloadSettingsFragment.kt index 7141f97a..e795e40f 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/DownloadSettingsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/DownloadSettingsFragment.kt @@ -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("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("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("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("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("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( diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/GeneralSettingsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/GeneralSettingsFragment.kt index 0074ea74..f4ce09cd 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/GeneralSettingsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/GeneralSettingsFragment.kt @@ -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("app_language")?.apply { + if (value == null) { + value = Locale.getDefault().language + summary = Locale.getDefault().displayLanguage + } + setOnPreferenceChangeListener { _, newValue -> AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue.toString())) true } + } findPreference("label_visibility")?.apply { isVisible = !resources.getBoolean(R.bool.uses_side_nav) @@ -96,6 +88,9 @@ class GeneralSettingsFragment : BaseSettingsFragment() { findPreference("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("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("theme_accent")?.apply { + summary = entry + setOnPreferenceChangeListener { _, _ -> ThemeUtil.updateThemes() true } - highContrast!!.onPreferenceChangeListener = - Preference.OnPreferenceChangeListener { _: Preference?, _: Any -> + } + + findPreference("high_contrast")?.apply { + setOnPreferenceChangeListener { _, _ -> ThemeUtil.updateThemes() true } + } - showTerminalShareIcon!!.onPreferenceChangeListener = - Preference.OnPreferenceChangeListener { pref: Preference?, _: Any -> + findPreference("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("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("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("piped_instance")?.setOnPreferenceClickListener { UiUtil.showPipedInstancesDialog(requireActivity(), preferences.getString("piped_instance", "")!!){ @@ -249,13 +252,87 @@ class GeneralSettingsFragment : BaseSettingsFragment() { } true } + + findPreference("hide_thumbnails")?.apply { + summary = values.joinToString(", ") { entries[entryValues.indexOf(it)] } + setOnPreferenceChangeListener { _, newValues -> + summary = (newValues as Set<*>).joinToString(", ") { entries[entryValues.indexOf(it)] } + true + } + } + + findPreference("modify_download_card")?.apply { + summary = values.joinToString(", ") { entries[entryValues.indexOf(it)] } + setOnPreferenceChangeListener { _, newValues -> + summary = (newValues as Set<*>).joinToString(", ") { entries[entryValues.indexOf(it)] } + true + } + } + + findPreference("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("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("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("ignore_battery")?.isVisible = false } super.onResume() } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/ProcessingSettingsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/ProcessingSettingsFragment.kt index cf83298a..a046b2ef 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/ProcessingSettingsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/ProcessingSettingsFragment.kt @@ -91,6 +91,40 @@ class ProcessingSettingsFragment : BaseSettingsFragment() { true } } + + findPreference("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("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 + } + } } diff --git a/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt index 62a37209..5393b26f 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt @@ -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) { diff --git a/app/src/main/java/com/deniscerri/ytdl/util/ThemeUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/ThemeUtil.kt index 971891e2..5dd97cec 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/ThemeUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/ThemeUtil.kt @@ -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"){ diff --git a/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt index 3eb2dd1a..8d6d9b70 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt @@ -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){ diff --git a/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt index 7777e534..3f90ac1e 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt @@ -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() - 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") } diff --git a/app/src/main/res/layout/create_command_template.xml b/app/src/main/res/layout/create_command_template.xml index 4c16e043..8d59f467 100644 --- a/app/src/main/res/layout/create_command_template.xml +++ b/app/src/main/res/layout/create_command_template.xml @@ -171,9 +171,6 @@ android:textSize="15sp" app:layout_constraintEnd_toEndOf="parent" /> - + + أغراض/غرض وقت الانتظار قبل الاستسلام، في ثوانٍ متقدم - أوامر إضافية لجلب البيانات + أوامر إضافية لجلب البيانات الصورة المصغرة اسم الحزمة مواقع الويب diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 2d74c5e2..5388595b 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -422,7 +422,7 @@ Məlumat Çıxarıcı (YouTube) element Uzun Vaxt Çıxışı - Məlumat Alınması Əlavə Əmrləri + Məlumat Alınması Əlavə Əmrləri Miniatür Digər YouTube Extractor Hissəcikləri Bitirməzdən əvvəl saniyə ilə gözləmək vaxtı diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 38238f8c..15233381 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -433,5 +433,5 @@ Stav Odstraněno Webové stránky - Doplňkové příkazy pro načítání dat + Doplňkové příkazy pro načítání dat \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 6e301873..31e1fa75 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -423,7 +423,7 @@ Socket-Timeout Wartezeit vor dem Aufgeben in Sekunden Erweitert - Zusätzliche Befehle zum Abrufen von Daten + Zusätzliche Befehle zum Abrufen von Daten Element(e) Vorschaubild \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 45c28404..40ad9408 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -425,7 +425,7 @@ Avanzado Otros argumentos del extractor de YouTube Tiempo de espera antes de abandonar, en segundos - Comandos adicionales para la obtención de datos + Comandos adicionales para la obtención de datos Miniaturas Pausar todo Reanudar todo diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index f1bc8554..8661cc3b 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -421,7 +421,7 @@ Przekoduj Video Czas oczekiwania przed rezygnacją w sekundach Zaawansowane - Dodatkowe polecenia pobierania danych + Dodatkowe polecenia pobierania danych Miniatura element(y) Zatrzymaj wszystko diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 7fe341aa..8fb9a46b 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -422,7 +422,7 @@ Extrator para busca de dados (YouTube) Tempo de esperar antes de desistir, em segundos Avançado - Comandos extras para busca de dados + Comandos extras para busca de dados Outros argumentos do YouTube Extractor item(s) Tempo limite de Socket diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index c956297b..d50f653d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -426,7 +426,7 @@ Время подождать, прежде чем сдаться, в секундах Другие аргументы в пользу экстрактора YouTube Тайм-аут сокета - Дополнительные команды получения данных + Дополнительные команды получения данных Приостановить все Возобновить все Имя пакета diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 85b7fa80..e7b7f93f 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -433,5 +433,5 @@ Odstránené Webové stránky Miniatúra - Doplnkové príkazy na načítanie údajov + Doplnkové príkazy na načítanie údajov \ No newline at end of file diff --git a/app/src/main/res/values-sq-rAL/strings.xml b/app/src/main/res/values-sq-rAL/strings.xml index b7776315..3d08c259 100644 --- a/app/src/main/res/values-sq-rAL/strings.xml +++ b/app/src/main/res/values-sq-rAL/strings.xml @@ -432,6 +432,6 @@ Avancuar Timeout Socket Koha për të pritur përpara se të dorëzohet, në sekonda - Komandat Ekstra per marrjen e të dhënave + Komandat Ekstra per marrjen e të dhënave Modeli i komandës së preferuar \ No newline at end of file diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 6ca2d446..a20ebdc9 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -422,7 +422,7 @@ Екстрактор прикупљања података (YouTube) Временско ограничење сокета Напредно - Додатне команде за прикупљање података + Додатне команде за прикупљање података Сличица Преферирани командни шаблон Остали YouTube Extractor аргументи diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 7350d56b..e803cb20 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -419,7 +419,7 @@ வீடியோவை மறுபரிசீலனை செய்யுங்கள் வலைத்தளங்கள் மேம்பட்ட - கூடுதல் கட்டளைகளைப் பெறும் தரவு + கூடுதல் கட்டளைகளைப் பெறும் தரவு சிறுபடம் உருப்படி (கள்) சாக்கெட் நேரம் முடிந்தது diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 65b7aa09..c7244985 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -426,7 +426,7 @@ Тайм-аут сокету Додатково Мініатюра - Додаткові команди отримання даних + Додаткові команди отримання даних Переважний шаблон команди Статус Зупинити все diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 560d1411..5b1e30f8 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -419,7 +419,7 @@ 将播放列表名用作专辑元数据 如果专辑元数据不存在,使用播放列表名 数据获取提取工具(YouTube) - 数据获取的额外命令 + 数据获取的额外命令 缩略图 项目 其他 YouTube 提取器变量 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7b91718b..277408bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -436,4 +436,5 @@ Advanced Data Fetching Extra Command Thumbnail + Enable command templates to be used for data fetching as extra commands \ No newline at end of file diff --git a/app/src/main/res/xml/advanced_preferences.xml b/app/src/main/res/xml/advanced_preferences.xml index 85b314a4..90dc8917 100644 --- a/app/src/main/res/xml/advanced_preferences.xml +++ b/app/src/main/res/xml/advanced_preferences.xml @@ -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]" /> + + + + \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/108010000.txt b/fastlane/metadata/android/en-US/changelogs/108010000.txt new file mode 100644 index 00000000..40b0376b --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/108010000.txt @@ -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