bug fixes

pull/1242/head
deniscerri 4 weeks ago
parent bf90b2aca3
commit 5cffd60e81
No known key found for this signature in database
GPG Key ID: 95C43D517D830350

@ -12,7 +12,7 @@ def properties = new Properties()
def versionMajor = 1 def versionMajor = 1
def versionMinor = 8 def versionMinor = 8
def versionPatch = 9 def versionPatch = 9
def versionBuild = 0 // bump for dogfood builds, public betas, etc. def versionBuild = 1 // bump for dogfood builds, public betas, etc.
//WHEN RELEASING BETA KEEP THE SAME VERSION AS LATEST STABLE, SO USERS CAN DOWNGRADE //WHEN RELEASING BETA KEEP THE SAME VERSION AS LATEST STABLE, SO USERS CAN DOWNGRADE
def isBeta = false def isBeta = false
@ -237,7 +237,7 @@ dependencies {
implementation 'com.google.accompanist:accompanist-webview:0.36.0' implementation 'com.google.accompanist:accompanist-webview:0.36.0'
implementation 'androidx.compose.material3:material3-android:1.4.0' implementation 'androidx.compose.material3:material3-android:1.4.0'
implementation "io.noties.markwon:core:4.6.2" implementation "io.noties.markwon:core:4.6.2"
implementation("com.github.teamnewpipe:newpipeextractor:v0.26.3") implementation("com.github.teamnewpipe:newpipeextractor:-SNAPSHOT")
implementation("commons-io:commons-io:2.5") implementation("commons-io:commons-io:2.5")
implementation("org.apache.commons:commons-compress:1.12") implementation("org.apache.commons:commons-compress:1.12")

@ -34,7 +34,6 @@
</queries> </queries>
<application <application
android:usesCleartextTraffic="true"
android:name=".App" android:name=".App"
android:allowBackup="false" android:allowBackup="false"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|locale" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|locale"

@ -65,13 +65,14 @@ class DownloadMultipleCardViewModel(application: Application) : AndroidViewModel
} }
.flowOn(Dispatchers.Default) .flowOn(Dispatchers.Default)
private var currentPagingSource: PagingSource<Int, DownloadItemConfigureMultiple>? = null
val processingDownloads: Flow<PagingData<DownloadItemConfigureMultiple>> = Pager( val processingDownloads: Flow<PagingData<DownloadItemConfigureMultiple>> = Pager(
config = PagingConfig( config = PagingConfig(
pageSize = 40, pageSize = 40,
enablePlaceholders = false, enablePlaceholders = false,
initialLoadSize = 80 initialLoadSize = 80
), ),
pagingSourceFactory = { dao.getProcessingDownloads() } pagingSourceFactory = { dao.getProcessingDownloads().also { currentPagingSource = it } }
).flow.cachedIn(viewModelScope) ).flow.cachedIn(viewModelScope)
val processingIds: Flow<List<Long>> = dao.getProcessingDownloadsIds() val processingIds: Flow<List<Long>> = dao.getProcessingDownloadsIds()
@ -90,4 +91,8 @@ class DownloadMultipleCardViewModel(application: Application) : AndroidViewModel
} }
}.flowOn(Dispatchers.IO) }.flowOn(Dispatchers.IO)
fun refreshProcessingDownloads() {
currentPagingSource?.invalidate()
}
} }

@ -149,7 +149,7 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
true true
} }
card.setOnClickListener { card.setOnClickListener {
if (checkedItems.isNotEmpty()) { if (card.isChecked || checkedItems.isNotEmpty()) {
checkCard(card, video.id, video) checkCard(card, video.id, video)
}else{ }else{
onItemClickListener.onCardDetailsClick(video) onItemClickListener.onCardDetailsClick(video)

@ -31,11 +31,15 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.children import androidx.core.view.children
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.setPadding import androidx.core.view.setPadding
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.media3.exoplayer.offline.Download import androidx.media3.exoplayer.offline.Download
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.paging.CombinedLoadStates
import androidx.paging.LoadState
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -233,6 +237,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
processingItemsCount = ids.size processingItemsCount = ids.size
processingDownloadIDs = ids processingDownloadIDs = ids
count.text = "$processingItemsCount ${getString(R.string.selected)}" count.text = "$processingItemsCount ${getString(R.string.selected)}"
downloadMultipleCardViewModel.refreshProcessingDownloads()
} }
} }

