diff --git a/app/build.gradle b/app/build.gradle index 8ece81c4..5bf72d99 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -201,7 +201,7 @@ dependencies { implementation 'me.xdrop:fuzzywuzzy:1.4.0' // aria2c downloader - implementation 'com.github.LagradOst:Aria2cButton:master-SNAPSHOT' + implementation 'com.github.LagradOst:Aria2cButton:v0.0.3' } task androidSourcesJar(type: Jar) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/DownloadButton.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/DownloadButton.kt new file mode 100644 index 00000000..b026eb9d --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/DownloadButton.kt @@ -0,0 +1,42 @@ +package com.lagradost.cloudstream3.ui + +import android.content.Context +import android.util.AttributeSet +import android.widget.TextView +import androidx.core.view.isVisible +import com.lagradost.cloudstream3.R +import com.lagradost.fetchbutton.aria2c.DownloadStatusTell +import com.lagradost.fetchbutton.aria2c.Metadata +import com.lagradost.fetchbutton.ui.PieFetchButton + +class DownloadButton(context: Context, attributeSet: AttributeSet) : + PieFetchButton(context, attributeSet) { + + var progressText: TextView? = null + var mainText: TextView? = null + override fun onAttachedToWindow() { + super.onAttachedToWindow() + progressText = findViewById(R.id.result_movie_download_text_precentage) + mainText = findViewById(R.id.result_movie_download_text) + } + + override fun setStatus(status: DownloadStatusTell?) { + super.setStatus(status) + val txt = when (status) { + DownloadStatusTell.Paused -> R.string.download_paused + DownloadStatusTell.Active -> R.string.downloading + DownloadStatusTell.Complete -> R.string.downloaded + else -> R.string.download + } + mainText?.setText(txt) + } + + override fun updateViewOnDownload(metadata: Metadata) { + super.updateViewOnDownload(metadata) + + val isVis = metadata.progressPercentage > 0 + progressText?.isVisible = isVis + if (isVis) + progressText?.text = "${metadata.progressPercentage}%" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt index a541171b..98362b13 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt @@ -9,6 +9,7 @@ import androidx.cardview.widget.CardView import androidx.core.widget.ContentLoadingProgressBar import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.utils.AppUtils.getNameFull import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos @@ -30,6 +31,7 @@ data class VisualDownloadChildCached( ) data class DownloadClickEvent(val action: Int, val data: EasyDownloadButton.IMinimumData) +data class DownloadEpisodeClickEvent(val action: Int, val data: ResultEpisode) class DownloadChildAdapter( var cardList: List, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/DownloadHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/DownloadHelper.kt new file mode 100644 index 00000000..067874b1 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/DownloadHelper.kt @@ -0,0 +1,130 @@ +package com.lagradost.cloudstream3.ui.result + +import android.net.Uri +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD +import com.lagradost.cloudstream3.ui.download.DownloadEpisodeClickEvent +import com.lagradost.cloudstream3.ui.player.DownloadFileGenerator +import com.lagradost.cloudstream3.utils.ExtractorUri +import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons +import com.lagradost.fetchbutton.aria2c.DownloadStatusTell +import com.lagradost.fetchbutton.ui.PieFetchButton + +object DownloadHelper { + fun PieFetchButton.play(card: ResultEpisode) { + val files = this.getVideos() + DownloadFileGenerator( + files.map { path -> + ExtractorUri( + uri = Uri.parse(path), + + id = card.id, + parentId = card.parentId, + name = context.getString(R.string.downloaded_file), //click.data.name ?: keyInfo.displayName + season = card.season, + episode = card.episode, + headerName = card.headerName, + tvType = card.tvType, + + //basePath = keyInfo.basePath, + //displayName = keyInfo.displayName, + //relativePath = keyInfo.relativePath, + ) + } + ) + } + + fun PieFetchButton.setUp( + card: ResultEpisode, + downloadClickCallback: (DownloadEpisodeClickEvent) -> Unit + ) { + setPersistentId(card.id.toLong()) + + setOnClickListener { view -> + if (view !is PieFetchButton) return@setOnClickListener + when (view.currentStatus) { + null, DownloadStatusTell.Removed -> { + view.setStatus(DownloadStatusTell.Waiting) + downloadClickCallback.invoke( + DownloadEpisodeClickEvent( + DOWNLOAD_ACTION_DOWNLOAD, + card + ) + ) + } + DownloadStatusTell.Paused -> { + view.popupMenuNoIcons( + listOf( + 1 to R.string.resume, + 2 to R.string.play_episode, + 3 to R.string.delete + ) + ) { + when (itemId) { + 1 -> if (!view.resumeDownload()) { + downloadClickCallback.invoke( + DownloadEpisodeClickEvent( + DOWNLOAD_ACTION_DOWNLOAD, + card + ) + ) + } + 2 -> { + play(card) + } + 3 -> { + view.deleteAllFiles() + } + } + } + } + DownloadStatusTell.Complete -> { + view.popupMenuNoIcons( + listOf( + 2 to R.string.play_episode, + 3 to R.string.delete + ) + ) { + when (itemId) { + 2 -> { + play(card) + } + 3 -> { + view.deleteAllFiles() + } + } + } + } + DownloadStatusTell.Active -> { + view.popupMenuNoIcons( + listOf( + 4 to R.string.pause, + 2 to R.string.play_episode, + 3 to R.string.delete + ) + ) { + when (itemId) { + 4 -> { + view.pauseDownload() + } + 2 -> { + play(card) + } + 3 -> { + view.deleteAllFiles() + } + } + } + } + DownloadStatusTell.Error -> { + view.redownload() + } + DownloadStatusTell.Waiting -> { + + } + else -> {} + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt index 8dd17d86..e5e886b0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt @@ -14,12 +14,11 @@ import androidx.recyclerview.widget.RecyclerView import com.google.android.material.button.MaterialButton import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.ui.download.DownloadButtonViewHolder -import com.lagradost.cloudstream3.ui.download.DownloadClickEvent +import com.lagradost.cloudstream3.ui.download.DownloadEpisodeClickEvent +import com.lagradost.cloudstream3.ui.result.DownloadHelper.setUp import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.UIHelper.setImage -import com.lagradost.fetchbutton.aria2c.UriRequest -import com.lagradost.fetchbutton.aria2c.newUriRequest import kotlinx.android.synthetic.main.result_episode.view.* import kotlinx.android.synthetic.main.result_episode.view.episode_text import kotlinx.android.synthetic.main.result_episode_large.view.episode_filler @@ -53,7 +52,7 @@ data class EpisodeClickEvent(val action: Int, val data: ResultEpisode) class EpisodeAdapter( private val hasDownloadSupport: Boolean, private val clickCallback: (EpisodeClickEvent) -> Unit, - private val downloadClickCallback: suspend ResultEpisode.() -> List, + private val downloadClickCallback: (DownloadEpisodeClickEvent) -> Unit, ) : RecyclerView.Adapter() { private var cardList: MutableList = mutableListOf() @@ -126,7 +125,7 @@ class EpisodeAdapter( itemView: View, private val hasDownloadSupport: Boolean, private val clickCallback: (EpisodeClickEvent) -> Unit, - private val downloadClickCallback : suspend ResultEpisode.() -> List, + private val downloadClickCallback: (DownloadEpisodeClickEvent) -> Unit, ) : RecyclerView.ViewHolder(itemView) { //override var downloadButton = EasyDownloadButton() @@ -151,11 +150,8 @@ class EpisodeAdapter( val downloadButton = parentView.result_episode_download - downloadButton.setPersistentId(card.id.toLong()) downloadButton.isVisible = hasDownloadSupport - downloadButton.setDefaultClickListener { - downloadClickCallback.invoke(card) - } + downloadButton.setUp(card, downloadClickCallback) val name = if (card.name == null) "${episodeText.context.getString(R.string.episode)} ${card.episode}" else "${card.episode}. ${card.name}" diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index f3ff7562..e0f76509 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -34,12 +34,8 @@ import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.syncproviders.providers.Kitsu import com.lagradost.cloudstream3.ui.WatchType -import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD -import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick -import com.lagradost.cloudstream3.ui.download.DownloadViewModel -import com.lagradost.cloudstream3.ui.download.EasyDownloadButton import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment -import com.lagradost.cloudstream3.ui.result.ResultViewModel2.Companion.getDownloadRequest +import com.lagradost.cloudstream3.ui.result.DownloadHelper.setUp import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.* @@ -47,21 +43,19 @@ import com.lagradost.cloudstream3.utils.AppUtils.getNameFull import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.openBrowser -import com.lagradost.cloudstream3.utils.Coroutines.ioWorkSafe -import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard +import kotlinx.android.synthetic.main.download_button.* import kotlinx.android.synthetic.main.fragment_result.* import kotlinx.android.synthetic.main.fragment_result.result_cast_items import kotlinx.android.synthetic.main.fragment_result.result_cast_text import kotlinx.android.synthetic.main.fragment_result.result_coming_soon import kotlinx.android.synthetic.main.fragment_result.result_data_holder import kotlinx.android.synthetic.main.fragment_result.result_description -import kotlinx.android.synthetic.main.fragment_result.result_download_movie import kotlinx.android.synthetic.main.fragment_result.result_episode_loading import kotlinx.android.synthetic.main.fragment_result.result_episodes import kotlinx.android.synthetic.main.fragment_result.result_error_text @@ -74,11 +68,6 @@ import kotlinx.android.synthetic.main.fragment_result.result_meta_rating import kotlinx.android.synthetic.main.fragment_result.result_meta_site import kotlinx.android.synthetic.main.fragment_result.result_meta_type import kotlinx.android.synthetic.main.fragment_result.result_meta_year -import kotlinx.android.synthetic.main.fragment_result.result_movie_download_icon -import kotlinx.android.synthetic.main.fragment_result.result_movie_download_text -import kotlinx.android.synthetic.main.fragment_result.result_movie_download_text_precentage -import kotlinx.android.synthetic.main.fragment_result.result_movie_progress_downloaded -import kotlinx.android.synthetic.main.fragment_result.result_movie_progress_downloaded_holder import kotlinx.android.synthetic.main.fragment_result.result_next_airing import kotlinx.android.synthetic.main.fragment_result.result_next_airing_time import kotlinx.android.synthetic.main.fragment_result.result_no_episodes @@ -255,10 +244,8 @@ open class ResultFragment : ResultTrailerPlayer() { return inflater.inflate(resultLayout, container, false) } - private var downloadButton: EasyDownloadButton? = null override fun onDestroyView() { updateUIListener = null - downloadButton?.dispose() super.onDestroyView() } @@ -345,7 +332,12 @@ open class ResultFragment : ResultTrailerPlayer() { return@setOnLongClickListener true } - main { + + result_download_movie?.setUp(ep) { + viewModel.download(it.data) + } + result_download_movie?.isVisible = true + /*main { val file = ioWorkSafe { context?.let { @@ -390,11 +382,12 @@ open class ResultFragment : ResultTrailerPlayer() { } } result_movie_progress_downloaded_holder?.isVisible = true - } + }*/ } } else -> { - result_movie_progress_downloaded_holder?.isVisible = false + //result_movie_progress_downloaded_holder?.isVisible = false + result_download_movie?.isVisible = false result_play_movie?.isVisible = false } } @@ -459,7 +452,14 @@ open class ResultFragment : ResultTrailerPlayer() { val storedData = getStoredData(activity ?: context ?: return) ?: return //viewModel.clear() - viewModel.load(activity, storedData.url ?: return, storedData.apiName, storedData.showFillers, storedData.dubStatus, storedData.start) + viewModel.load( + activity, + storedData.url ?: return, + storedData.apiName, + storedData.showFillers, + storedData.dubStatus, + storedData.start + ) } } @@ -508,8 +508,8 @@ open class ResultFragment : ResultTrailerPlayer() { { episodeClick -> viewModel.handleAction(activity, episodeClick) }, - { - viewModel.getRequest(this)?.links ?: emptyList() + { clickEvent -> + viewModel.download(clickEvent.data) //handleDownloadClick(activity, downloadClickEvent) } ) @@ -918,7 +918,14 @@ open class ResultFragment : ResultTrailerPlayer() { if (storedData?.url != null) { result_reload_connectionerror.setOnClickListener { - viewModel.load(activity, storedData.url, storedData.apiName, storedData.showFillers, storedData.dubStatus, storedData.start) + viewModel.load( + activity, + storedData.url, + storedData.apiName, + storedData.showFillers, + storedData.dubStatus, + storedData.start + ) } result_reload_connection_open_in_browser?.setOnClickListener { @@ -954,7 +961,14 @@ open class ResultFragment : ResultTrailerPlayer() { if (restart || !viewModel.hasLoaded()) { //viewModel.clear() - viewModel.load(activity, storedData.url, storedData.apiName, storedData.showFillers, storedData.dubStatus, storedData.start) + viewModel.load( + activity, + storedData.url, + storedData.apiName, + storedData.showFillers, + storedData.dubStatus, + storedData.start + ) } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index f620fc65..51caaf79 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -52,9 +52,11 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.setDub import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultEpisode import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason import com.lagradost.cloudstream3.utils.UIHelper.checkWrite +import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath +import com.lagradost.fetchbutton.NotificationMetaData import com.lagradost.fetchbutton.aria2c.Aria2Starter import com.lagradost.fetchbutton.aria2c.UriRequest import com.lagradost.fetchbutton.aria2c.newUriRequest @@ -648,6 +650,29 @@ class ResultViewModel2 : ViewModel() { System.currentTimeMillis(), ) ) + + val notification = AcraApplication.context?.let { ctx -> + val rowTwoExtra = if (episode.name != null) " - ${episode.name}\n" else "" + val rowTwo = if (episode.season != null && episode.episode > 0) { + "${ctx.getString(R.string.season_short)}${episode.season}:${ctx.getString(R.string.episode_short)}${episode.episode}" + rowTwoExtra + } else if (episode.episode > 0) { + "${ctx.getString(R.string.episode)} ${episode.episode}" + rowTwoExtra + } else { + (episode.name ?: "") + "" + } + NotificationMetaData( + episode.id, + iconColor = ctx.colorFromAttribute(R.attr.colorPrimary), + posterUrl = currentPoster, + contentTitle = currentHeaderName, + secondRow = rowTwo, + subText = null, + linkName = currentHeaderName, + rowTwoExtra = null + ) + } + + val linkRequests = links.filter { link -> !link.isM3u8 }.map { link -> newUriRequest( episode.id.toLong(), link.url, @@ -656,7 +681,8 @@ class ResultViewModel2 : ViewModel() { AcraApplication.context ?: return null, meta ), ".mp4" - ), folder, link.headers, USER_AGENT + ), folder, link.headers, USER_AGENT, + notificationMetaData = notification ) } @@ -668,13 +694,14 @@ class ResultViewModel2 : ViewModel() { true ) ) - } - .map { ExtractorSubtitleLink(it.name, it.url, "") } + }.distinctBy { it.url } + //.map { ExtractorSubtitleLink(it.name, it.url, "") } .map { link -> val fileName = VideoDownloadManager.getFileName( AcraApplication.context ?: return null, meta - ) + ) + ".vtt" + newUriRequest(0, link.url, fileName, folder, link.headers, USER_AGENT) //downloadSubtitle(context, link, fileName, folder) } @@ -1066,6 +1093,19 @@ class ResultViewModel2 : ViewModel() { handleEpisodeClickEvent(activity, click) } + private fun downloadFromRequest(req: DownloadRequest) { + Aria2Starter.download(req.links) + for (sub in req.subs.take(5)) { + Aria2Starter.download(sub) + } + } + + fun download(card: ResultEpisode) = ioSafe { + getRequest(card)?.let { req -> + downloadFromRequest(req) + } + } + suspend fun getRequest(card: ResultEpisode): DownloadRequest? { val response = currentResponse ?: return null return downloadEpisode( @@ -1202,7 +1242,7 @@ class ResultViewModel2 : ViewModel() { listOf(result.links[index]), result.subs, ) ?: return@ioSafe - Aria2Starter.client?.downloadFailQueue(req.links) { _, _ -> } + downloadFromRequest(req) } showToast( activity, diff --git a/app/src/main/res/layout/download_button.xml b/app/src/main/res/layout/download_button.xml new file mode 100644 index 00000000..9014102e --- /dev/null +++ b/app/src/main/res/layout/download_button.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/download_button_layout.xml b/app/src/main/res/layout/download_button_layout.xml new file mode 100644 index 00000000..7f9caa1c --- /dev/null +++ b/app/src/main/res/layout/download_button_layout.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml index 48aeb72e..eb6c7f33 100644 --- a/app/src/main/res/layout/fragment_result.xml +++ b/app/src/main/res/layout/fragment_result.xml @@ -570,89 +570,7 @@ android:focusable="true" android:layout_width="match_parent" />--> - - - - - - - - - - - - - - - - + @@ -65,6 +66,9 @@ #FFF @style/CustomPreferenceThemeOverlay + ?attr/white + ?attr/white + ?attr/white