mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-28 16:00:31 +05:30
Use java.nio.file APIs in download functionality.
This commit is contained in:
parent
9c237861c5
commit
13f8b3d49d
@ -2,6 +2,7 @@ package com.github.libretube.api.obj
|
|||||||
|
|
||||||
import com.github.libretube.db.obj.DownloadItem
|
import com.github.libretube.db.obj.DownloadItem
|
||||||
import com.github.libretube.enums.FileType
|
import com.github.libretube.enums.FileType
|
||||||
|
import java.nio.file.Paths
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ data class Streams(
|
|||||||
val uploaderSubscriberCount: Long = 0,
|
val uploaderSubscriberCount: Long = 0,
|
||||||
val previewFrames: List<PreviewFrames> = emptyList()
|
val previewFrames: List<PreviewFrames> = emptyList()
|
||||||
) {
|
) {
|
||||||
|
@Suppress("NewApi") // The Paths class is desugared.
|
||||||
fun toDownloadItems(
|
fun toDownloadItems(
|
||||||
videoId: String,
|
videoId: String,
|
||||||
fileName: String,
|
fileName: String,
|
||||||
@ -53,7 +55,7 @@ data class Streams(
|
|||||||
type = FileType.VIDEO,
|
type = FileType.VIDEO,
|
||||||
videoId = videoId,
|
videoId = videoId,
|
||||||
fileName = stream?.getQualityString(fileName).orEmpty(),
|
fileName = stream?.getQualityString(fileName).orEmpty(),
|
||||||
path = "",
|
path = Paths.get(""),
|
||||||
url = stream?.url,
|
url = stream?.url,
|
||||||
format = videoFormat,
|
format = videoFormat,
|
||||||
quality = videoQuality
|
quality = videoQuality
|
||||||
@ -70,7 +72,7 @@ data class Streams(
|
|||||||
type = FileType.AUDIO,
|
type = FileType.AUDIO,
|
||||||
videoId = videoId,
|
videoId = videoId,
|
||||||
fileName = stream?.getQualityString(fileName).orEmpty(),
|
fileName = stream?.getQualityString(fileName).orEmpty(),
|
||||||
path = "",
|
path = Paths.get(""),
|
||||||
url = stream?.url,
|
url = stream?.url,
|
||||||
format = audioFormat,
|
format = audioFormat,
|
||||||
quality = audioQuality
|
quality = audioQuality
|
||||||
@ -84,7 +86,7 @@ data class Streams(
|
|||||||
type = FileType.SUBTITLE,
|
type = FileType.SUBTITLE,
|
||||||
videoId = videoId,
|
videoId = videoId,
|
||||||
fileName = "${fileName}_$subtitleCode.srt",
|
fileName = "${fileName}_$subtitleCode.srt",
|
||||||
path = "",
|
path = Paths.get(""),
|
||||||
url = subtitles.find { it.code == subtitleCode }?.url
|
url = subtitles.find { it.code == subtitleCode }?.url
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.github.libretube.db
|
package com.github.libretube.db
|
||||||
|
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.Paths
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import kotlinx.datetime.toLocalDate
|
import kotlinx.datetime.toLocalDate
|
||||||
|
|
||||||
@ -10,4 +12,10 @@ object Converters {
|
|||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun stringToLocalDate(string: String?) = string?.toLocalDate()
|
fun stringToLocalDate(string: String?) = string?.toLocalDate()
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun pathToString(path: Path?) = path?.toString()
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun stringToPath(string: String?) = string?.let { Paths.get(it) }
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.github.libretube.db.obj
|
|||||||
|
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import java.nio.file.Path
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
|
|
||||||
@Entity(tableName = "download")
|
@Entity(tableName = "download")
|
||||||
@ -12,5 +13,5 @@ data class Download(
|
|||||||
val description: String = "",
|
val description: String = "",
|
||||||
val uploader: String = "",
|
val uploader: String = "",
|
||||||
val uploadDate: LocalDate? = null,
|
val uploadDate: LocalDate? = null,
|
||||||
val thumbnailPath: String? = null
|
val thumbnailPath: Path? = null
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,7 @@ import androidx.room.ForeignKey
|
|||||||
import androidx.room.Index
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.github.libretube.enums.FileType
|
import com.github.libretube.enums.FileType
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
tableName = "downloadItem",
|
tableName = "downloadItem",
|
||||||
@ -24,7 +25,7 @@ data class DownloadItem(
|
|||||||
val type: FileType,
|
val type: FileType,
|
||||||
val videoId: String,
|
val videoId: String,
|
||||||
val fileName: String,
|
val fileName: String,
|
||||||
var path: String,
|
var path: Path,
|
||||||
var url: String? = null,
|
var url: String? = null,
|
||||||
var format: String? = null,
|
var format: String? = null,
|
||||||
var quality: String? = null,
|
var quality: String? = null,
|
||||||
|
11
app/src/main/java/com/github/libretube/extensions/Path.kt
Normal file
11
app/src/main/java/com/github/libretube/extensions/Path.kt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package com.github.libretube.extensions
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
|
||||||
|
fun Path.toAndroidUri(): Uri? {
|
||||||
|
@Suppress("NewApi") // The Path class is desugared.
|
||||||
|
return if (exists()) toFile().toUri() else null
|
||||||
|
}
|
@ -8,7 +8,8 @@ import com.github.libretube.constants.IntentData
|
|||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
import com.github.libretube.db.obj.DownloadItem
|
import com.github.libretube.db.obj.DownloadItem
|
||||||
import com.github.libretube.services.DownloadService
|
import com.github.libretube.services.DownloadService
|
||||||
import java.io.File
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.createDirectories
|
||||||
|
|
||||||
object DownloadHelper {
|
object DownloadHelper {
|
||||||
const val VIDEO_DIR = "video"
|
const val VIDEO_DIR = "video"
|
||||||
@ -20,23 +21,21 @@ object DownloadHelper {
|
|||||||
const val DEFAULT_TIMEOUT = 15 * 1000
|
const val DEFAULT_TIMEOUT = 15 * 1000
|
||||||
const val DEFAULT_RETRY = 3
|
const val DEFAULT_RETRY = 3
|
||||||
|
|
||||||
fun getOfflineStorageDir(context: Context): File {
|
private fun getOfflineStorageDir(context: Context): Path {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return context.filesDir
|
val file = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
context.filesDir
|
||||||
return try {
|
} else {
|
||||||
|
try {
|
||||||
context.getExternalFilesDir(null)!!
|
context.getExternalFilesDir(null)!!
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
context.filesDir
|
context.filesDir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return file.toPath()
|
||||||
fun getDownloadDir(context: Context, path: String): File {
|
|
||||||
return File(
|
|
||||||
getOfflineStorageDir(context),
|
|
||||||
path
|
|
||||||
).apply {
|
|
||||||
if (!this.exists()) this.mkdirs()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getDownloadDir(context: Context, path: String): Path {
|
||||||
|
return getOfflineStorageDir(context).resolve(path).createDirectories()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMaxConcurrentDownloads(): Int {
|
fun getMaxConcurrentDownloads(): Int {
|
||||||
|
@ -6,6 +6,7 @@ import android.graphics.BitmapFactory
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
|
import androidx.core.net.toUri
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.disk.DiskCache
|
import coil.disk.DiskCache
|
||||||
import coil.load
|
import coil.load
|
||||||
@ -13,8 +14,9 @@ import coil.request.CachePolicy
|
|||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.github.libretube.api.CronetHelper
|
import com.github.libretube.api.CronetHelper
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
|
import com.github.libretube.extensions.toAndroidUri
|
||||||
import com.github.libretube.util.DataSaverMode
|
import com.github.libretube.util.DataSaverMode
|
||||||
import java.io.File
|
import java.nio.file.Path
|
||||||
|
|
||||||
object ImageHelper {
|
object ImageHelper {
|
||||||
lateinit var imageLoader: ImageLoader
|
lateinit var imageLoader: ImageLoader
|
||||||
@ -54,9 +56,10 @@ object ImageHelper {
|
|||||||
if (!DataSaverMode.isEnabled(target.context)) target.load(url, imageLoader)
|
if (!DataSaverMode.isEnabled(target.context)) target.load(url, imageLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun downloadImage(context: Context, url: String, path: String) {
|
fun downloadImage(context: Context, url: String, path: Path) {
|
||||||
getAsync(context, url) { bitmap ->
|
getAsync(context, url) { bitmap ->
|
||||||
saveImage(context, bitmap, Uri.fromFile(File(path)))
|
@Suppress("NewApi") // The Path class is desugared.
|
||||||
|
saveImage(context, bitmap, path.toFile().toUri())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,10 +72,8 @@ object ImageHelper {
|
|||||||
imageLoader.enqueue(request)
|
imageLoader.enqueue(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDownloadedImage(context: Context, path: String): Bitmap? {
|
fun getDownloadedImage(context: Context, path: Path): Bitmap? {
|
||||||
val file = File(path)
|
return path.toAndroidUri()?.let { getImage(context, it) }
|
||||||
if (!file.exists()) return null
|
|
||||||
return getImage(context, Uri.fromFile(file))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveImage(context: Context, bitmapImage: Bitmap, imagePath: Uri) {
|
private fun saveImage(context: Context, bitmapImage: Bitmap, imagePath: Uri) {
|
||||||
|
@ -38,7 +38,12 @@ import java.io.File
|
|||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.SocketTimeoutException
|
import java.net.SocketTimeoutException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.StandardOpenOption
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
import kotlin.io.path.absolute
|
||||||
|
import kotlin.io.path.createFile
|
||||||
|
import kotlin.io.path.fileSize
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@ -48,7 +53,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okio.BufferedSink
|
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.sink
|
import okio.sink
|
||||||
import okio.source
|
import okio.source
|
||||||
@ -96,7 +100,8 @@ class DownloadService : LifecycleService() {
|
|||||||
RetrofitInstance.api.getStreams(videoId)
|
RetrofitInstance.api.getStreams(videoId)
|
||||||
}
|
}
|
||||||
|
|
||||||
val thumbnailTargetFile = getDownloadFile(DownloadHelper.THUMBNAIL_DIR, fileName)
|
val thumbnailTargetPath = getDownloadPath(DownloadHelper.THUMBNAIL_DIR, fileName)
|
||||||
|
.absolute()
|
||||||
|
|
||||||
val download = Download(
|
val download = Download(
|
||||||
videoId,
|
videoId,
|
||||||
@ -104,13 +109,13 @@ class DownloadService : LifecycleService() {
|
|||||||
streams.description,
|
streams.description,
|
||||||
streams.uploader,
|
streams.uploader,
|
||||||
streams.uploadDate,
|
streams.uploadDate,
|
||||||
thumbnailTargetFile.absolutePath
|
thumbnailTargetPath
|
||||||
)
|
)
|
||||||
Database.downloadDao().insertDownload(download)
|
Database.downloadDao().insertDownload(download)
|
||||||
ImageHelper.downloadImage(
|
ImageHelper.downloadImage(
|
||||||
this@DownloadService,
|
this@DownloadService,
|
||||||
streams.thumbnailUrl,
|
streams.thumbnailUrl,
|
||||||
thumbnailTargetFile.absolutePath
|
thumbnailTargetPath
|
||||||
)
|
)
|
||||||
|
|
||||||
val downloadItems = streams.toDownloadItems(
|
val downloadItems = streams.toDownloadItems(
|
||||||
@ -136,13 +141,11 @@ class DownloadService : LifecycleService() {
|
|||||||
* for the requested file.
|
* for the requested file.
|
||||||
*/
|
*/
|
||||||
private fun start(item: DownloadItem) {
|
private fun start(item: DownloadItem) {
|
||||||
val file = when (item.type) {
|
item.path = when (item.type) {
|
||||||
FileType.AUDIO -> getDownloadFile(DownloadHelper.AUDIO_DIR, item.fileName)
|
FileType.AUDIO -> getDownloadPath(DownloadHelper.AUDIO_DIR, item.fileName)
|
||||||
FileType.VIDEO -> getDownloadFile(DownloadHelper.VIDEO_DIR, item.fileName)
|
FileType.VIDEO -> getDownloadPath(DownloadHelper.VIDEO_DIR, item.fileName)
|
||||||
FileType.SUBTITLE -> getDownloadFile(DownloadHelper.SUBTITLE_DIR, item.fileName)
|
FileType.SUBTITLE -> getDownloadPath(DownloadHelper.SUBTITLE_DIR, item.fileName)
|
||||||
}
|
}.createFile().absolute()
|
||||||
file.createNewFile()
|
|
||||||
item.path = file.absolutePath
|
|
||||||
|
|
||||||
lifecycleScope.launch(coroutineContext) {
|
lifecycleScope.launch(coroutineContext) {
|
||||||
item.id = Database.downloadDao().insertDownloadItem(item).toInt()
|
item.id = Database.downloadDao().insertDownloadItem(item).toInt()
|
||||||
@ -158,8 +161,8 @@ class DownloadService : LifecycleService() {
|
|||||||
downloadQueue[item.id] = true
|
downloadQueue[item.id] = true
|
||||||
val notificationBuilder = getNotificationBuilder(item)
|
val notificationBuilder = getNotificationBuilder(item)
|
||||||
setResumeNotification(notificationBuilder, item)
|
setResumeNotification(notificationBuilder, item)
|
||||||
val file = File(item.path)
|
val path = item.path
|
||||||
var totalRead = file.length()
|
var totalRead = path.fileSize()
|
||||||
val url = URL(item.url ?: return)
|
val url = URL(item.url ?: return)
|
||||||
|
|
||||||
url.getContentLength().let { size ->
|
url.getContentLength().let { size ->
|
||||||
@ -206,7 +209,8 @@ class DownloadService : LifecycleService() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val sink: BufferedSink = file.sink(true).buffer()
|
@Suppress("NewApi") // The StandardOpenOption enum is desugared.
|
||||||
|
val sink = path.sink(StandardOpenOption.APPEND).buffer()
|
||||||
val sourceByte = con.inputStream.source()
|
val sourceByte = con.inputStream.source()
|
||||||
|
|
||||||
var lastTime = System.currentTimeMillis() / 1000
|
var lastTime = System.currentTimeMillis() / 1000
|
||||||
@ -437,14 +441,8 @@ class DownloadService : LifecycleService() {
|
|||||||
/**
|
/**
|
||||||
* Get a [File] from the corresponding download directory and the file name
|
* Get a [File] from the corresponding download directory and the file name
|
||||||
*/
|
*/
|
||||||
private fun getDownloadFile(directory: String, fileName: String): File {
|
private fun getDownloadPath(directory: String, fileName: String): Path {
|
||||||
return File(
|
return DownloadHelper.getDownloadDir(this, directory).resolve(fileName)
|
||||||
DownloadHelper.getDownloadDir(
|
|
||||||
this@DownloadService,
|
|
||||||
directory
|
|
||||||
),
|
|
||||||
fileName
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
@ -15,6 +15,7 @@ import com.github.libretube.databinding.ActivityOfflinePlayerBinding
|
|||||||
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||||
import com.github.libretube.db.DatabaseHolder.Database
|
import com.github.libretube.db.DatabaseHolder.Database
|
||||||
import com.github.libretube.enums.FileType
|
import com.github.libretube.enums.FileType
|
||||||
|
import com.github.libretube.extensions.toAndroidUri
|
||||||
import com.github.libretube.extensions.updateParameters
|
import com.github.libretube.extensions.updateParameters
|
||||||
import com.github.libretube.helpers.PlayerHelper
|
import com.github.libretube.helpers.PlayerHelper
|
||||||
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
|
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
|
||||||
@ -33,7 +34,6 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
|||||||
import com.google.android.exoplayer2.ui.StyledPlayerView
|
import com.google.android.exoplayer2.ui.StyledPlayerView
|
||||||
import com.google.android.exoplayer2.upstream.FileDataSource
|
import com.google.android.exoplayer2.upstream.FileDataSource
|
||||||
import com.google.android.exoplayer2.util.MimeTypes
|
import com.google.android.exoplayer2.util.MimeTypes
|
||||||
import java.io.File
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -107,10 +107,6 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun File.toUri(): Uri? {
|
|
||||||
return if (this.exists()) Uri.fromFile(this) else null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun playVideo() {
|
private fun playVideo() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val downloadFiles = withContext(Dispatchers.IO) {
|
val downloadFiles = withContext(Dispatchers.IO) {
|
||||||
@ -121,9 +117,9 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
val audio = downloadFiles.firstOrNull { it.type == FileType.AUDIO }
|
val audio = downloadFiles.firstOrNull { it.type == FileType.AUDIO }
|
||||||
val subtitle = downloadFiles.firstOrNull { it.type == FileType.SUBTITLE }
|
val subtitle = downloadFiles.firstOrNull { it.type == FileType.SUBTITLE }
|
||||||
|
|
||||||
val videoUri = video?.path?.let { File(it).toUri() }
|
val videoUri = video?.path?.toAndroidUri()
|
||||||
val audioUri = audio?.path?.let { File(it).toUri() }
|
val audioUri = audio?.path?.toAndroidUri()
|
||||||
val subtitleUri = subtitle?.path?.let { File(it).toUri() }
|
val subtitleUri = subtitle?.path?.toAndroidUri()
|
||||||
|
|
||||||
setMediaSource(videoUri, audioUri, subtitleUri)
|
setMediaSource(videoUri, audioUri, subtitleUri)
|
||||||
|
|
||||||
@ -143,7 +139,6 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
.setMimeType(MimeTypes.APPLICATION_SUBRIP)
|
.setMimeType(MimeTypes.APPLICATION_SUBRIP)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
subtitle?.id
|
|
||||||
|
|
||||||
when {
|
when {
|
||||||
videoUri != null && audioUri != null -> {
|
videoUri != null && audioUri != null -> {
|
||||||
|
@ -18,7 +18,8 @@ import com.github.libretube.ui.activities.OfflinePlayerActivity
|
|||||||
import com.github.libretube.ui.viewholders.DownloadsViewHolder
|
import com.github.libretube.ui.viewholders.DownloadsViewHolder
|
||||||
import com.github.libretube.util.TextUtils
|
import com.github.libretube.util.TextUtils
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.io.File
|
import kotlin.io.path.deleteIfExists
|
||||||
|
import kotlin.io.path.fileSize
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ class DownloadsAdapter(
|
|||||||
videoInfo.text = download.uploadDate?.let { TextUtils.localizeDate(it) }
|
videoInfo.text = download.uploadDate?.let { TextUtils.localizeDate(it) }
|
||||||
|
|
||||||
val downloadSize = items.sumOf { it.downloadSize }
|
val downloadSize = items.sumOf { it.downloadSize }
|
||||||
val currentSize = items.sumOf { File(it.path).length() }
|
val currentSize = items.sumOf { it.path.fileSize() }
|
||||||
|
|
||||||
if (downloadSize == -1L) {
|
if (downloadSize == -1L) {
|
||||||
progressBar.isIndeterminate = true
|
progressBar.isIndeterminate = true
|
||||||
@ -96,16 +97,10 @@ class DownloadsAdapter(
|
|||||||
.setTitle(R.string.delete)
|
.setTitle(R.string.delete)
|
||||||
.setMessage(R.string.irreversible)
|
.setMessage(R.string.irreversible)
|
||||||
.setPositiveButton(R.string.okay) { _, _ ->
|
.setPositiveButton(R.string.okay) { _, _ ->
|
||||||
items.map { File(it.path) }.forEach { file ->
|
items.forEach {
|
||||||
runCatching {
|
it.path.deleteIfExists()
|
||||||
if (file.exists()) file.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runCatching {
|
|
||||||
download.thumbnailPath?.let {
|
|
||||||
File(it).delete()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
download.thumbnailPath?.deleteIfExists()
|
||||||
|
|
||||||
runBlocking(Dispatchers.IO) {
|
runBlocking(Dispatchers.IO) {
|
||||||
DatabaseHolder.Database.downloadDao().deleteDownload(download)
|
DatabaseHolder.Database.downloadDao().deleteDownload(download)
|
||||||
|
@ -24,7 +24,7 @@ import com.github.libretube.receivers.DownloadReceiver
|
|||||||
import com.github.libretube.services.DownloadService
|
import com.github.libretube.services.DownloadService
|
||||||
import com.github.libretube.ui.adapters.DownloadsAdapter
|
import com.github.libretube.ui.adapters.DownloadsAdapter
|
||||||
import com.github.libretube.ui.viewholders.DownloadsViewHolder
|
import com.github.libretube.ui.viewholders.DownloadsViewHolder
|
||||||
import java.io.File
|
import kotlin.io.path.fileSize
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
@ -84,7 +84,7 @@ class DownloadsFragment : Fragment() {
|
|||||||
binding.downloads.adapter = DownloadsAdapter(requireContext(), downloads) {
|
binding.downloads.adapter = DownloadsAdapter(requireContext(), downloads) {
|
||||||
var isDownloading = false
|
var isDownloading = false
|
||||||
val ids = it.downloadItems
|
val ids = it.downloadItems
|
||||||
.filter { item -> File(item.path).length() < item.downloadSize }
|
.filter { item -> item.path.fileSize() < item.downloadSize }
|
||||||
.map { item -> item.id }
|
.map { item -> item.id }
|
||||||
|
|
||||||
if (!serviceConnection.isBound) {
|
if (!serviceConnection.isBound) {
|
||||||
|
@ -36,7 +36,7 @@ androidx-work-runtime = { group = "androidx.work", name="work-runtime-ktx", vers
|
|||||||
exoplayer = { group = "com.google.android.exoplayer", name = "exoplayer", version.ref = "exoplayer" }
|
exoplayer = { group = "com.google.android.exoplayer", name = "exoplayer", version.ref = "exoplayer" }
|
||||||
exoplayer-extension-mediasession = { group = "com.google.android.exoplayer", name = "extension-mediasession", version.ref = "exoplayer" }
|
exoplayer-extension-mediasession = { group = "com.google.android.exoplayer", name = "extension-mediasession", version.ref = "exoplayer" }
|
||||||
square-retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
|
square-retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
|
||||||
desugaring = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugaring" }
|
desugaring = { group = "com.android.tools", name = "desugar_jdk_libs_nio", version.ref = "desugaring" }
|
||||||
exoplayer-extension-cronet = { group = "com.google.android.exoplayer", name = "extension-cronet", version.ref = "exoplayer" }
|
exoplayer-extension-cronet = { group = "com.google.android.exoplayer", name = "extension-cronet", version.ref = "exoplayer" }
|
||||||
exoplayer-dash = { group = "com.google.android.exoplayer", name = "exoplayer-dash", version.ref = "exoplayer" }
|
exoplayer-dash = { group = "com.google.android.exoplayer", name = "exoplayer-dash", version.ref = "exoplayer" }
|
||||||
cronet-embedded = { group = "org.chromium.net", name = "cronet-embedded", version.ref = "cronetEmbedded" }
|
cronet-embedded = { group = "org.chromium.net", name = "cronet-embedded", version.ref = "cronetEmbedded" }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user