Unifile total fix

This commit is contained in:
Blatzar 2021-11-02 15:25:12 +01:00
parent 4b1e5ab9b1
commit b7c4ab06a7
4 changed files with 109 additions and 71 deletions

View file

@ -57,6 +57,8 @@ class DownloadViewModel : ViewModel() {
for (c in children) {
val childFile = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(context, c.id) ?: continue
println("FFFFFFFFFFFFFFFFFFF $childFile")
if (childFile.fileLength <= 1) continue
val len = childFile.totalBytes
val flen = childFile.fileLength
@ -111,6 +113,6 @@ class DownloadViewModel : ViewModel() {
_availableBytes.postValue(localBytesAvailable)
_downloadBytes.postValue(localDownloadedBytes)
_headerCards.postValue(visual)
_headerCards.postValue(visual.also { println("VISUAL $it") })
}
}

View file

@ -44,7 +44,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
// Open file picker
private val pathPicker = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
val context = AcraApplication.context ?: return@registerForActivityResult
val context = context ?: AcraApplication.context ?: return@registerForActivityResult
// RW perms for the path
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION

View file

@ -5,11 +5,13 @@ import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.*
import android.database.Cursor
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.provider.OpenableColumns
import androidx.annotation.DrawableRes
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
@ -36,6 +38,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import okhttp3.internal.closeQuietly
import java.io.*
import java.lang.Thread.sleep
import java.net.URI
@ -468,6 +471,7 @@ object VideoDownloadManager {
@RequiresApi(Build.VERSION_CODES.Q)
private fun ContentResolver.getExistingDownloadUriOrNullQ(relativePath: String, displayName: String): Uri? {
println("GETTING INFO $relativePath :::::::::. $displayName")
try {
val projection = arrayOf(
MediaStore.MediaColumns._ID,
@ -530,52 +534,6 @@ object VideoDownloadManager {
val fileStream: OutputStream? = null,
)
/**
* Guarantees a directory is present with the dir name (if createMissingDirectories is true).
* Works recursively when '/' is present.
* Will remove any file with the dir name if present and add directory.
*
* @param directoryName if null will use the current path.
* @return UniFile / null if createMissingDirectories = false and folder is not found.
* */
private fun UniFile.gotoDir(directoryName: String?, createMissingDirectories: Boolean = true): UniFile? {
// Can give this error on scoped storage, haven't solved.
// W/DocumentsContract: Failed to create document
// java.lang.IllegalArgumentException: Parent document isn't a directory
try {
val allDirectories = directoryName?.split("/")
return if (allDirectories?.size == 1 || allDirectories == null) {
val found = this.findFile(directoryName)
when {
directoryName.isNullOrBlank() -> this
found?.isDirectory == true -> found
!createMissingDirectories -> null
// Below creates directories
found?.isFile == true -> {
found.delete()
this.createDirectory(directoryName)
}
this.isDirectory -> this.createDirectory(directoryName)
else -> this.parentFile?.createDirectory(directoryName)
}
} else {
var currentDirectory = this
allDirectories.forEach {
// If the next directory is not found it returns the deepest directory possible.
val nextDir = currentDirectory.gotoDir(it, createMissingDirectories)
currentDirectory = nextDir ?: return null
}
currentDirectory
}
} catch (e: Exception) {
logError(e)
return null
}
}
/**
* Sets up the appropriate file and creates a data stream from the file.
* Used for initializing downloads.
@ -594,11 +552,10 @@ object VideoDownloadManager {
val baseFile = context.getBasePath()
if (isScopedStorage && baseFile.first?.isDownloadDir() == true) {
val relativePath = getRelativePath(folder)
val cr = context.contentResolver ?: return StreamData(ERROR_CONTENT_RESOLVER_NOT_FOUND)
val currentExistingFile =
cr.getExistingDownloadUriOrNullQ(relativePath, displayName) // CURRENT FILE WITH THE SAME PATH
cr.getExistingDownloadUriOrNullQ(folder ?: "", displayName) // CURRENT FILE WITH THE SAME PATH
fileLength =
if (currentExistingFile == null || !resume) 0 else (cr.getFileLength(currentExistingFile)
@ -630,7 +587,7 @@ object VideoDownloadManager {
put(MediaStore.MediaColumns.TITLE, name)
if (currentMimeType != null)
put(MediaStore.MediaColumns.MIME_TYPE, currentMimeType)
put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath)
put(MediaStore.MediaColumns.RELATIVE_PATH, folder)
}
cr.insert(
@ -642,7 +599,6 @@ object VideoDownloadManager {
fileStream = cr.openOutputStream(newFileUri, "w" + (if (appendFile) "a" else ""))
?: return StreamData(ERROR_CONTENT_RESOLVER_NOT_FOUND)
} else {
println("SEYUP FFFFFFF")
val subDir = baseFile.first?.gotoDir(folder)
val rFile = subDir?.findFile(displayName)
if (rFile?.exists() != true) {
@ -650,7 +606,7 @@ object VideoDownloadManager {
if (subDir?.createFile(displayName) == null) return StreamData(ERROR_CONTENT_RESOLVER_NOT_FOUND)
} else {
if (resume) {
fileLength = rFile.length()
fileLength = rFile.size()
} else {
fileLength = 0
if (!rFile.delete()) return StreamData(ERROR_CONTENT_RESOLVER_NOT_FOUND)
@ -680,15 +636,14 @@ object VideoDownloadManager {
val basePath = context.getBasePath()
// val relativePath = getRelativePath(folder)
val displayName = getDisplayName(name, extension)
val relativePath = if (isScopedStorage && basePath.first.isDownloadDir()) getRelativePath(folder) else folder
fun deleteFile(): Int {
return delete(context, name, folder, extension, parentId, basePath.first)
return delete(context, name, relativePath, extension, parentId, basePath.first)
}
val stream = setupStream(context, name, folder, extension, tryResume)
val stream = setupStream(context, name, relativePath, extension, tryResume)
if (stream.errorCode != SUCCESS_STREAM) return stream.errorCode
val resume = stream.resume!!
@ -746,7 +701,12 @@ object VideoDownloadManager {
context.setKey(
KEY_DOWNLOAD_INFO,
it.toString(),
DownloadedFileInfo(bytesTotal, folder ?: "", displayName, basePath = basePath.second)
DownloadedFileInfo(
bytesTotal,
relativePath ?: "",
displayName,
basePath = basePath.second
)
)
}
@ -897,6 +857,57 @@ object VideoDownloadManager {
}
}
/**
* Guarantees a directory is present with the dir name (if createMissingDirectories is true).
* Works recursively when '/' is present.
* Will remove any file with the dir name if present and add directory.
*
* @param directoryName if null will use the current path.
* @return UniFile / null if createMissingDirectories = false and folder is not found.
* */
private fun UniFile.gotoDir(directoryName: String?, createMissingDirectories: Boolean = true): UniFile? {
// May give this error on scoped storage.
// W/DocumentsContract: Failed to create document
// java.lang.IllegalArgumentException: Parent document isn't a directory
// Not present in latest testing.
println("Going to dir $directoryName from ${this.uri} ---- ${this.filePath}")
try {
val allDirectories = directoryName?.split("/")
return if (allDirectories?.size == 1 || allDirectories == null) {
val found = this.findFile(directoryName)
when {
directoryName.isNullOrBlank() -> this
found?.isDirectory == true -> found
!createMissingDirectories -> null
// Below creates directories
found?.isFile == true -> {
found.delete()
this.createDirectory(directoryName)
}
this.isDirectory -> this.createDirectory(directoryName)
else -> this.parentFile?.createDirectory(directoryName)
}
} else {
var currentDirectory = this
allDirectories.forEach {
// If the next directory is not found it returns the deepest directory possible.
val nextDir = currentDirectory.gotoDir(it, createMissingDirectories)
currentDirectory = nextDir ?: return null
}
currentDirectory
}
} catch (e: Exception) {
logError(e)
return null
}
}
private fun getDisplayName(name: String, extension: String): String {
return "$name.$extension"
}
@ -918,7 +929,7 @@ object VideoDownloadManager {
)
}
@Deprecated("TODO fix unifile to work with download directory.")
@Deprecated("TODO fix UniFile to work with download directory.")
private fun getRelativePath(folder: String?): String {
return (Environment.DIRECTORY_DOWNLOADS + '/' + folder + '/').replace('/', File.separatorChar)
}
@ -928,8 +939,9 @@ object VideoDownloadManager {
* Should only be used to get a download path.
* */
private fun basePathToFile(context: Context, path: String?): UniFile? {
println("BASE TO FILE $path")
return when {
path == null -> getDownloadDir()
path.isNullOrBlank() -> getDownloadDir()
path.startsWith("content://") -> UniFile.fromUri(context, path.toUri())
else -> UniFile.fromFile(File(path))
}
@ -943,6 +955,8 @@ object VideoDownloadManager {
fun Context.getBasePath(): Pair<UniFile?, String?> {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val basePathSetting = settingsManager.getString(getString(R.string.download_path_key), null)
println("GET CURRENT path $basePathSetting")
return basePathToFile(this, basePathSetting) to basePathSetting
}
@ -970,8 +984,9 @@ object VideoDownloadManager {
}
} else {
val dir = basePath?.gotoDir(folder)
val success = dir?.findFile(displayName)
?.delete()
val file = dir?.findFile(displayName)
println("FOUND FILE ${file?.filePath} ${file?.uri} ${file?.exists()}")
val success = file?.delete()
if (success != true) return ERROR_DELETING_FILE else {
// Cleans up empty directory
if (dir.listFiles()?.isEmpty() == true) dir.delete()
@ -991,7 +1006,7 @@ object VideoDownloadManager {
folder: String?,
parentId: Int?,
startIndex: Int?,
createNotificationCallback: (VideoDownloadManager.CreateNotificationMetadata) -> Unit
createNotificationCallback: (CreateNotificationMetadata) -> Unit
): Int {
val extension = "mp4"
fun logcatPrint(vararg items: Any?) {
@ -1014,17 +1029,19 @@ object VideoDownloadManager {
)
var realIndex = startIndex ?: 0
val stream = setupStream(context, name, folder, extension, realIndex > 0)
val basePath = context.getBasePath()
val relativePath = if (isScopedStorage && basePath.first.isDownloadDir()) getRelativePath(folder) else folder
val stream = setupStream(context, name, relativePath, extension, realIndex > 0)
if (stream.errorCode != SUCCESS_STREAM) return stream.errorCode
if (!stream.resume!!) realIndex = 0
val fileLengthAdd = stream.fileLength!!
val tsIterator = m3u8Helper.hlsYield(listOf(m3u8), realIndex)
// val relativePath = getRelativePath(folder)
val displayName = getDisplayName(name, extension)
val basePath = context.getBasePath()
val fileStream = stream.fileStream!!
@ -1038,7 +1055,7 @@ object VideoDownloadManager {
val totalTs = firstTs.totalTs.toLong()
fun deleteFile(): Int {
return delete(context, name, folder, extension, parentId, basePath.first)
return delete(context, name, relativePath, extension, parentId, basePath.first)
}
/*
Most of the auto generated m3u8 out there have TS of the same size.
@ -1057,7 +1074,7 @@ object VideoDownloadManager {
it.toString(),
DownloadedFileInfo(
(bytesDownloaded * (totalTs / tsProgress.toFloat())).toLong(),
folder ?: "",
relativePath ?: "",
displayName,
tsProgress.toString(),
basePath = basePath.second
@ -1350,9 +1367,23 @@ object VideoDownloadManager {
val file = base?.gotoDir(info.relativePath, false)?.findFile(info.displayName)
// val normalPath = context.getNormalPath(getFile(info.relativePath), info.displayName)
// val dFile = File(normalPath)
if (file?.exists() != true) return null
return DownloadedFileInfoResult(file.length(), info.totalBytes, file.uri)
return DownloadedFileInfoResult(file.size(), info.totalBytes, file.uri)
}
}
/**
* Gets the true download size as Scoped Storage sometimes wrongly returns 0.
* */
fun UniFile.size(): Long {
val len = length()
return if (len <= 1) {
val inputStream = this.openInputStream()
return inputStream.available().toLong().also { inputStream.closeQuietly() }
} else {
len
}
}
@ -1382,7 +1413,12 @@ object VideoDownloadManager {
// val normalPath = context.getNormalPath(getFile(info.relativePath), info.displayName)
// val dFile = File(normalPath)
if (file?.exists() != true) return true
return file.delete()
return try {
file.delete()
} catch (e: Exception) {
val cr = context.contentResolver
cr.delete(file.uri, null, null) > 0
}
}
}