@ -74,6 +74,7 @@ class CropVideoBottomSheetDialog(
private lateinit var forwardBtn: MaterialButton private lateinit var forwardBtn: MaterialButton
private lateinit var muteBtn: MaterialButton private lateinit var muteBtn: MaterialButton
private lateinit var resetBtn: Button private lateinit var resetBtn: Button
private lateinit var clearZoom: Button
private lateinit var okBtn: Button private lateinit var okBtn: Button
private lateinit var xEditText: EditText private lateinit var xEditText: EditText
@ -157,6 +158,7 @@ class CropVideoBottomSheetDialog(
forwardBtn = view.findViewById(R.id.forward) forwardBtn = view.findViewById(R.id.forward)
muteBtn = view.findViewById(R.id.mute) muteBtn = view.findViewById(R.id.mute)
resetBtn = view.findViewById(R.id.resetButton) resetBtn = view.findViewById(R.id.resetButton)
clearZoom = view.findViewById(R.id.clearZoom)
okBtn = view.findViewById(R.id.okButton) okBtn = view.findViewById(R.id.okButton)
xEditText = view.findViewById(R.id.crop_x_edittext) xEditText = view.findViewById(R.id.crop_x_edittext)
@ -218,9 +220,23 @@ class CropVideoBottomSheetDialog(
} }
resetBtn.isEnabled = item.videoPreferences.cropValues.isNotBlank() resetBtn.isEnabled = item.videoPreferences.cropValues.isNotBlank()
val formatReference = item.allFormats.sortedByDescending { it.height }.firstOrNull {
(it.height ?: 0) > 0 && (it.width ?: 0) > 0
}
videoWidth = formatReference?.width ?: 0
videoHeight = formatReference?.height ?: 0
clearZoom.setOnClickListener {
listener?.onClearCrop()
ratioChipGroup.findViewById<Chip>(R.id.chip_free).performClick()
refreshTextInputs(0, 0, videoWidth, videoHeight)
applyVideoCoordsToOverlay(0, 0, videoWidth, videoHeight)
}
okBtn.setOnClickListener { okBtn.setOnClickListener {
readTextFieldsToVideoCoords()?.let { (x, y, w, h) -> readTextFieldsToVideoCoords()?.let { (x, y, w, h) ->
listener?.onChangeCrop(x, y, w, h, videoWidth, videoHeight) listener?.onChangeCrop(x, y, w, h, videoWidth, videoHeight)
resetBtn.isEnabled = true
} }
player.stop() player.stop()
dismiss() dismiss()
@ -245,11 +261,6 @@ class CropVideoBottomSheetDialog(
} }
}) })
val formatReference = item.allFormats.sortedByDescending { it.height }.firstOrNull {
(it.height ?: 0) > 0 && (it.width ?: 0) > 0
}
videoWidth = formatReference?.width ?: 0
videoHeight = formatReference?.height ?: 0
showOverlayAndLoad() showOverlayAndLoad()
videoView.setOnClickListener { videoView.setOnClickListener {

@ -153,13 +153,13 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
override fun onActionButtonClick(itemID: Long) { override fun onActionButtonClick(itemID: Long) {
lifecycleScope.launch { lifecycleScope.launch {
runCatching { runCatching {
actionMode?.finish()
val item = withContext(Dispatchers.IO){ val item = withContext(Dispatchers.IO){
downloadViewModel.getItemByID(itemID) downloadViewModel.getItemByID(itemID)
} }
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
downloadViewModel.queueDownloads(listOf(item), true) downloadViewModel.queueDownloads(listOf(item), true)
} }
actionMode?.finish()
}.onFailure { }.onFailure {
Toast.makeText(requireContext(), getString(R.string.error_restarting_download), Toast.LENGTH_LONG).show() Toast.makeText(requireContext(), getString(R.string.error_restarting_download), Toast.LENGTH_LONG).show()
} }

@ -153,7 +153,6 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
override fun onActionButtonClick(itemID: Long) { override fun onActionButtonClick(itemID: Long) {
lifecycleScope.launch { lifecycleScope.launch {
actionMode?.finish()
val item = withContext(Dispatchers.IO){ val item = withContext(Dispatchers.IO){
downloadViewModel.getItemByID(itemID) downloadViewModel.getItemByID(itemID)
} }
@ -166,6 +165,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
bundle bundle
) )
} }
actionMode?.finish()
} }
} }

