download movie stuff

This commit is contained in:
LagradOst 2021-07-25 16:25:09 +02:00
parent 423ee144fd
commit 77778bdbb6
16 changed files with 176 additions and 149 deletions

View File

@ -205,6 +205,9 @@ class MainActivity : AppCompatActivity() {
// this.startService(mServiceIntent)
//}
//settingsManager.getBoolean("disable_automatic_data_downloads", true) &&
// TODO RETURN TO TRUE
/*
if (isUsingMobileData()) {
Toast.makeText(this, "Downloads not resumed on mobile data", Toast.LENGTH_LONG).show()
} else {
@ -226,7 +229,10 @@ class MainActivity : AppCompatActivity() {
resumeQueue?.sortedBy { it.index }?.forEach {
VideoDownloadManager.downloadFromResume(this, it.pkg)
}
}
}*/
/*
val castContext = CastContext.getSharedInstance(applicationContext)
fun buildMediaQueueItem(video: String): MediaQueueItem {

View File

@ -214,7 +214,7 @@ class DubbedAnimeProvider : MainAPI() {
val img = fixUrl(document.select("div.fkimgs > img").attr("src"))
return AnimeLoadResponse(
null, null, title, slug, this.name, TvType.Anime, img, year, ArrayList(episodes), null, null, descript,
null, null, title, "$mainUrl/$slug", this.name, TvType.Anime, img, year, ArrayList(episodes), null, null, descript,
)
}
}

View File

@ -83,7 +83,7 @@ object DownloadButtonSetup {
info.path.toString(),
click.data.id,
headerName ?: "null",
click.data.episode,
if (click.data.episode <= 0) null else click.data.episode,
click.data.season
),
act.getViewPos(click.data.id)?.position ?: 0
@ -194,8 +194,8 @@ object DownloadButtonSetup {
Pair(DOWNLOAD_ACTION_DELETE_FILE, R.string.popup_delete_file),
)
// DON'T RESUME A DOWNLOADED FILE
if (lastState != VideoDownloadManager.DownloadType.IsDone && ((currentBytes * 100 / totalBytes) < 98)) {
// DON'T RESUME A DOWNLOADED FILE lastState != VideoDownloadManager.DownloadType.IsDone &&
if ((currentBytes * 100 / totalBytes) < 98) {
list.add(
if (lastState == VideoDownloadManager.DownloadType.IsDownloading)
Pair(DOWNLOAD_ACTION_PAUSE_DOWNLOAD, R.string.popup_pause_download)

View File

@ -99,94 +99,10 @@ class DownloadChildAdapter(
card.data,
clickCallback
)
/*
val totalMbString = "%.1f".format(card.totalBytes / 1000000f)
var lastState: VideoDownloadManager.DownloadType? = null
var currentBytes: Long = card.currentBytes
fun changeDownloadImage(state: VideoDownloadManager.DownloadType) {
runOnMainThread {
val img = when (state) {
VideoDownloadManager.DownloadType.IsPaused -> R.drawable.ic_baseline_play_arrow_24
VideoDownloadManager.DownloadType.IsDownloading -> R.drawable.netflix_pause
else -> R.drawable.ic_baseline_delete_outline_24
}
downloadImage?.setImageResource(img)
}
}
fun fixDownloadedBytes(setCurrentBytes: Long, animate : Boolean) {
currentBytes = setCurrentBytes
runOnMainThread {
val currentMbString = "%.1f".format(currentBytes / 1000000f)
extraInfo?.text =
"${currentMbString}MB / ${totalMbString}MB"
progressBarDownload?.let { bar ->
bar.max = (card.totalBytes / 1000).toInt()
if(animate) {
val animation: ObjectAnimator = ObjectAnimator.ofInt(
bar,
"progress",
bar.progress,
(currentBytes / 1000).toInt()
)
animation.duration = 500
animation.setAutoCancel(true)
animation.interpolator = DecelerateInterpolator()
animation.start()
} else {
bar.progress = (currentBytes / 1000).toInt()
}
}
}
}
fixDownloadedBytes(card.currentBytes, false)
changeDownloadImage(getDownloadState(card.data.id))
VideoDownloadManager.downloadProgressEvent += { downloadData ->
if (card.data.id == downloadData.first) {
fixDownloadedBytes(downloadData.second, true)
}
}
VideoDownloadManager.downloadStatusEvent += { downloadData ->
if (card.data.id == downloadData.first) {
if (lastState != downloadData.second) { // TO PREVENT WASTING UI TIME
lastState = downloadData.second
changeDownloadImage(downloadData.second)
}
}
}
holder.setOnClickListener {
clickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, d))
}
downloadImage.setOnClickListener {
val list = arrayListOf(
Pair(DOWNLOAD_ACTION_DELETE_FILE, R.string.popup_delete_file),
)
// DON'T RESUME A DOWNLOADED FILE
if (lastState != VideoDownloadManager.DownloadType.IsDone && (currentBytes * 100 / card.totalBytes < 98)) {
list.add(
if (lastState == VideoDownloadManager.DownloadType.IsDownloading)
Pair(DOWNLOAD_ACTION_PAUSE_DOWNLOAD, R.string.popup_pause_download)
else
Pair(DOWNLOAD_ACTION_RESUME_DOWNLOAD, R.string.popup_resume_download)
)
}
it.popupMenuNoIcons(
list
) {
clickCallback.invoke(DownloadClickEvent(itemId, d))
}
}*/
}
}
}

