Major performance and bug fixes to downloads (#1164)

This commit is contained in:
Luna712 2024-07-04 11:37:08 -06:00 committed by GitHub
parent 29ec554334
commit 03b8b6e637
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 195 additions and 170 deletions

View file

@ -2,6 +2,7 @@ package com.lagradost.cloudstream3
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.schabi.newpipe.extractor.downloader.Downloader import org.schabi.newpipe.extractor.downloader.Downloader
import org.schabi.newpipe.extractor.downloader.Request import org.schabi.newpipe.extractor.downloader.Request
import org.schabi.newpipe.extractor.downloader.Response import org.schabi.newpipe.extractor.downloader.Response
@ -18,7 +19,7 @@ class DownloaderTestImpl private constructor(builder: OkHttpClient.Builder) : Do
val dataToSend: ByteArray? = request.dataToSend() val dataToSend: ByteArray? = request.dataToSend()
var requestBody: RequestBody? = null var requestBody: RequestBody? = null
if (dataToSend != null) { if (dataToSend != null) {
requestBody = RequestBody.create(null, dataToSend) requestBody = dataToSend.toRequestBody(null, 0, dataToSend.size)
} }
val requestBuilder: okhttp3.Request.Builder = okhttp3.Request.Builder() val requestBuilder: okhttp3.Request.Builder = okhttp3.Request.Builder()
.method(httpMethod, requestBody).url(url) .method(httpMethod, requestBody).url(url)

View file

@ -13,9 +13,10 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.DownloadChildEpisodeBinding import com.lagradost.cloudstream3.databinding.DownloadChildEpisodeBinding
import com.lagradost.cloudstream3.databinding.DownloadHeaderEpisodeBinding import com.lagradost.cloudstream3.databinding.DownloadHeaderEpisodeBinding
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.download.button.DownloadStatusTell
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
@ -26,6 +27,9 @@ const val DOWNLOAD_ACTION_PAUSE_DOWNLOAD = 3
const val DOWNLOAD_ACTION_DOWNLOAD = 4 const val DOWNLOAD_ACTION_DOWNLOAD = 4
const val DOWNLOAD_ACTION_LONG_CLICK = 5 const val DOWNLOAD_ACTION_LONG_CLICK = 5
const val DOWNLOAD_ACTION_GO_TO_CHILD = 0
const val DOWNLOAD_ACTION_LOAD_RESULT = 1
abstract class VisualDownloadCached( abstract class VisualDownloadCached(
open val currentBytes: Long, open val currentBytes: Long,
open val totalBytes: Long, open val totalBytes: Long,
@ -93,110 +97,128 @@ class DownloadAdapter(
private val mediaClickCallback: (DownloadClickEvent) -> Unit, private val mediaClickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(card: VisualDownloadCached?) { fun bind(card: VisualDownloadCached?) {
when (binding) { when (binding) {
is DownloadHeaderEpisodeBinding -> binding.apply { is DownloadHeaderEpisodeBinding -> bindHeader(card as? VisualDownloadHeaderCached)
if (card == null || card !is VisualDownloadHeaderCached) return@apply is DownloadChildEpisodeBinding -> bindChild(card as? VisualDownloadChildCached)
val d = card.data }
}
downloadHeaderPoster.apply { @SuppressLint("SetTextI18n")
setImage(d.poster) private fun bindHeader(card: VisualDownloadHeaderCached?) {
setOnClickListener { if (binding !is DownloadHeaderEpisodeBinding) return
clickCallback.invoke(DownloadHeaderClickEvent(1, d)) card ?: return
} val d = card.data
}
downloadHeaderTitle.text = d.name binding.apply {
val mbString = formatShortFileSize(itemView.context, card.totalBytes) downloadHeaderPoster.apply {
setImage(d.poster)
if (card.child != null) { setOnClickListener {
downloadHeaderGotoChild.isVisible = false clickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_LOAD_RESULT, d))
downloadButton.setDefaultClickListener(card.child, downloadHeaderInfo, mediaClickCallback)
downloadButton.isVisible = true
episodeHolder.setOnClickListener {
mediaClickCallback.invoke(
DownloadClickEvent(
DOWNLOAD_ACTION_PLAY_FILE,
card.child
)
)
}
} else {
downloadButton.isVisible = false
downloadHeaderGotoChild.isVisible = true
try {
downloadHeaderInfo.text =
downloadHeaderInfo.context.getString(R.string.extra_info_format)
.format(
card.totalDownloads,
if (card.totalDownloads == 1) downloadHeaderInfo.context.getString(
R.string.episode
) else downloadHeaderInfo.context.getString(
R.string.episodes
),
mbString
)
} catch (t: Throwable) {
// You probably formatted incorrectly
downloadHeaderInfo.text = "Error"
logError(t)
}
episodeHolder.setOnClickListener {
clickCallback.invoke(DownloadHeaderClickEvent(0, d))
}
} }
} }
is DownloadChildEpisodeBinding -> binding.apply { downloadHeaderTitle.text = d.name
if (card == null || card !is VisualDownloadChildCached) return@apply val formattedSizeString = formatShortFileSize(itemView.context, card.totalBytes)
val d = card.data
val posDur = DataStoreHelper.getViewPos(d.id) if (card.child != null) {
downloadChildEpisodeProgress.apply { downloadHeaderGotoChild.isVisible = false
if (posDur != null) {
val visualPos = posDur.fixVisual() val status = downloadButton.getStatus(card.child.id, card.currentBytes, card.totalBytes)
max = (visualPos.duration / 1000).toInt() if (status == DownloadStatusTell.IsDone) {
progress = (visualPos.position / 1000).toInt() // We do this here instead if we are finished downloading
isVisible = true // so that we can use the value from the view model
} else isVisible = false // rather than extra unneeded disk operations and to prevent a
// delay in updating download icon state.
downloadButton.setProgress(card.currentBytes, card.totalBytes)
downloadButton.applyMetaData(card.child.id, card.currentBytes, card.totalBytes)
// We will let the view model handle this
downloadButton.doSetProgress = false
downloadHeaderInfo.text = formattedSizeString
} else downloadButton.doSetProgress = true
downloadButton.setDefaultClickListener(card.child, downloadHeaderInfo, mediaClickCallback)
downloadButton.isVisible = true
episodeHolder.setOnClickListener {
mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, card.child))
}
} else {
downloadButton.isVisible = false
downloadHeaderGotoChild.isVisible = true
try {
downloadHeaderInfo.text = downloadHeaderInfo.context.getString(R.string.extra_info_format)
.format(
card.totalDownloads,
downloadHeaderInfo.context.resources.getQuantityString(
R.plurals.episodes,
card.totalDownloads
),
formattedSizeString
)
} catch (e: Exception) {
// You probably formatted incorrectly
downloadHeaderInfo.text = "Error"
logError(e)
} }
downloadButton.setDefaultClickListener(card.data, downloadChildEpisodeTextExtra, mediaClickCallback) episodeHolder.setOnClickListener {
clickCallback.invoke(DownloadHeaderClickEvent(DOWNLOAD_ACTION_GO_TO_CHILD, d))
downloadChildEpisodeText.apply {
text = context.getNameFull(d.name, d.episode, d.season)
isSelected = true // Needed for text repeating
} }
}
}
}
downloadChildEpisodeHolder.setOnClickListener { private fun bindChild(card: VisualDownloadChildCached?) {
mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, d)) if (binding !is DownloadChildEpisodeBinding) return
card ?: return
val d = card.data
binding.apply {
val posDur = getViewPos(d.id)
downloadChildEpisodeProgress.apply {
isVisible = posDur != null
posDur?.let {
val visualPos = it.fixVisual()
max = (visualPos.duration / 1000).toInt()
progress = (visualPos.position / 1000).toInt()
} }
} }
val status = downloadButton.getStatus(d.id, card.currentBytes, card.totalBytes)
if (status == DownloadStatusTell.IsDone) {
// We do this here instead if we are finished downloading
// so that we can use the value from the view model
// rather than extra unneeded disk operations and to prevent a
// delay in updating download icon state.
downloadButton.setProgress(card.currentBytes, card.totalBytes)
downloadButton.applyMetaData(d.id, card.currentBytes, card.totalBytes)
// We will let the view model handle this
downloadButton.doSetProgress = false
downloadChildEpisodeTextExtra.text = formatShortFileSize(downloadChildEpisodeTextExtra.context, card.totalBytes)
} else downloadButton.doSetProgress = true
downloadButton.setDefaultClickListener(d, downloadChildEpisodeTextExtra, mediaClickCallback)
downloadButton.isVisible = true
downloadChildEpisodeText.apply {
text = context.getNameFull(d.name, d.episode, d.season)
isSelected = true // Needed for text repeating
}
downloadChildEpisodeHolder.setOnClickListener {
mediaClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, d))
}
} }
} }
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = when (viewType) { val binding = when (viewType) {
VIEW_TYPE_HEADER -> { VIEW_TYPE_HEADER -> DownloadHeaderEpisodeBinding.inflate(inflater, parent, false)
DownloadHeaderEpisodeBinding.inflate( VIEW_TYPE_CHILD -> DownloadChildEpisodeBinding.inflate(inflater, parent, false)
LayoutInflater.from(parent.context),
parent,
false
)
}
VIEW_TYPE_CHILD -> {
DownloadChildEpisodeBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
}
else -> throw IllegalArgumentException("Invalid view type") else -> throw IllegalArgumentException("Invalid view type")
} }
return DownloadViewHolder(binding, clickCallback, mediaClickCallback) return DownloadViewHolder(binding, clickCallback, mediaClickCallback)
@ -207,8 +229,11 @@ class DownloadAdapter(
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
val card = getItem(position) return when (getItem(position)) {
return if (card is VisualDownloadChildCached) VIEW_TYPE_CHILD else VIEW_TYPE_HEADER is VisualDownloadChildCached -> VIEW_TYPE_CHILD
is VisualDownloadHeaderCached -> VIEW_TYPE_HEADER
else -> throw IllegalArgumentException("Invalid data type at position $position")
}
} }
class DiffCallback : DiffUtil.ItemCallback<VisualDownloadCached>() { class DiffCallback : DiffUtil.ItemCallback<VisualDownloadCached>() {

View file

@ -35,6 +35,7 @@ class DownloadChildFragment : Fragment() {
override fun onDestroyView() { override fun onDestroyView() {
downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent -= it } downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent -= it }
downloadDeleteEventListener = null
binding = null binding = null
super.onDestroyView() super.onDestroyView()
} }

View file

@ -40,7 +40,6 @@ import com.lagradost.cloudstream3.ui.result.setLinearListLayout
import com.lagradost.cloudstream3.ui.settings.Globals.TV import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE
import com.lagradost.cloudstream3.utils.DataStore import com.lagradost.cloudstream3.utils.DataStore
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
@ -65,16 +64,8 @@ class DownloadFragment : Fragment() {
this.layoutParams = param this.layoutParams = param
} }
private fun setList(list: List<VisualDownloadHeaderCached>) {
main {
(binding?.downloadList?.adapter as? DownloadAdapter)?.submitList(list)
}
}
override fun onDestroyView() { override fun onDestroyView() {
downloadDeleteEventListener?.let { downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent -= it }
VideoDownloadManager.downloadDeleteEvent -= it
}
downloadDeleteEventListener = null downloadDeleteEventListener = null
binding = null binding = null
super.onDestroyView() super.onDestroyView()
@ -100,12 +91,10 @@ class DownloadFragment : Fragment() {
hideKeyboard() hideKeyboard()
binding?.downloadStorageAppbar?.setAppBarNoScrollFlagsOnTV() binding?.downloadStorageAppbar?.setAppBarNoScrollFlagsOnTV()
observe(downloadsViewModel.noDownloadsText) {
binding?.textNoDownloads?.text = it
}
observe(downloadsViewModel.headerCards) { observe(downloadsViewModel.headerCards) {
setList(it) (binding?.downloadList?.adapter as? DownloadAdapter)?.submitList(it)
binding?.downloadLoading?.isVisible = false binding?.downloadLoading?.isVisible = false
binding?.textNoDownloads?.isVisible = it.isEmpty()
} }
observe(downloadsViewModel.availableBytes) { observe(downloadsViewModel.availableBytes) {
updateStorageInfo(view.context, it, R.string.free_storage, binding?.downloadFreeTxt, binding?.downloadFree) updateStorageInfo(view.context, it, R.string.free_storage, binding?.downloadFreeTxt, binding?.downloadFree)
@ -164,7 +153,7 @@ class DownloadFragment : Fragment() {
private fun handleItemClick(click: DownloadHeaderClickEvent) { private fun handleItemClick(click: DownloadHeaderClickEvent) {
when (click.action) { when (click.action) {
0 -> { DOWNLOAD_ACTION_GO_TO_CHILD -> {
if (!click.data.type.isMovieType()) { if (!click.data.type.isMovieType()) {
val folder = DataStore.getFolderName(DOWNLOAD_EPISODE_CACHE, click.data.id.toString()) val folder = DataStore.getFolderName(DOWNLOAD_EPISODE_CACHE, click.data.id.toString())
activity?.navigate( activity?.navigate(
@ -173,7 +162,7 @@ class DownloadFragment : Fragment() {
) )
} }
} }
1 -> { DOWNLOAD_ACTION_LOAD_RESULT -> {
(activity as AppCompatActivity?)?.loadResult(click.data.url, click.data.apiName) (activity as AppCompatActivity?)?.loadResult(click.data.url, click.data.apiName)
} }
} }

View file

@ -16,17 +16,11 @@ import com.lagradost.cloudstream3.utils.DataStore.getFolderName
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager import com.lagradost.cloudstream3.utils.VideoDownloadManager.getDownloadFileInfoAndUpdateSettings
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class DownloadViewModel : ViewModel() { class DownloadViewModel : ViewModel() {
private val _noDownloadsText = MutableLiveData<String>().apply {
value = ""
}
val noDownloadsText: LiveData<String> = _noDownloadsText
private val _headerCards = private val _headerCards =
MutableLiveData<List<VisualDownloadHeaderCached>>().apply { listOf<VisualDownloadHeaderCached>() } MutableLiveData<List<VisualDownloadHeaderCached>>().apply { listOf<VisualDownloadHeaderCached>() }
val headerCards: LiveData<List<VisualDownloadHeaderCached>> = _headerCards val headerCards: LiveData<List<VisualDownloadHeaderCached>> = _headerCards
@ -43,8 +37,8 @@ class DownloadViewModel : ViewModel() {
fun updateList(context: Context) = viewModelScope.launchSafe { fun updateList(context: Context) = viewModelScope.launchSafe {
val children = withContext(Dispatchers.IO) { val children = withContext(Dispatchers.IO) {
val headers = context.getKeys(DOWNLOAD_EPISODE_CACHE) context.getKeys(DOWNLOAD_EPISODE_CACHE)
headers.mapNotNull { context.getKey<VideoDownloadHelper.DownloadEpisodeCached>(it) } .mapNotNull { context.getKey<VideoDownloadHelper.DownloadEpisodeCached>(it) }
.distinctBy { it.id } // Remove duplicates .distinctBy { it.id } // Remove duplicates
} }
@ -57,10 +51,10 @@ class DownloadViewModel : ViewModel() {
// Gets all children downloads // Gets all children downloads
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
for (c in children) { children.forEach { c ->
val childFile = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(context, c.id) ?: continue val childFile = getDownloadFileInfoAndUpdateSettings(context, c.id) ?: return@forEach
if (childFile.fileLength <= 1) continue if (childFile.fileLength <= 1) return@forEach
val len = childFile.totalBytes val len = childFile.totalBytes
val flen = childFile.fileLength val flen = childFile.fileLength

View file

@ -1,7 +1,7 @@
package com.lagradost.cloudstream3.ui.download.button package com.lagradost.cloudstream3.ui.download.button
import android.content.Context import android.content.Context
import android.text.format.Formatter import android.text.format.Formatter.formatShortFileSize
import android.util.AttributeSet import android.util.AttributeSet
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.TextView import android.widget.TextView
@ -9,6 +9,8 @@ import androidx.annotation.LayoutRes
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar import androidx.core.widget.ContentLoadingProgressBar
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.mainWork
import com.lagradost.cloudstream3.utils.VideoDownloadManager import com.lagradost.cloudstream3.utils.VideoDownloadManager
typealias DownloadStatusTell = VideoDownloadManager.DownloadType typealias DownloadStatusTell = VideoDownloadManager.DownloadType
@ -34,7 +36,7 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) :
lateinit var progressBar: ContentLoadingProgressBar lateinit var progressBar: ContentLoadingProgressBar
var progressText: TextView? = null var progressText: TextView? = null
/*val gid: String? get() = sessionIdToGid[persistentId] /* val gid: String? get() = sessionIdToGid[persistentId]
// used for resuming data // used for resuming data
var _lastRequestOverride: UriRequest? = null var _lastRequestOverride: UriRequest? = null
@ -44,7 +46,7 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) :
_lastRequestOverride = value _lastRequestOverride = value
} }
var files: List<AbstractClient.JsonFile> = emptyList()*/ var files: List<AbstractClient.JsonFile> = emptyList() */
protected var isZeroBytes: Boolean = true protected var isZeroBytes: Boolean = true
fun inflate(@LayoutRes layout: Int) { fun inflate(@LayoutRes layout: Int) {
@ -55,9 +57,12 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) :
resetViewData() resetViewData()
} }
var doSetProgress = true
open fun resetViewData() { open fun resetViewData() {
// lastRequest = null // lastRequest = null
isZeroBytes = true isZeroBytes = true
doSetProgress = true
persistentId = null persistentId = null
} }
@ -68,37 +73,45 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) :
persistentId = id persistentId = id
currentMetaData.id = id currentMetaData.id = id
VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(context, id)?.let { savedData -> if (!doSetProgress) return
val downloadedBytes = savedData.fileLength
val totalBytes = savedData.totalBytes
/*lastRequest = savedData.uriRequest ioSafe {
files = savedData.files val savedData = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(context, id)
var totalBytes: Long = 0 mainWork {
var downloadedBytes: Long = 0 if (savedData != null) {
for (file in savedData.files) { val downloadedBytes = savedData.fileLength
downloadedBytes += file.completedLength val totalBytes = savedData.totalBytes
totalBytes += file.length
}*/ setProgress(downloadedBytes, totalBytes)
setProgress(downloadedBytes, totalBytes) applyMetaData(id, downloadedBytes, totalBytes)
// some extra padding for just in case } else run { resetView() }
val status = VideoDownloadManager.downloadStatus[id]
?: if (downloadedBytes > 1024L && downloadedBytes + 1024L >= totalBytes) DownloadStatusTell.IsDone else DownloadStatusTell.IsPaused
currentMetaData.apply {
this.id = id
this.downloadedLength = downloadedBytes
this.totalLength = totalBytes
this.status = status
} }
setStatus(status)
} ?: run {
resetView()
} }
} }
abstract fun setStatus(status: VideoDownloadManager.DownloadType?) abstract fun setStatus(status: VideoDownloadManager.DownloadType?)
fun getStatus(id:Int, downloadedBytes: Long, totalBytes: Long): DownloadStatusTell {
// some extra padding for just in case
return VideoDownloadManager.downloadStatus[id]
?: if (downloadedBytes > 1024L && downloadedBytes + 1024L >= totalBytes) {
DownloadStatusTell.IsDone
} else DownloadStatusTell.IsPaused
}
fun applyMetaData(id:Int, downloadedBytes: Long, totalBytes: Long) {
val status = getStatus(id, downloadedBytes, totalBytes)
currentMetaData.apply {
this.id = id
this.downloadedLength = downloadedBytes
this.totalLength = totalBytes
this.status = status
}
setStatus(status)
}
open fun setProgress(downloadedBytes: Long, totalBytes: Long) { open fun setProgress(downloadedBytes: Long, totalBytes: Long) {
isZeroBytes = downloadedBytes == 0L isZeroBytes = downloadedBytes == 0L
progressBar.post { progressBar.post {
@ -124,13 +137,15 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) :
if (isZeroBytes) { if (isZeroBytes) {
progressText?.isVisible = false progressText?.isVisible = false
} else { } else {
progressText?.apply { if (doSetProgress) {
val currentMbString = Formatter.formatShortFileSize(context, downloadedBytes) progressText?.apply {
val totalMbString = Formatter.formatShortFileSize(context, totalBytes) val currentFormattedSizeString = formatShortFileSize(context, downloadedBytes)
text = val totalFormattedSizeString = formatShortFileSize(context, totalBytes)
//if (isTextPercentage) "%d%%".format(setCurrentBytes * 100L / setTotalBytes) else text =
context?.getString(R.string.download_size_format) // if (isTextPercentage) "%d%%".format(setCurrentBytes * 100L / setTotalBytes) else
?.format(currentMbString, totalMbString) context?.getString(R.string.download_size_format)
?.format(currentFormattedSizeString, totalFormattedSizeString)
}
} }
} }
@ -167,8 +182,8 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) :
override fun onAttachedToWindow() { override fun onAttachedToWindow() {
VideoDownloadManager.downloadStatusEvent += ::downloadStatusEvent VideoDownloadManager.downloadStatusEvent += ::downloadStatusEvent
//VideoDownloadManager.downloadDeleteEvent += ::downloadDeleteEvent // VideoDownloadManager.downloadDeleteEvent += ::downloadDeleteEvent
//VideoDownloadManager.downloadEvent += ::downloadEvent // VideoDownloadManager.downloadEvent += ::downloadEvent
VideoDownloadManager.downloadProgressEvent += ::downloadProgressEvent VideoDownloadManager.downloadProgressEvent += ::downloadProgressEvent
val pid = persistentId val pid = persistentId
@ -182,8 +197,8 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) :
override fun onDetachedFromWindow() { override fun onDetachedFromWindow() {
VideoDownloadManager.downloadStatusEvent -= ::downloadStatusEvent VideoDownloadManager.downloadStatusEvent -= ::downloadStatusEvent
//VideoDownloadManager.downloadDeleteEvent -= ::downloadDeleteEvent // VideoDownloadManager.downloadDeleteEvent -= ::downloadDeleteEvent
//VideoDownloadManager.downloadEvent -= ::downloadEvent // VideoDownloadManager.downloadEvent -= ::downloadEvent
VideoDownloadManager.downloadProgressEvent -= ::downloadProgressEvent VideoDownloadManager.downloadProgressEvent -= ::downloadProgressEvent
super.onDetachedFromWindow() super.onDetachedFromWindow()
@ -198,5 +213,4 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) :
* Get a clean slate again, might be useful in recyclerview? * Get a clean slate again, might be useful in recyclerview?
* */ * */
abstract fun resetView() abstract fun resetView()
} }

View file

@ -13,7 +13,6 @@ import androidx.annotation.MainThread
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
@ -29,7 +28,6 @@ import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager import com.lagradost.cloudstream3.utils.VideoDownloadManager
import com.lagradost.cloudstream3.utils.VideoDownloadManager.KEY_RESUME_PACKAGES import com.lagradost.cloudstream3.utils.VideoDownloadManager.KEY_RESUME_PACKAGES
open class PieFetchButton(context: Context, attributeSet: AttributeSet) : open class PieFetchButton(context: Context, attributeSet: AttributeSet) :
BaseFetchButton(context, attributeSet) { BaseFetchButton(context, attributeSet) {
@ -303,6 +301,7 @@ open class PieFetchButton(context: Context, attributeSet: AttributeSet) :
setStatus(null) setStatus(null)
currentMetaData = DownloadMetadata(0, 0, 0, null) currentMetaData = DownloadMetadata(0, 0, 0, null)
isZeroBytes = true isZeroBytes = true
doSetProgress = true
progressBar.progress = 0 progressBar.progress = 0
} }

View file

@ -17,6 +17,7 @@ import androidx.work.Data
import androidx.work.ExistingWorkPolicy import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
@ -28,7 +29,6 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.services.VideoDownloadService import com.lagradost.cloudstream3.services.VideoDownloadService
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.Coroutines.main
@ -234,10 +234,10 @@ object VideoDownloadManager {
return cachedBitmaps[url] return cachedBitmaps[url]
} }
val bitmap = com.bumptech.glide.Glide.with(this) val bitmap = Glide.with(this)
.asBitmap() .asBitmap()
.load(GlideUrl(url) { headers ?: emptyMap() }) .load(GlideUrl(url) { headers ?: emptyMap() })
.into(720, 720) .submit(720, 720)
.get() .get()
if (bitmap != null) { if (bitmap != null) {

View file

@ -143,17 +143,14 @@
<TextView <TextView
android:id="@+id/text_no_downloads" android:id="@+id/text_no_downloads"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_gravity="center"
android:layout_marginTop="8dp" android:layout_margin="30dp"
android:layout_marginEnd="8dp" android:text="@string/downloads_empty"
android:textAlignment="center" android:gravity="center"
android:textSize="20sp" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" tools:visibility="visible" />
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- <!--
<ProgressBar <ProgressBar

View file

@ -149,6 +149,7 @@
<string name="download_canceled">Download Canceled</string> <string name="download_canceled">Download Canceled</string>
<string name="download_done">Download Done</string> <string name="download_done">Download Done</string>
<string name="download_format" translatable="false">%s - %s</string> <string name="download_format" translatable="false">%s - %s</string>
<string name="downloads_empty">There are currently no downloads.</string>
<string name="update_started">Update Started</string> <string name="update_started">Update Started</string>
<string name="stream">Network stream</string> <string name="stream">Network stream</string>
<string name="open_local_video">Open local video</string> <string name="open_local_video">Open local video</string>
@ -340,6 +341,10 @@
<string name="livestreams">Livestreams</string> <string name="livestreams">Livestreams</string>
<string name="nsfw">NSFW</string> <string name="nsfw">NSFW</string>
<string name="others">Others</string> <string name="others">Others</string>
<plurals name="episodes" translatable="false">
<item quantity="one">@string/episode</item>
<item quantity="other">@string/episodes</item>
</plurals>
<!--singular--> <!--singular-->
<string name="movies_singular">Movie</string> <string name="movies_singular">Movie</string>
<string name="tv_series_singular">Series</string> <string name="tv_series_singular">Series</string>