@ -590,11 +590,10 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() } deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int -> deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
lifecycleScope.launch { lifecycleScope.launch {
actionMode?.finish()
val selectedObjects = getSelectedIDs() val selectedObjects = getSelectedIDs()
historyAdapter.clearCheckedItems() historyAdapter.clearCheckedItems()
historyViewModel.deleteAllWithIDs(selectedObjects, deleteFile[0]) historyViewModel.deleteAllWithIDs(selectedObjects, deleteFile[0])
actionMode?.finish()
} }
} }
deleteDialog.show() deleteDialog.show()
@ -602,22 +601,20 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
} }
R.id.share -> { R.id.share -> {
lifecycleScope.launch { lifecycleScope.launch {
actionMode?.finish()
val selectedObjects = getSelectedIDs() val selectedObjects = getSelectedIDs()
val paths = withContext(Dispatchers.IO){ val paths = withContext(Dispatchers.IO){
historyViewModel.getDownloadPathsFromIDs(selectedObjects) historyViewModel.getDownloadPathsFromIDs(selectedObjects)
} }
FileUtil.shareFileIntent(requireContext(), paths.flatten()) FileUtil.shareFileIntent(requireContext(), paths.flatten())
historyAdapter.clearCheckedItems() historyAdapter.clearCheckedItems()
actionMode?.finish()
} }
true true
} }
R.id.redownload -> { R.id.redownload -> {
lifecycleScope.launch { lifecycleScope.launch {
val selectedObjects = getSelectedIDs() val selectedObjects = getSelectedIDs()
actionMode?.finish()
historyAdapter.clearCheckedItems() historyAdapter.clearCheckedItems()
if (selectedObjects.size == 1) { if (selectedObjects.size == 1) {
val tmp = withContext(Dispatchers.IO) { val tmp = withContext(Dispatchers.IO) {
@ -633,6 +630,7 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
Pair("ignore_duplicates", true) Pair("ignore_duplicates", true)
)) ))
} }
actionMode?.finish()
}else { }else {
val showDownloadCard = sharedPreferences.getBoolean("download_card", true) val showDownloadCard = sharedPreferences.getBoolean("download_card", true)
downloadViewModel.turnHistoryItemsToProcessingDownloads(selectedObjects, downloadNow = !showDownloadCard) downloadViewModel.turnHistoryItemsToProcessingDownloads(selectedObjects, downloadNow = !showDownloadCard)
@ -642,6 +640,7 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{
bundle.putBoolean("ignore_duplicates", true) bundle.putBoolean("ignore_duplicates", true)
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundle) findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundle)
} }
actionMode?.finish()
} }
} }
true true

@ -162,7 +162,6 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
override fun onActionButtonClick(itemID: Long) { override fun onActionButtonClick(itemID: Long) {
lifecycleScope.launch { lifecycleScope.launch {
actionMode?.finish()
val item = withContext(Dispatchers.IO){ val item = withContext(Dispatchers.IO){
downloadViewModel.getItemByID(itemID) downloadViewModel.getItemByID(itemID)
@ -175,6 +174,7 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
}else{ }else{
Toast.makeText(requireContext(), getString(R.string.error_restarting_download), Toast.LENGTH_LONG).show() Toast.makeText(requireContext(), getString(R.string.error_restarting_download), Toast.LENGTH_LONG).show()
} }
actionMode?.finish()
} }
} }

@ -159,7 +159,6 @@ class ScheduledDownloadsFragment : Fragment(), ScheduledDownloadAdapter.OnItemCl
override fun onActionButtonClick(itemID: Long) { override fun onActionButtonClick(itemID: Long) {
lifecycleScope.launch { lifecycleScope.launch {
actionMode?.finish()
runCatching { runCatching {
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
downloadViewModel.resetScheduleTimeForItemsAndStartDownload(listOf(itemID)) downloadViewModel.resetScheduleTimeForItemsAndStartDownload(listOf(itemID))
@ -167,6 +166,7 @@ class ScheduledDownloadsFragment : Fragment(), ScheduledDownloadAdapter.OnItemCl
}.onFailure { }.onFailure {
Toast.makeText(requireContext(), it.message, Toast.LENGTH_LONG).show() Toast.makeText(requireContext(), it.message, Toast.LENGTH_LONG).show()
} }
actionMode?.finish()
} }
} }

