newpipe extractor fix
add approx filesize
remember text wrap state in logs/terminal
if quick download enabled, added ability to show the download card while sharing the txt file on any download type
hide download size when enabling cutting because the size is unknown
This commit is contained in:
zaednasr 2024-08-31 21:39:04 +02:00
parent 3df58df7bd
commit 70ed5008ac
No known key found for this signature in database
GPG key ID: 92B1DE23AD3D0E9E
17 changed files with 150 additions and 78 deletions

View file

@ -134,7 +134,7 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4" coreLibraryDesugaring "com.android.tools:desugar_jdk_libs_nio:2.1.0"
implementation "com.github.yausername.youtubedl-android:library:$youtubedlAndroidVer" implementation "com.github.yausername.youtubedl-android:library:$youtubedlAndroidVer"
implementation "com.github.yausername.youtubedl-android:ffmpeg:$youtubedlAndroidVer" implementation "com.github.yausername.youtubedl-android:ffmpeg:$youtubedlAndroidVer"
implementation "com.github.yausername.youtubedl-android:aria2c:$youtubedlAndroidVer" implementation "com.github.yausername.youtubedl-android:aria2c:$youtubedlAndroidVer"
@ -161,7 +161,7 @@ dependencies {
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer" androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer"
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation "com.devbrackets.android:exomedia:4.3.0" implementation 'com.devbrackets.android:exomedia:5.1.0'
implementation("androidx.cardview:cardview:1.0.0") implementation("androidx.cardview:cardview:1.0.0")
implementation("com.squareup.picasso:picasso:2.71828") implementation("com.squareup.picasso:picasso:2.71828")

View file

@ -29,6 +29,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.forEach import androidx.core.view.forEach
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.FragmentContainerView import androidx.fragment.app.FragmentContainerView
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -38,6 +39,7 @@ import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.anggrayudi.storage.file.getAbsolutePath
import com.deniscerri.ytdl.database.DBManager import com.deniscerri.ytdl.database.DBManager
import com.deniscerri.ytdl.database.repository.DownloadRepository import com.deniscerri.ytdl.database.repository.DownloadRepository
import com.deniscerri.ytdl.database.viewmodel.CookieViewModel import com.deniscerri.ytdl.database.viewmodel.CookieViewModel
@ -405,39 +407,45 @@ class MainActivity : BaseActivity() {
intent.getParcelableExtra(Intent.EXTRA_STREAM) intent.getParcelableExtra(Intent.EXTRA_STREAM)
} }
if (preferences.getString("preferred_download_type", "video") == "command"){ var downloadType = DownloadViewModel.Type.valueOf(preferences.getString("preferred_download_type", "video")!!)
val f = File(FileUtil.formatPath(uri?.path ?: "")) if (preferences.getBoolean("quick_download", false) || downloadType == DownloadViewModel.Type.command) {
if (!f.exists()){ val docFile = DocumentFile.fromSingleUri(this, uri!!)
Toast.makeText(context, "Couldn't read file", Toast.LENGTH_LONG).show() if (docFile?.exists() == true){
val bundle = Bundle()
val path = docFile.getAbsolutePath(this)
if (downloadType == DownloadViewModel.Type.auto) {
downloadType = downloadViewModel.getDownloadType(null, path)
}
bundle.putParcelable("result", downloadViewModel.createEmptyResultItem(path))
bundle.putSerializable("type", downloadType)
navController.navigate(R.id.downloadBottomSheetDialog, bundle)
return return
} }
val bundle = Bundle() }
bundle.putParcelable("result", downloadViewModel.createEmptyResultItem(f.absolutePath))
bundle.putSerializable("type", DownloadViewModel.Type.command) val `is` = contentResolver.openInputStream(uri!!)
navController.navigate(R.id.downloadBottomSheetDialog, bundle) val textBuilder = StringBuilder()
}else{ val reader: Reader = BufferedReader(
val `is` = contentResolver.openInputStream(uri!!) InputStreamReader(
val textBuilder = StringBuilder() `is`, Charset.forName(
val reader: Reader = BufferedReader( StandardCharsets.UTF_8.name()
InputStreamReader(
`is`, Charset.forName(
StandardCharsets.UTF_8.name()
)
) )
) )
var c: Int )
while (reader.read().also { c = it } != -1) { var c: Int
textBuilder.append(c.toChar()) while (reader.read().also { c = it } != -1) {
} textBuilder.append(c.toChar())
val bundle = Bundle()
bundle.putString("url", textBuilder.toString())
navController.popBackStack(R.id.homeFragment, true)
navController.navigate(
R.id.homeFragment,
bundle
)
} }
val bundle = Bundle()
bundle.putString("url", textBuilder.toString())
navController.popBackStack(R.id.homeFragment, true)
navController.navigate(
R.id.homeFragment,
bundle
)
} catch (e: Exception) { } catch (e: Exception) {
Toast.makeText(context, "Couldn't read file", Toast.LENGTH_LONG).show()
e.printStackTrace() e.printStackTrace()
} }
}else if (action == Intent.ACTION_VIEW){ }else if (action == Intent.ACTION_VIEW){

View file

@ -219,12 +219,12 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
val formatCard = view.findViewById<MaterialCardView>(R.id.format_card_constraintLayout) val formatCard = view.findViewById<MaterialCardView>(R.id.format_card_constraintLayout)
val chosenFormat = downloadItem.format val chosenFormat = downloadItem.format
UiUtil.populateFormatCard(requireContext(), formatCard, chosenFormat, null) UiUtil.populateFormatCard(requireContext(), formatCard, chosenFormat, null, showSize = downloadItem.downloadSections.isEmpty())
val listener = object : OnFormatClickListener { val listener = object : OnFormatClickListener {
override fun onFormatClick(formatTuple: FormatTuple) { override fun onFormatClick(formatTuple: FormatTuple) {
formatTuple.format?.apply { formatTuple.format?.apply {
downloadItem.format = this downloadItem.format = this
UiUtil.populateFormatCard(requireContext(), formatCard, this, null) UiUtil.populateFormatCard(requireContext(), formatCard, this, null, showSize = downloadItem.downloadSections.isEmpty())
} }
} }
@ -245,12 +245,12 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
val preferredFormat = downloadViewModel.getFormat(formats, Type.audio) val preferredFormat = downloadViewModel.getFormat(formats, Type.audio)
downloadItem.format = preferredFormat downloadItem.format = preferredFormat
downloadItem.allFormats = formats downloadItem.allFormats = formats
UiUtil.populateFormatCard(requireContext(), formatCard, preferredFormat, null) UiUtil.populateFormatCard(requireContext(), formatCard, preferredFormat, null, showSize = downloadItem.downloadSections.isEmpty())
} }
} }
formatCard.setOnClickListener{ formatCard.setOnClickListener{
if (parentFragmentManager.findFragmentByTag("formatSheet") == null){ if (parentFragmentManager.findFragmentByTag("formatSheet") == null){
val bottomSheet = FormatSelectionBottomSheetDialog(listOf(downloadItem), listener) val bottomSheet = FormatSelectionBottomSheetDialog(listOf(downloadItem), listener, canUpdate = !nonSpecific)
bottomSheet.show(parentFragmentManager, "formatSheet") bottomSheet.show(parentFragmentManager, "formatSheet")
} }
} }
@ -319,7 +319,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
if(isUpdatingData){ if(isUpdatingData){
val snack = Snackbar.make(view, context.getString(R.string.please_wait), Snackbar.LENGTH_SHORT) val snack = Snackbar.make(view, context.getString(R.string.please_wait), Snackbar.LENGTH_SHORT)
snack.show() snack.show()
}else{ }else if (!nonSpecific){
val snack = Snackbar.make(view, context.getString(R.string.cut_unavailable), Snackbar.LENGTH_SHORT) val snack = Snackbar.make(view, context.getString(R.string.cut_unavailable), Snackbar.LENGTH_SHORT)
snack.setAction(R.string.update){ snack.setAction(R.string.update){
CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) { CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
@ -332,6 +332,10 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
snack.show() snack.show()
} }
}, },
cutValueChanged = {
downloadItem.downloadSections = it
UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format, showSize = downloadItem.downloadSections.isEmpty())
},
extraCommandsClicked = { extraCommandsClicked = {
val callback = object : ExtraCommandsListener { val callback = object : ExtraCommandsListener {
override fun onChangeExtraCommand(c: String) { override fun onChangeExtraCommand(c: String) {
@ -358,7 +362,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
formats.find { it.format_id == formatID }?.apply { formats.find { it.format_id == formatID }?.apply {
downloadItem.format = this downloadItem.format = this
val formatCard = requireView().findViewById<MaterialCardView>(R.id.format_card_constraintLayout) val formatCard = requireView().findViewById<MaterialCardView>(R.id.format_card_constraintLayout)
UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format, listOf()) UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format, listOf(), showSize = downloadItem.downloadSections.isEmpty())
} }
} }

View file

@ -187,13 +187,13 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
//remove outdated player url of 1hr so it can refetch it in the cut player //remove outdated player url of 1hr so it can refetch it in the cut player
if (result.creationTime > System.currentTimeMillis() - 3600000) result.urls = "" if (result.creationTime > System.currentTimeMillis() - 3600000) result.urls = ""
val fragmentManager = parentFragmentManager val fragmentManager = parentFragmentManager
fragmentAdapter = DownloadFragmentAdapter( fragmentAdapter = DownloadFragmentAdapter(
fragmentManager, fragmentManager,
lifecycle, lifecycle,
result, result,
currentDownloadItem, currentDownloadItem,
nonSpecific = result.url.endsWith(".txt"),
isIncognito = incognito isIncognito = incognito
) )

View file

@ -579,6 +579,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
}, },
cutClicked = {}, cutClicked = {},
cutDisabledClicked = {}, cutDisabledClicked = {},
cutValueChanged = {},
extraCommandsClicked = { extraCommandsClicked = {
val callback = object : ExtraCommandsListener { val callback = object : ExtraCommandsListener {
override fun onChangeExtraCommand(c: String) { override fun onChangeExtraCommand(c: String) {
@ -649,6 +650,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
}, },
cutClicked = {}, cutClicked = {},
cutDisabledClicked = {}, cutDisabledClicked = {},
cutValueChanged = {},
filenameTemplateSet = { checked -> filenameTemplateSet = { checked ->
items.forEach { it.customFileNameTemplate = checked } items.forEach { it.customFileNameTemplate = checked }
CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } } CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } }
@ -846,8 +848,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
audio!!.setOnClickListener { audio!!.setOnClickListener {
lifecycleScope.launch { lifecycleScope.launch {
item = downloadViewModel.switchDownloadType(listOf(item), DownloadViewModel.Type.audio).first()
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
item = downloadViewModel.switchDownloadType(listOf(item), DownloadViewModel.Type.audio).first()
downloadViewModel.updateDownload(item) downloadViewModel.updateDownload(item)
} }
bottomSheet.cancel() bottomSheet.cancel()
@ -856,8 +858,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
video!!.setOnClickListener { video!!.setOnClickListener {
lifecycleScope.launch { lifecycleScope.launch {
item = downloadViewModel.switchDownloadType(listOf(item), DownloadViewModel.Type.video).first()
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
item = downloadViewModel.switchDownloadType(listOf(item), DownloadViewModel.Type.video).first()
downloadViewModel.updateDownload(item) downloadViewModel.updateDownload(item)
} }
bottomSheet.cancel() bottomSheet.cancel()
@ -866,8 +868,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
command!!.setOnClickListener { command!!.setOnClickListener {
lifecycleScope.launch { lifecycleScope.launch {
item = downloadViewModel.switchDownloadType(listOf(item), DownloadViewModel.Type.command).first()
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
item = downloadViewModel.switchDownloadType(listOf(item), DownloadViewModel.Type.command).first()
downloadViewModel.updateDownload(item) downloadViewModel.updateDownload(item)
} }
bottomSheet.cancel() bottomSheet.cancel()

View file

@ -237,7 +237,15 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
val formatCard = view.findViewById<MaterialCardView>(R.id.format_card_constraintLayout) val formatCard = view.findViewById<MaterialCardView>(R.id.format_card_constraintLayout)
val chosenFormat = downloadItem.format val chosenFormat = downloadItem.format
UiUtil.populateFormatCard(requireContext(), formatCard, chosenFormat, downloadItem.allFormats.filter { downloadItem.videoPreferences.audioFormatIDs.contains(it.format_id) })
UiUtil.populateFormatCard(
requireContext(),
formatCard,
chosenFormat,
downloadItem.allFormats.filter { downloadItem.videoPreferences.audioFormatIDs.contains(it.format_id) },
showSize = downloadItem.downloadSections.isEmpty()
)
val listener = object : OnFormatClickListener { val listener = object : OnFormatClickListener {
override fun onFormatClick(formatTuple: FormatTuple) { override fun onFormatClick(formatTuple: FormatTuple) {
formatTuple.format?.apply { formatTuple.format?.apply {
@ -248,7 +256,8 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
downloadItem.videoPreferences.audioFormatIDs.addAll(it) downloadItem.videoPreferences.audioFormatIDs.addAll(it)
} }
UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format, UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format,
if(downloadItem.videoPreferences.removeAudio) listOf() else formatTuple.audioFormats if(downloadItem.videoPreferences.removeAudio) listOf() else formatTuple.audioFormats,
showSize = downloadItem.downloadSections.isEmpty()
) )
} }
@ -272,14 +281,15 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
downloadItem.format = preferredFormat downloadItem.format = preferredFormat
downloadItem.allFormats = formats downloadItem.allFormats = formats
UiUtil.populateFormatCard(requireContext(), formatCard, preferredFormat, UiUtil.populateFormatCard(requireContext(), formatCard, preferredFormat,
if(downloadItem.videoPreferences.removeAudio) listOf() else formats.filter { preferredAudioFormats.contains(it.format_id) } if(downloadItem.videoPreferences.removeAudio) listOf() else formats.filter { preferredAudioFormats.contains(it.format_id) },
showSize = downloadItem.downloadSections.isEmpty()
) )
} }
} }
formatCard.setOnClickListener{ formatCard.setOnClickListener{
if (parentFragmentManager.findFragmentByTag("formatSheet") == null){ if (parentFragmentManager.findFragmentByTag("formatSheet") == null){
val bottomSheet = FormatSelectionBottomSheetDialog(listOf(downloadItem), listener) val bottomSheet = FormatSelectionBottomSheetDialog(listOf(downloadItem), listener, canUpdate = !nonSpecific)
bottomSheet.show(parentFragmentManager, "formatSheet") bottomSheet.show(parentFragmentManager, "formatSheet")
} }
} }
@ -349,7 +359,7 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
if(isUpdatingData){ if(isUpdatingData){
val snack = Snackbar.make(view, context.getString(R.string.please_wait), Snackbar.LENGTH_SHORT) val snack = Snackbar.make(view, context.getString(R.string.please_wait), Snackbar.LENGTH_SHORT)
snack.show() snack.show()
}else{ }else if (!nonSpecific){
val snack = Snackbar.make(view, context.getString(R.string.cut_unavailable), Snackbar.LENGTH_SHORT) val snack = Snackbar.make(view, context.getString(R.string.cut_unavailable), Snackbar.LENGTH_SHORT)
snack.setAction(R.string.update){ snack.setAction(R.string.update){
CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) { CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
@ -362,6 +372,17 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
snack.show() snack.show()
} }
}, },
cutValueChanged = {
downloadItem.downloadSections = it
UiUtil.populateFormatCard(
requireContext(),
formatCard,
downloadItem.format,
if(downloadItem.videoPreferences.removeAudio) listOf()
else downloadItem.allFormats.filter { f -> downloadItem.videoPreferences.audioFormatIDs.contains(f.format_id) },
showSize = downloadItem.downloadSections.isEmpty()
)
},
filenameTemplateSet = { filenameTemplateSet = {
downloadItem.customFileNameTemplate = it downloadItem.customFileNameTemplate = it
}, },
@ -376,7 +397,13 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
}, },
removeAudioClicked = { removeAudioClicked = {
downloadItem.videoPreferences.removeAudio = it downloadItem.videoPreferences.removeAudio = it
UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format, if (it) listOf() else downloadItem.allFormats.filter { downloadItem.videoPreferences.audioFormatIDs.contains(it.format_id) }) UiUtil.populateFormatCard(
requireContext(),
formatCard,
downloadItem.format,
if (it) listOf() else downloadItem.allFormats.filter { downloadItem.videoPreferences.audioFormatIDs.contains(it.format_id) },
showSize = downloadItem.downloadSections.isEmpty()
)
}, },
recodeVideoClicked = { recodeVideoClicked = {
downloadItem.videoPreferences.recodeVideo = it downloadItem.videoPreferences.recodeVideo = it
@ -429,7 +456,8 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
downloadItem.videoPreferences.audioFormatIDs.addAll(arrayListOf(format.format_id)) downloadItem.videoPreferences.audioFormatIDs.addAll(arrayListOf(format.format_id))
val formatCard = requireView().findViewById<MaterialCardView>(R.id.format_card_constraintLayout) val formatCard = requireView().findViewById<MaterialCardView>(R.id.format_card_constraintLayout)
UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format, UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format,
if(downloadItem.videoPreferences.removeAudio) listOf() else listOf(format) if(downloadItem.videoPreferences.removeAudio) listOf() else listOf(format),
showSize = downloadItem.downloadSections.isEmpty()
) )
} }