View File

@ -1,26 +1,18 @@
package com.lagradost.cloudstream3.ui.download
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContentProviderCompat.requireContext
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
import com.lagradost.cloudstream3.ui.player.PlayerFragment
import com.lagradost.cloudstream3.ui.player.UriData
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager
import kotlinx.android.synthetic.main.fragment_child_downloads.*
@ -88,12 +80,17 @@ class DownloadChildFragment : Fragment() {
ArrayList(),
) { click ->
handleDownloadClick(activity, name, click)
when (click.action) {
DOWNLOAD_ACTION_DELETE_FILE -> {
updateList(folder)
}
}
VideoDownloadManager.downloadDeleteEvent += { id ->
val list = (download_child_list?.adapter as DownloadChildAdapter?)?.cardList
if (list != null) {
if (list.any { it.data.id == id }) {
updateList(folder)
}
}
}
download_child_list.adapter = adapter
download_child_list.layoutManager = GridLayoutManager(context, 1)

View File

@ -16,15 +16,16 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.isMovieType
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
import com.lagradost.cloudstream3.ui.result.ResultFragment
import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE
import com.lagradost.cloudstream3.utils.DataStore.getFolderName
import com.lagradost.cloudstream3.utils.VideoDownloadManager
import kotlinx.android.synthetic.main.fragment_child_downloads.*
import kotlinx.android.synthetic.main.fragment_downloads.*
import kotlinx.android.synthetic.main.fragment_result.*
class DownloadFragment : Fragment() {
private lateinit var downloadsViewModel: DownloadViewModel
private fun getBytesAsText(bytes: Long): String {
@ -40,6 +41,11 @@ class DownloadFragment : Fragment() {
this.layoutParams = param
}
fun setList(list : List<VisualDownloadHeaderCached>) {
(download_list?.adapter as DownloadHeaderAdapter? ?: return).cardList = list
(download_list?.adapter as DownloadHeaderAdapter? ?: return).notifyDataSetChanged()
}
@SuppressLint("SetTextI18n")
override fun onCreateView(
inflater: LayoutInflater,
@ -52,8 +58,7 @@ class DownloadFragment : Fragment() {
text_no_downloads.text = it
}
observe(downloadsViewModel.headerCards) {
(download_list?.adapter as DownloadHeaderAdapter? ?: return@observe).cardList = it
(download_list?.adapter as DownloadHeaderAdapter? ?: return@observe).notifyDataSetChanged()
setList(it)
}
observe(downloadsViewModel.availableBytes) {
download_free_txt?.text = "Free • ${getBytesAsText(it)}GB"
@ -76,19 +81,36 @@ class DownloadFragment : Fragment() {
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
DownloadHeaderAdapter(
ArrayList(),
) { click ->
if(click.data.type.isMovieType()) {
//TODO MOVIE
}
else {
val folder = getFolderName(DOWNLOAD_EPISODE_CACHE, click.data.id.toString())
val navController = activity?.findNavController(R.id.nav_host_fragment)
navController?.navigate(R.id.navigation_download_child, Bundle().apply {
{ click ->
if (click.data.type.isMovieType()) {
//wont be called
} else {
val folder = getFolderName(DOWNLOAD_EPISODE_CACHE, click.data.id.toString())
val navController = activity?.findNavController(R.id.nav_host_fragment)
navController?.navigate(R.id.navigation_download_child, Bundle().apply {
putString("folder", folder)
putString("name", click.data.name)
})
})
}
},
{ downloadClickEvent ->
handleDownloadClick(activity, downloadClickEvent.data.name, downloadClickEvent)
if(downloadClickEvent.action == DOWNLOAD_ACTION_DELETE_FILE) {
downloadsViewModel.updateList(requireContext())
}
}
)
VideoDownloadManager.downloadDeleteEvent += { id ->
val list = (download_list?.adapter as DownloadHeaderAdapter?)?.cardList
if (list != null) {
if (list.any { it.data.id == id }) {
setList(ArrayList())
downloadsViewModel.updateList(requireContext())
}
}
}
download_list.adapter = adapter
download_list.layoutManager = GridLayoutManager(context, 1)
downloadsViewModel.updateList(requireContext())

View File

@ -7,11 +7,13 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.cardview.widget.CardView
import androidx.core.widget.ContentLoadingProgressBar
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.isMovieType
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.setUpButton
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import kotlinx.android.synthetic.main.download_header_episode.view.*
@ -19,7 +21,9 @@ data class VisualDownloadHeaderCached(
val currentOngoingDownloads: Int,
val totalDownloads: Int,
val totalBytes: Long,
val currentBytes: Long,
val data: VideoDownloadHelper.DownloadHeaderCached,
val child: VideoDownloadHelper.DownloadEpisodeCached?,
)
data class DownloadHeaderClickEvent(val action: Int, val data: VideoDownloadHelper.DownloadHeaderCached)
@ -27,13 +31,15 @@ data class DownloadHeaderClickEvent(val action: Int, val data: VideoDownloadHelp
class DownloadHeaderAdapter(
var cardList: List<VisualDownloadHeaderCached>,
private val clickCallback: (DownloadHeaderClickEvent) -> Unit,
private val movieClickCallback: (DownloadClickEvent) -> Unit,
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return DownloadHeaderViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.download_header_episode, parent, false),
clickCallback
clickCallback,
movieClickCallback
)
}
@ -53,12 +59,17 @@ class DownloadHeaderAdapter(
constructor(
itemView: View,
private val clickCallback: (DownloadHeaderClickEvent) -> Unit,
private val movieClickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.ViewHolder(itemView) {
private val poster: ImageView = itemView.download_header_poster
private val title: TextView = itemView.download_header_title
private val extraInfo: TextView = itemView.download_header_info
private val holder: CardView = itemView.episode_holder
private val downloadBar: ContentLoadingProgressBar = itemView.download_header_progress_downloaded
private val downloadImage: ImageView = itemView.download_header_episode_download
private val normalImage: ImageView = itemView.download_header_goto_child
@SuppressLint("SetTextI18n")
fun bind(card: VisualDownloadHeaderCached) {
val d = card.data
@ -77,13 +88,36 @@ class DownloadHeaderAdapter(
title.text = d.name
val mbString = "%.1f".format(card.totalBytes / 1000000f)
extraInfo.text =
if (d.type.isMovieType())
"${mbString}MB" else
//val isMovie = d.type.isMovieType()
if (card.child != null) {
downloadBar.visibility = View.VISIBLE
downloadImage.visibility = View.VISIBLE
normalImage.visibility = View.GONE
setUpButton(
card.currentBytes,
card.totalBytes,
downloadBar,
downloadImage,
extraInfo,
card.child,
movieClickCallback
)
holder.setOnClickListener {
movieClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, card.child))
}
} else {
downloadBar.visibility = View.GONE
downloadImage.visibility = View.GONE
normalImage.visibility = View.VISIBLE
extraInfo.text =
"${card.totalDownloads} Episode${if (card.totalDownloads == 1) "" else "s"} | ${mbString}MB"
holder.setOnClickListener {
clickCallback.invoke(DownloadHeaderClickEvent(0, d))
holder.setOnClickListener {
clickCallback.invoke(DownloadHeaderClickEvent(0, d))
}
}
}
}

View File

@ -7,8 +7,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.isMovieType
import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
import com.lagradost.cloudstream3.utils.DataStore.getFolderName
import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
@ -38,11 +41,14 @@ class DownloadViewModel : ViewModel() {
fun updateList(context: Context) = viewModelScope.launch {
val children = withContext(Dispatchers.IO) {
val headers = context.getKeys(DOWNLOAD_EPISODE_CACHE)
headers.mapNotNull { context.getKey<VideoDownloadHelper.DownloadEpisodeCached>(it) }.distinctBy { it.id } // Remove duplicates
headers.mapNotNull { context.getKey<VideoDownloadHelper.DownloadEpisodeCached>(it) }
.distinctBy { it.id } // Remove duplicates
}
// parentId : bytes
val bytesUsedByChild = HashMap<Int, Long>()
val totalBytesUsedByChild = HashMap<Int, Long>()
// parentId : bytes
val currentBytesUsedByChild = HashMap<Int, Long>()
// parentId : downloadsCount
val totalDownloads = HashMap<Int, Int>()
@ -51,19 +57,13 @@ class DownloadViewModel : ViewModel() {
for (c in children) {
val childFile = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(context, c.id) ?: continue
if(childFile.fileLength <= 1) continue
if (childFile.fileLength <= 1) continue
val len = childFile.totalBytes
if (bytesUsedByChild.containsKey(c.parentId)) {
bytesUsedByChild[c.parentId] = bytesUsedByChild[c.parentId]?.plus(len) ?: len
} else {
bytesUsedByChild[c.parentId] = len
}
val flen = childFile.fileLength
if (totalDownloads.containsKey(c.parentId)) {
totalDownloads[c.parentId] = totalDownloads[c.parentId]?.plus(1) ?: 1
} else {
totalDownloads[c.parentId] = 1
}
totalBytesUsedByChild[c.parentId] = totalBytesUsedByChild[c.parentId]?.plus(len) ?: len
currentBytesUsedByChild[c.parentId] = currentBytesUsedByChild[c.parentId]?.plus(flen) ?: flen
totalDownloads[c.parentId] = totalDownloads[c.parentId]?.plus(1) ?: 1
}
}
@ -75,9 +75,21 @@ class DownloadViewModel : ViewModel() {
val visual = withContext(Dispatchers.IO) {
cached.mapNotNull { // TODO FIX
val downloads = totalDownloads[it.id] ?: 0
val bytes = bytesUsedByChild[it.id] ?: 0
if(bytes <= 0 || downloads <= 0) return@mapNotNull null
VisualDownloadHeaderCached(0, downloads, bytes, it)
val bytes = totalBytesUsedByChild[it.id] ?: 0
val currentBytes = currentBytesUsedByChild[it.id] ?: 0
if (bytes <= 0 || downloads <= 0) return@mapNotNull null
val movieEpisode = if (!it.type.isMovieType()) null else context.getKey<VideoDownloadHelper.DownloadEpisodeCached>(
DOWNLOAD_EPISODE_CACHE,
getFolderName(it.id.toString(), it.id.toString())
)
VisualDownloadHeaderCached(
0,
downloads,
bytes,
currentBytes,
it,
movieEpisode
)
}
}

View File

@ -380,7 +380,7 @@ class ResultFragment : Fragment() {
val meta = VideoDownloadManager.DownloadEpisodeMetadata(
episodeClick.data.id,
titleName,
apiName ?: return,
apiName,
episodeClick.data.poster ?: currentPoster,
episodeClick.data.name,
if (isMovie) null else episodeClick.data.season,

View File

@ -171,7 +171,7 @@ class ResultViewModel : ViewModel() {
null,
d.dataUrl,
d.apiName,
(mainId + 1),
(mainId), // HAS SAME ID
0,
null,
null,

View File

@ -143,6 +143,7 @@ object VideoDownloadManager {
val downloadStatus = HashMap<Int, DownloadType>()
val downloadStatusEvent = Event<Pair<Int, DownloadType>>()
val downloadDeleteEvent = Event<Int>()
val downloadEvent = Event<Pair<Int, DownloadActionType>>()
val downloadProgressEvent = Event<Triple<Int, Long, Long>>()
val downloadQueue = LinkedList<DownloadResumePackage>()
@ -427,6 +428,7 @@ object VideoDownloadManager {
} else {
if (!File(normalPath).delete()) return ERROR_DELETING_FILE
}
downloadDeleteEvent.invoke(ep.id)
return SUCCESS_STOPPED
}
@ -716,10 +718,11 @@ object VideoDownloadManager {
}
private fun deleteFile(context: Context, id: Int): Boolean {
val info = context.getKey<DownloadedFileInfo>(KEY_DOWNLOAD_INFO, id.toString()) ?: return false
downloadEvent.invoke(Pair(id, DownloadActionType.Stop))
downloadProgressEvent.invoke(Triple(id, 0, 0))
downloadStatusEvent.invoke(Pair(id, DownloadType.IsStopped))
val info = context.getKey<DownloadedFileInfo>(KEY_DOWNLOAD_INFO, id.toString()) ?: return false
downloadDeleteEvent.invoke(id)
if (isScopedStorage()) {
val cr = context.contentResolver ?: return false

View File

@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="850.39dp"
android:height="850.39dp"
android:width="24dp"
android:height="24dp"
android:viewportWidth="850.39"
android:viewportHeight="850.39">
<path

View File

@ -83,7 +83,8 @@
android:visibility="visible"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="2dp"
android:layout_width="30dp"
android:id="@+id/download_child_episode_download"

View File

@ -34,7 +34,7 @@
android:orientation="vertical"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_marginEnd="50dp"
android:layout_marginEnd="70dp"
android:layout_height="wrap_content">
<TextView
android:id="@+id/download_header_title"
@ -52,14 +52,49 @@
android:layout_height="wrap_content">
</TextView>
</LinearLayout>
<!--
android:background="?selectableItemBackgroundBorderless"-->
<ImageView
android:layout_marginStart="-40dp"
android:layout_marginStart="-50dp"
android:layout_height="match_parent"
android:layout_width="30dp"
android:id="@+id/download_header_goto_child"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_baseline_keyboard_arrow_right_24"
android:contentDescription="@string/download_descript"/>
<FrameLayout
android:layout_marginStart="-50dp"
android:layout_gravity="end"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<androidx.core.widget.ContentLoadingProgressBar
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_width="40dp"
android:layout_height="40dp"
android:id="@+id/download_header_progress_downloaded"
android:indeterminate="false"
android:progressDrawable="@drawable/circular_progress_bar"
android:background="@drawable/circle_shape"
style="?android:attr/progressBarStyleHorizontal"
android:max="100"
android:layout_margin="5dp"
android:layout_gravity="center_vertical"
android:progress="0"
android:visibility="visible"
/>
<ImageView
android:visibility="visible"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_gravity="center_vertical"
android:layout_height="wrap_content"
android:padding="2dp"
android:layout_width="30dp"
android:id="@+id/download_header_episode_download"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_baseline_play_arrow_24"
android:contentDescription="@string/download_descript"/>
</FrameLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@ -86,7 +86,8 @@
android:visibility="visible"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="2dp"
android:layout_width="30dp"
android:id="@+id/result_episode_download"

View File

@ -101,7 +101,8 @@
android:visibility="visible"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="2dp"
android:layout_width="30dp"
android:id="@+id/result_episode_download"
@ -109,7 +110,6 @@
android:src="@drawable/ic_baseline_play_arrow_24"
android:contentDescription="@string/download_descript"/>
</FrameLayout>
</LinearLayout>
<TextView
android:paddingTop="10dp"