@ -1,5 +1,6 @@
package com.deniscerri.ytdl.util package com.deniscerri.ytdl.util
import android.content.ContentUris
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.media.MediaScannerConnection import android.media.MediaScannerConnection
@ -55,30 +56,29 @@ object FileUtil {
val file = File(path) val file = File(path)
val uri = MediaStore.Files.getContentUri("external") val uri = MediaStore.Files.getContentUri("external")
val selection: String
val selectionArgs: Array<String>
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val parentPath = file.parentFile?.absolutePath.orEmpty() // Query by DATA first to find the exact MediaStore row ID, then delete by ID
val primaryRoot = Environment.getExternalStorageDirectory().absolutePath val projection = arrayOf(MediaStore.MediaColumns._ID)
if (parentPath.startsWith(primaryRoot)) { val selection = MediaStore.MediaColumns.DATA + " =?"
val trimmed = parentPath val selectionArgs = arrayOf(file.absolutePath)
.removePrefix(primaryRoot)
.removePrefix(File.separator) val rowUri = contentResolver.query(uri, projection, selection, selectionArgs, null)
val relativePath = if (trimmed.isEmpty()) "" else "$trimmed${File.separator}" ?.use { cursor ->
selection = MediaStore.MediaColumns.RELATIVE_PATH + " =? AND " + if (cursor.moveToFirst()) {
MediaStore.MediaColumns.DISPLAY_NAME + " =?" val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
selectionArgs = arrayOf(relativePath, file.name) ContentUris.withAppendedId(uri, id)
} else { } else null
// Non-primary storage: fall back to DATA query }
selection = MediaStore.MediaColumns.DATA + " =?"
selectionArgs = arrayOf(file.absolutePath) if (rowUri != null) {
contentResolver.delete(rowUri, null, null)
} }
} else { } else {
selection = MediaStore.MediaColumns.DATA + " =?" // Pre-Q: DATA column is reliable
selectionArgs = arrayOf(file.absolutePath) val selection = MediaStore.MediaColumns.DATA + " =?"
val selectionArgs = arrayOf(file.absolutePath)
contentResolver.delete(uri, selection, selectionArgs)
} }
contentResolver.delete(uri, selection, selectionArgs)
} }
fun exists(path: String) : Boolean { fun exists(path: String) : Boolean {

@ -96,16 +96,36 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/clearZoom"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="wrap_content"
android:contentDescription="@string/reset"
android:layout_height="wrap_content"
app:icon="@drawable/baseline_delete_24"
app:iconSize="30dp"
app:iconTint="?android:colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<Space
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/rewind" android:id="@+id/rewind"
style="@style/Widget.Material3.Button.IconButton" style="@style/Widget.Material3.Button.IconButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="40dp"
app:icon="@drawable/exomedia_ic_rewind_white" app:icon="@drawable/exomedia_ic_rewind_white"
app:iconSize="40dp" app:iconSize="40dp"
android:layout_marginHorizontal="15dp"
app:iconTint="?android:colorAccent" /> app:iconTint="?android:colorAccent" />
<Space
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/playpause" android:id="@+id/playpause"
style="@style/Widget.Material3.Button.IconButton" style="@style/Widget.Material3.Button.IconButton"
@ -117,16 +137,24 @@
app:iconSize="40dp" app:iconSize="40dp"
app:iconTint="?android:colorAccent" /> app:iconTint="?android:colorAccent" />
<Space
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/forward" android:id="@+id/forward"
style="@style/Widget.Material3.Button.IconButton" style="@style/Widget.Material3.Button.IconButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="40dp"
app:icon="@drawable/exomedia_ic_fast_forward_white" app:icon="@drawable/exomedia_ic_fast_forward_white"
app:iconSize="40dp" app:iconSize="40dp"
android:layout_marginHorizontal="15dp"
app:iconTint="?android:colorAccent" /> app:iconTint="?android:colorAccent" />
<Space
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/mute" android:id="@+id/mute"
style="@style/Widget.Material3.Button.IconButton" style="@style/Widget.Material3.Button.IconButton"

@ -23,6 +23,7 @@ buildscript {
repositories { repositories {
mavenCentral() mavenCentral()
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
maven { url 'https://central.sonatype.com/repository/maven-snapshots/' }
google() google()
} }

Loading…
Cancel
Save