View file

@ -41,6 +41,7 @@ import kotlinx.coroutines.withContext
class FormatSelectionBottomSheetDialog( class FormatSelectionBottomSheetDialog(
private val _items: List<DownloadItem?>? = null, private val _items: List<DownloadItem?>? = null,
private val _listener: OnFormatClickListener? = null, private val _listener: OnFormatClickListener? = null,
private val canUpdate: Boolean = true,
private val _multipleFormatsListener: OnMultipleFormatClickListener? = null private val _multipleFormatsListener: OnMultipleFormatClickListener? = null
) : BottomSheetDialogFragment() { ) : BottomSheetDialogFragment() {
@ -180,7 +181,7 @@ class FormatSelectionBottomSheetDialog(
refreshBtn = view.findViewById(R.id.format_refresh) refreshBtn = view.findViewById(R.id.format_refresh)
filterBtn.isVisible = chosenFormats.isNotEmpty() || items.all { it!!.url.isYoutubeURL() } filterBtn.isVisible = chosenFormats.isNotEmpty() || items.all { it!!.url.isYoutubeURL() }
if (!isMissingFormats || items.isEmpty() || items.first()?.url?.isEmpty() == true) { if (!isMissingFormats || items.isEmpty() || items.first()?.url?.isEmpty() == true || !canUpdate) {
refreshBtn.visibility = View.GONE refreshBtn.visibility = View.GONE
} }

View file

@ -131,6 +131,7 @@ class DownloadLogFragment : Fragment() {
// contentScrollView.setPadding(0,0,0, // contentScrollView.setPadding(0,0,0,
// (requireContext().resources.displayMetrics.density * 150).toInt() // (requireContext().resources.displayMetrics.density * 150).toInt()
// ) // )
sharedPreferences.edit().putBoolean("wrap_text_log", true).apply()
updateAutoScrollState() updateAutoScrollState()
}else{ }else{
val parent = content.parent as ViewGroup val parent = content.parent as ViewGroup
@ -148,6 +149,7 @@ class DownloadLogFragment : Fragment() {
scrollView.id = R.id.horizontalscroll_output scrollView.id = R.id.horizontalscroll_output
parent.addView(scrollView, 0) parent.addView(scrollView, 0)
updateAutoScrollState() updateAutoScrollState()
sharedPreferences.edit().putBoolean("wrap_text_log", false).apply()
} }
} }
@ -186,6 +188,11 @@ class DownloadLogFragment : Fragment() {
false false
} }
sharedPreferences.getBoolean("wrap_text_log", false).apply {
if (this){
bottomAppBar.menu.performIdentifierAction(R.id.wrap, 0)
}
}
logViewModel.getLogFlowByID(id!!).observe(viewLifecycleOwner){logItem -> logViewModel.getLogFlowByID(id!!).observe(viewLifecycleOwner){logItem ->
kotlin.runCatching { kotlin.runCatching {

View file

@ -256,6 +256,7 @@ class TerminalFragment : Fragment() {
scrollView.removeAllViews() scrollView.removeAllViews()
parent.removeView(scrollView) parent.removeView(scrollView)
parent.addView(output, 0) parent.addView(output, 0)
sharedPreferences.edit().putBoolean("wrap_text_terminal", true).apply()
}else{ }else{
val parent = output?.parent as ViewGroup val parent = output?.parent as ViewGroup
parent.removeView(output) parent.removeView(output)
@ -267,6 +268,7 @@ class TerminalFragment : Fragment() {
scrollView.addView(output) scrollView.addView(output)
scrollView.id = R.id.horizontalscroll_output scrollView.id = R.id.horizontalscroll_output
parent.addView(scrollView, 0) parent.addView(scrollView, 0)
sharedPreferences.edit().putBoolean("wrap_text_terminal", false).apply()
} }
} }
R.id.export_clipboard -> { R.id.export_clipboard -> {
@ -297,6 +299,12 @@ class TerminalFragment : Fragment() {
} }
} }
} }
sharedPreferences.getBoolean("wrap_text_terminal", false).apply {
if (this){
bottomAppBar.menu.performIdentifierAction(R.id.wrap, 0)
}
}
} }
private fun hideCancelFab() { private fun hideCancelFab() {
kotlin.runCatching { kotlin.runCatching {

View file

@ -267,16 +267,21 @@ object Extensions {
val finishingProgressLinesRegex = Pattern.compile("\\[download]\\h+(100%|[a-zA-Z])") val finishingProgressLinesRegex = Pattern.compile("\\[download]\\h+(100%|[a-zA-Z])")
if (line.isNotBlank()) { if (line.isNotBlank()) {
var newLine = "" var newline = ""
val newLines = line.lines() val newLines = line.lines().filter { !lines.contains(it) }
if (!lines.takeLast(3).any { newLines.contains(it) }) { lines.addAll(newLines)
lines.addAll(newLines.dropLast(1)) if (newLines.isNotEmpty()) {
newLine = "\n${newLines.last()}" newLines.last().apply {
if (this.contains("[download")) {
newline = "\n${this}"
}
}
} }
return lines.dropLastWhile {
return lines.distinct().filterNot {
it.contains("[download") && !finishingProgressLinesRegex.matcher(it).find() it.contains("[download") && !finishingProgressLinesRegex.matcher(it).find()
}.joinToString("\n") + newLine }.joinToString("\n") + newline
} }
return lines.filterNot { return lines.filterNot {

View file

@ -119,6 +119,7 @@ class NotificationUtil(var context: Context) {
) )
.setPriority(NotificationCompat.PRIORITY_LOW) .setPriority(NotificationCompat.PRIORITY_LOW)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setGroup(DOWNLOAD_RUNNING_NOTIFICATION_ID.toString())
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE) .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.clearActions() .clearActions()
.build() .build()
@ -148,6 +149,7 @@ class NotificationUtil(var context: Context) {
fun createDownloadServiceNotification( fun createDownloadServiceNotification(
pendingIntent: PendingIntent?, pendingIntent: PendingIntent?,
title: String?, title: String?,
group : Int = DOWNLOAD_RUNNING_NOTIFICATION_ID
): Notification { ): Notification {
val notificationBuilder = getBuilder(DOWNLOAD_SERVICE_CHANNEL_ID) val notificationBuilder = getBuilder(DOWNLOAD_SERVICE_CHANNEL_ID)
@ -167,6 +169,7 @@ class NotificationUtil(var context: Context) {
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setProgress(PROGRESS_MAX, PROGRESS_CURR, true) .setProgress(PROGRESS_MAX, PROGRESS_CURR, true)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
.setGroup(group.toString())
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE) .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.clearActions() .clearActions()
.build() .build()
@ -371,6 +374,7 @@ class NotificationUtil(var context: Context) {
R.drawable.ic_launcher_foreground_large R.drawable.ic_launcher_foreground_large
) )
) )
.setGroup(DOWNLOAD_ERRORED_NOTIFICATION_ID.toString())
.setContentIntent(errorTabPendingIntent) .setContentIntent(errorTabPendingIntent)
.setPriority(NotificationCompat.PRIORITY_MAX) .setPriority(NotificationCompat.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
@ -427,6 +431,7 @@ class NotificationUtil(var context: Context) {
notificationBuilder.setProgress(100, progress, (progress == 0 || progress == 100)) notificationBuilder.setProgress(100, progress, (progress == 0 || progress == 100))
.setContentTitle(title) .setContentTitle(title)
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText)) .setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
.setGroup(DOWNLOAD_RUNNING_NOTIFICATION_ID.toString())
.clearActions() .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) .addAction(0, resources.getString(R.string.cancel), cancelNotificationPendingIntent)
@ -462,6 +467,7 @@ class NotificationUtil(var context: Context) {
notificationBuilder.setProgress(100, progress, progress == 0) notificationBuilder.setProgress(100, progress, progress == 0)
.setContentTitle(title) .setContentTitle(title)
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText)) .setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
.setGroup(DOWNLOAD_TERMINAL_RUNNING_NOTIFICATION_ID.toString())
.clearActions() .clearActions()
.addAction(0, resources.getString(R.string.cancel), cancelNotificationPendingIntent) .addAction(0, resources.getString(R.string.cancel), cancelNotificationPendingIntent)
notificationManager.notify(id, notificationBuilder.build()) notificationManager.notify(id, notificationBuilder.build())
@ -686,6 +692,8 @@ class NotificationUtil(var context: Context) {
const val DOWNLOAD_ERRORED_NOTIFICATION_ID = 60000 const val DOWNLOAD_ERRORED_NOTIFICATION_ID = 60000
const val FORMAT_UPDATING_FINISHED_NOTIFICATION_ID = 70000 const val FORMAT_UPDATING_FINISHED_NOTIFICATION_ID = 70000
const val QUERY_PROCESS_FINISHED_NOTIFICATION_ID = 80000 const val QUERY_PROCESS_FINISHED_NOTIFICATION_ID = 80000
const val DOWNLOAD_RUNNING_NOTIFICATION_ID = 90000
const val DOWNLOAD_TERMINAL_RUNNING_NOTIFICATION_ID = 99000
private const val PROGRESS_MAX = 100 private const val PROGRESS_MAX = 100
private const val PROGRESS_CURR = 0 private const val PROGRESS_CURR = 0

View file

@ -110,7 +110,7 @@ import java.util.function.Predicate
object UiUtil { object UiUtil {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun populateFormatCard(context: Context, formatCard : MaterialCardView, chosenFormat: Format, audioFormats: List<Format>?){ fun populateFormatCard(context: Context, formatCard : MaterialCardView, chosenFormat: Format, audioFormats: List<Format>? = null, showSize: Boolean = true){
var formatNote = chosenFormat.format_note var formatNote = chosenFormat.format_note
if (formatNote.isEmpty()) formatNote = context.getString(R.string.defaultValue) if (formatNote.isEmpty()) formatNote = context.getString(R.string.defaultValue)
else if (formatNote == "best") formatNote = context.getString(R.string.best_quality) else if (formatNote == "best") formatNote = context.getString(R.string.best_quality)
@ -168,7 +168,11 @@ object UiUtil {
var filesize = chosenFormat.filesize var filesize = chosenFormat.filesize
if (!audioFormats.isNullOrEmpty() && filesize > 10L) filesize += audioFormats.sumOf { it.filesize } if (!audioFormats.isNullOrEmpty() && filesize > 10L) filesize += audioFormats.sumOf { it.filesize }
formatCard.findViewById<TextView>(R.id.file_size).apply { formatCard.findViewById<TextView>(R.id.file_size).apply {
text = FileUtil.convertFileSize(filesize) if (showSize) {
text = FileUtil.convertFileSize(filesize)
}else{
text = "?"
}
setOnClickListener { setOnClickListener {
formatCard.callOnClick() formatCard.callOnClick()
} }
@ -1015,16 +1019,14 @@ object UiUtil {
tmp.setOnClickListener { tmp.setOnClickListener {
val c = it as Chip val c = it as Chip
val currentLanguages = editText.text.toString().split(",").filter { f -> f.isNotBlank() }.toMutableList()
if(!c.isChecked){ if(!c.isChecked){
editText.setText(editText.text.toString().replace(c.tag.toString(), "").removeSuffix(",")) editText.setText(currentLanguages.filter { l -> l != c.tag }.joinToString(","))
editText.setSelection(editText.text.length) editText.setSelection(editText.text.length)
}else{ }else{
if (editText.text.isBlank()){ currentLanguages.add(c.tag.toString())
editText.setText(c.tag.toString()) editText.setText(currentLanguages.joinToString(","))
editText.setSelection(editText.text.length) editText.setSelection(editText.text.length)
}else{
editText.append(",${c.tag}")
}
} }
} }
@ -1143,6 +1145,7 @@ object UiUtil {
saveThumbnailClicked: (Boolean) -> Unit, saveThumbnailClicked: (Boolean) -> Unit,
sponsorBlockItemsSet: (values: Array<String>, checkedItems: List<Boolean>) -> Unit, sponsorBlockItemsSet: (values: Array<String>, checkedItems: List<Boolean>) -> Unit,
cutClicked: (VideoCutListener) -> Unit, cutClicked: (VideoCutListener) -> Unit,
cutValueChanged: (String) -> Unit,
cutDisabledClicked: () -> Unit, cutDisabledClicked: () -> Unit,
filenameTemplateSet: (String) -> Unit, filenameTemplateSet: (String) -> Unit,
saveSubtitlesClicked: (Boolean) -> Unit, saveSubtitlesClicked: (Boolean) -> Unit,
@ -1346,7 +1349,7 @@ object UiUtil {
override fun onChangeCut(list: List<String>) { override fun onChangeCut(list: List<String>) {
if (list.isEmpty()){ if (list.isEmpty()){
downloadItem.downloadSections = "" cutValueChanged("")
cut.text = context.getString(R.string.cut) cut.text = context.getString(R.string.cut)
splitByChapters.isEnabled = true splitByChapters.isEnabled = true
@ -1362,7 +1365,7 @@ object UiUtil {
list.forEach { list.forEach {
value += "$it;" value += "$it;"
} }
downloadItem.downloadSections = value cutValueChanged(value)
cut.text = value.dropLast(1) cut.text = value.dropLast(1)
splitByChapters.isEnabled = false splitByChapters.isEnabled = false
@ -1419,6 +1422,7 @@ object UiUtil {
sponsorBlockItemsSet: (Array<String>, List<Boolean>) -> Unit, sponsorBlockItemsSet: (Array<String>, List<Boolean>) -> Unit,
cutClicked: (VideoCutListener) -> Unit, cutClicked: (VideoCutListener) -> Unit,
cutDisabledClicked: () -> Unit, cutDisabledClicked: () -> Unit,
cutValueChanged: (String) -> Unit,
extraCommandsClicked: () -> Unit extraCommandsClicked: () -> Unit
){ ){
val embedThumb = view.findViewById<Chip>(R.id.embed_thumb) val embedThumb = view.findViewById<Chip>(R.id.embed_thumb)
@ -1508,7 +1512,7 @@ object UiUtil {
val cutVideoListener = object : VideoCutListener { val cutVideoListener = object : VideoCutListener {
override fun onChangeCut(list: List<String>) { override fun onChangeCut(list: List<String>) {
if (list.isEmpty()){ if (list.isEmpty()){
downloadItem.downloadSections = "" cutValueChanged("")
cut.text = context.getString(R.string.cut) cut.text = context.getString(R.string.cut)
splitByChapters.isEnabled = true splitByChapters.isEnabled = true
@ -1518,7 +1522,7 @@ object UiUtil {
list.forEach { list.forEach {
value += "$it;" value += "$it;"
} }
downloadItem.downloadSections = value cutValueChanged(value)
cut.text = value.dropLast(1) cut.text = value.dropLast(1)
splitByChapters.isEnabled = false splitByChapters.isEnabled = false

View file

@ -387,12 +387,6 @@ class YTDLPUtil(private val context: Context) {
} }
} }
kotlin.runCatching {
if (!format.has("filesize")) {
format.put("filesize", 0)
}
}
kotlin.runCatching { kotlin.runCatching {
if(format.get("format_note").toString() == "null"){ if(format.get("format_note").toString() == "null"){
format.remove("format_note") format.remove("format_note")
@ -528,7 +522,11 @@ class YTDLPUtil(private val context: Context) {
fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest { fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest {
val useItemURL = sharedPreferences.getBoolean("use_itemurl_instead_playlisturl", false) val useItemURL = sharedPreferences.getBoolean("use_itemurl_instead_playlisturl", false)
val request = if (downloadItem.playlistURL.isNullOrBlank() || downloadItem.playlistTitle.isBlank() || useItemURL){ val request = if (downloadItem.url.endsWith(".txt")) {
YoutubeDLRequest(listOf()).apply {
addOption("-a", downloadItem.url)
}
}else if (downloadItem.playlistURL.isNullOrBlank() || downloadItem.playlistTitle.isBlank() || useItemURL){
YoutubeDLRequest(downloadItem.url) YoutubeDLRequest(downloadItem.url)
}else{ }else{
YoutubeDLRequest(downloadItem.playlistURL!!).apply { YoutubeDLRequest(downloadItem.playlistURL!!).apply {

View file

@ -49,7 +49,7 @@ class TerminalDownloadWorker(
val intent = Intent(context, TerminalActivity::class.java) val intent = Intent(context, TerminalActivity::class.java)
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
val notification = notificationUtil.createDownloadServiceNotification(pendingIntent, command.take(65)) val notification = notificationUtil.createDownloadServiceNotification(pendingIntent, command.take(65), NotificationUtil.DOWNLOAD_TERMINAL_RUNNING_NOTIFICATION_ID)
val foregroundInfo = ForegroundInfo(itemId, notification) val foregroundInfo = ForegroundInfo(itemId, notification)
setForegroundAsync(foregroundInfo) setForegroundAsync(foregroundInfo)

View file

@ -34,7 +34,6 @@
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:resize_mode="fixed_height" app:resize_mode="fixed_height"
app:useDefaultControls="true"
app:use_controller="false" /> app:use_controller="false" />
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout

View file

@ -28,7 +28,6 @@
android:keepScreenOn="true" android:keepScreenOn="true"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:resize_mode="fixed_height" app:resize_mode="fixed_height"
app:useDefaultControls="true"
app:use_controller="true" /> app:use_controller="true" />
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>

View file

@ -4,7 +4,8 @@
<item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item> <item name="android:windowIsTranslucent">true</item>
<item name="android:colorBackgroundCacheHint">@null</item> <item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowContentOverlay">@null</item> <item name="android:windowAnimationStyle">@null</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="preferenceTheme">@style/PreferenceTheme</item> <item name="preferenceTheme">@style/PreferenceTheme</item>
</style> </style>