2021-05-16 18:28:00 +00:00
|
|
|
package com.lagradost.cloudstream3.ui.result
|
|
|
|
|
2021-05-18 13:43:32 +00:00
|
|
|
import android.annotation.SuppressLint
|
2021-07-17 15:56:26 +00:00
|
|
|
import android.content.*
|
|
|
|
import android.content.Context.CLIPBOARD_SERVICE
|
|
|
|
import android.content.Intent.*
|
2021-06-10 15:15:14 +00:00
|
|
|
import android.net.Uri
|
2021-05-16 18:28:00 +00:00
|
|
|
import android.os.Bundle
|
2021-06-26 17:03:22 +00:00
|
|
|
import android.text.SpannableStringBuilder
|
2021-05-16 18:28:00 +00:00
|
|
|
import android.view.LayoutInflater
|
|
|
|
import android.view.View
|
2021-06-06 18:06:01 +00:00
|
|
|
import android.view.View.GONE
|
|
|
|
import android.view.View.VISIBLE
|
2021-05-16 18:28:00 +00:00
|
|
|
import android.view.ViewGroup
|
2021-06-10 15:15:14 +00:00
|
|
|
import android.widget.Toast
|
2021-05-18 13:43:32 +00:00
|
|
|
import androidx.appcompat.app.AlertDialog
|
2021-05-22 22:25:56 +00:00
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
2021-06-16 16:54:07 +00:00
|
|
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
2021-06-26 17:03:22 +00:00
|
|
|
import androidx.core.content.ContextCompat
|
2021-07-17 15:56:26 +00:00
|
|
|
import androidx.core.content.FileProvider
|
2021-06-26 17:03:22 +00:00
|
|
|
import androidx.core.text.color
|
2021-05-18 13:43:32 +00:00
|
|
|
import androidx.core.widget.NestedScrollView
|
2021-05-16 18:28:00 +00:00
|
|
|
import androidx.fragment.app.Fragment
|
|
|
|
import androidx.lifecycle.ViewModelProvider
|
2021-05-18 13:43:32 +00:00
|
|
|
import androidx.recyclerview.widget.GridLayoutManager
|
|
|
|
import androidx.recyclerview.widget.RecyclerView
|
2021-06-06 18:06:01 +00:00
|
|
|
import com.google.android.gms.cast.framework.CastButtonFactory
|
|
|
|
import com.google.android.gms.cast.framework.CastContext
|
|
|
|
import com.google.android.gms.cast.framework.CastState
|
|
|
|
import com.google.android.material.button.MaterialButton
|
2021-06-14 00:00:29 +00:00
|
|
|
import com.lagradost.cloudstream3.*
|
2021-06-16 22:31:41 +00:00
|
|
|
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
2021-07-25 16:08:34 +00:00
|
|
|
import com.lagradost.cloudstream3.APIHolder.getId
|
2021-07-30 01:14:53 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
|
|
|
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
|
|
|
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
|
|
|
import com.lagradost.cloudstream3.utils.UIHelper.getStatusBarHeight
|
|
|
|
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
|
|
|
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
|
|
|
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons
|
|
|
|
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
|
|
|
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
|
2021-05-18 13:43:32 +00:00
|
|
|
import com.lagradost.cloudstream3.mvvm.Resource
|
|
|
|
import com.lagradost.cloudstream3.mvvm.observe
|
2021-06-15 23:25:58 +00:00
|
|
|
import com.lagradost.cloudstream3.ui.WatchType
|
2021-07-24 20:50:57 +00:00
|
|
|
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
|
2021-07-30 01:14:53 +00:00
|
|
|
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
|
2021-07-28 19:14:45 +00:00
|
|
|
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton
|
2021-07-24 20:50:57 +00:00
|
|
|
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
2021-05-22 22:25:56 +00:00
|
|
|
import com.lagradost.cloudstream3.ui.player.PlayerData
|
|
|
|
import com.lagradost.cloudstream3.ui.player.PlayerFragment
|
2021-08-22 17:14:48 +00:00
|
|
|
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
|
|
|
|
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment.Companion.getDownloadSubsLanguageISO639_1
|
2021-07-17 14:14:25 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.*
|
2021-07-30 01:14:53 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.AppUtils.isAppInstalled
|
|
|
|
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
|
|
|
|
import com.lagradost.cloudstream3.utils.AppUtils.isConnectedToChromecast
|
2021-06-14 16:58:43 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.CastHelper.startCast
|
2021-07-17 14:14:25 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
2021-07-18 23:57:04 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.DataStore.getFolderName
|
2021-07-17 14:14:25 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
2021-06-15 16:07:20 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
2021-08-19 20:05:18 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
2021-06-29 23:14:48 +00:00
|
|
|
|
2021-07-04 17:00:04 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
|
2021-05-18 13:43:32 +00:00
|
|
|
import kotlinx.android.synthetic.main.fragment_result.*
|
2021-07-30 21:03:46 +00:00
|
|
|
import kotlinx.coroutines.Dispatchers
|
2021-07-17 14:14:25 +00:00
|
|
|
import kotlinx.coroutines.Job
|
2021-07-30 21:03:46 +00:00
|
|
|
import kotlinx.coroutines.withContext
|
2021-07-17 15:56:26 +00:00
|
|
|
import java.io.File
|
2021-07-28 01:04:32 +00:00
|
|
|
import kotlin.collections.ArrayList
|
|
|
|
import kotlin.collections.HashMap
|
2021-06-14 00:00:29 +00:00
|
|
|
|
2021-06-06 18:06:01 +00:00
|
|
|
const val MAX_SYNO_LENGH = 300
|
2021-05-18 13:43:32 +00:00
|
|
|
|
2021-07-29 15:16:08 +00:00
|
|
|
const val START_ACTION_NORMAL = 0
|
|
|
|
const val START_ACTION_RESUME_LATEST = 1
|
|
|
|
|
2021-05-18 13:43:32 +00:00
|
|
|
data class ResultEpisode(
|
|
|
|
val name: String?,
|
2021-06-10 15:15:14 +00:00
|
|
|
val poster: String?,
|
2021-05-18 13:43:32 +00:00
|
|
|
val episode: Int,
|
2021-05-28 13:38:06 +00:00
|
|
|
val season: Int?,
|
2021-06-14 16:58:43 +00:00
|
|
|
val data: String,
|
2021-05-18 13:43:32 +00:00
|
|
|
val apiName: String,
|
|
|
|
val id: Int,
|
2021-05-22 22:25:56 +00:00
|
|
|
val index: Int,
|
2021-06-15 16:07:20 +00:00
|
|
|
val position: Long, // time in MS
|
2021-06-10 15:15:14 +00:00
|
|
|
val duration: Long, // duration in MS
|
2021-06-26 19:32:50 +00:00
|
|
|
val rating: Int?,
|
|
|
|
val descript: String?,
|
2021-05-18 13:43:32 +00:00
|
|
|
)
|
2021-05-16 18:28:00 +00:00
|
|
|
|
2021-06-15 16:07:20 +00:00
|
|
|
fun ResultEpisode.getRealPosition(): Long {
|
|
|
|
if (duration <= 0) return 0
|
|
|
|
val percentage = position * 100 / duration
|
|
|
|
if (percentage <= 5 || percentage >= 95) return 0
|
|
|
|
return position
|
|
|
|
}
|
|
|
|
|
2021-06-16 17:40:02 +00:00
|
|
|
fun ResultEpisode.getDisplayPosition(): Long {
|
|
|
|
if (duration <= 0) return 0
|
|
|
|
val percentage = position * 100 / duration
|
|
|
|
if (percentage <= 1) return 0
|
|
|
|
if (percentage <= 5) return 5 * duration / 100
|
|
|
|
if (percentage >= 95) return duration
|
|
|
|
return position
|
|
|
|
}
|
|
|
|
|
2021-06-15 16:07:20 +00:00
|
|
|
fun Context.buildResultEpisode(
|
|
|
|
name: String?,
|
|
|
|
poster: String?,
|
|
|
|
episode: Int,
|
|
|
|
season: Int?,
|
|
|
|
data: String,
|
|
|
|
apiName: String,
|
|
|
|
id: Int,
|
|
|
|
index: Int,
|
2021-06-26 19:32:50 +00:00
|
|
|
rating: Int?,
|
|
|
|
descript: String?,
|
2021-06-15 16:07:20 +00:00
|
|
|
): ResultEpisode {
|
|
|
|
val posDur = getViewPos(id)
|
2021-06-26 19:32:50 +00:00
|
|
|
return ResultEpisode(
|
|
|
|
name,
|
2021-06-15 16:07:20 +00:00
|
|
|
poster,
|
|
|
|
episode,
|
|
|
|
season,
|
|
|
|
data,
|
|
|
|
apiName,
|
|
|
|
id,
|
|
|
|
index,
|
|
|
|
posDur?.position ?: 0,
|
2021-06-26 19:32:50 +00:00
|
|
|
posDur?.duration ?: 0,
|
|
|
|
rating,
|
|
|
|
descript,
|
|
|
|
)
|
2021-06-15 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2021-07-29 15:16:08 +00:00
|
|
|
/** 0f-1f */
|
2021-06-10 15:15:14 +00:00
|
|
|
fun ResultEpisode.getWatchProgress(): Float {
|
2021-07-29 15:16:08 +00:00
|
|
|
return (getDisplayPosition() / 1000).toFloat() / (duration / 1000).toFloat()
|
2021-06-10 15:15:14 +00:00
|
|
|
}
|
|
|
|
|
2021-05-16 18:28:00 +00:00
|
|
|
class ResultFragment : Fragment() {
|
2021-05-22 22:25:56 +00:00
|
|
|
companion object {
|
2021-07-30 14:09:57 +00:00
|
|
|
fun newInstance(url: String, apiName: String, startAction: Int = 0) =
|
2021-05-22 22:25:56 +00:00
|
|
|
ResultFragment().apply {
|
|
|
|
arguments = Bundle().apply {
|
|
|
|
putString("url", url)
|
|
|
|
putString("apiName", apiName)
|
2021-07-29 15:16:08 +00:00
|
|
|
putInt("startAction", startAction)
|
2021-05-22 22:25:56 +00:00
|
|
|
}
|
2021-05-18 13:43:32 +00:00
|
|
|
}
|
2021-05-22 22:25:56 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 16:07:20 +00:00
|
|
|
private var currentLoadingCount = 0 // THIS IS USED TO PREVENT LATE EVENTS, AFTER DISMISS WAS CLICKED
|
2021-05-16 18:28:00 +00:00
|
|
|
private lateinit var viewModel: ResultViewModel
|
2021-05-20 20:56:21 +00:00
|
|
|
private var allEpisodes: HashMap<Int, ArrayList<ExtractorLink>> = HashMap()
|
2021-07-17 14:14:25 +00:00
|
|
|
private var allEpisodesSubs: HashMap<Int, ArrayList<SubtitleFile>> = HashMap()
|
2021-06-15 16:07:20 +00:00
|
|
|
private var currentHeaderName: String? = null
|
2021-07-04 00:59:51 +00:00
|
|
|
private var currentType: TvType? = null
|
2021-06-15 16:07:20 +00:00
|
|
|
private var currentEpisodes: List<ResultEpisode>? = null
|
2021-07-29 15:16:08 +00:00
|
|
|
var downloadButton: EasyDownloadButton? = null
|
2021-05-16 18:28:00 +00:00
|
|
|
|
|
|
|
override fun onCreateView(
|
|
|
|
inflater: LayoutInflater,
|
|
|
|
container: ViewGroup?,
|
2021-05-18 13:43:32 +00:00
|
|
|
savedInstanceState: Bundle?,
|
2021-05-16 18:28:00 +00:00
|
|
|
): View? {
|
|
|
|
viewModel =
|
2021-07-30 21:03:46 +00:00
|
|
|
ViewModelProvider(activity ?: this).get(ResultViewModel::class.java)
|
2021-05-16 18:28:00 +00:00
|
|
|
return inflater.inflate(R.layout.fragment_result, container, false)
|
|
|
|
}
|
|
|
|
|
2021-07-28 19:14:45 +00:00
|
|
|
override fun onDestroyView() {
|
|
|
|
(result_episodes?.adapter as EpisodeAdapter?)?.killAdapter()
|
|
|
|
super.onDestroyView()
|
|
|
|
}
|
|
|
|
|
2021-05-22 22:25:56 +00:00
|
|
|
override fun onDestroy() {
|
|
|
|
//requireActivity().viewModelStore.clear() // REMEMBER THE CLEAR
|
2021-07-28 19:14:45 +00:00
|
|
|
downloadButton?.dispose()
|
|
|
|
|
2021-05-22 22:25:56 +00:00
|
|
|
super.onDestroy()
|
2021-06-16 00:15:07 +00:00
|
|
|
activity?.let {
|
|
|
|
it.window?.navigationBarColor =
|
|
|
|
it.colorFromAttribute(R.attr.darkBackground)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onResume() {
|
|
|
|
super.onResume()
|
|
|
|
activity?.let {
|
|
|
|
it.window?.navigationBarColor =
|
|
|
|
it.colorFromAttribute(R.attr.bitDarkerGrayBackground)
|
|
|
|
}
|
2021-05-22 22:25:56 +00:00
|
|
|
}
|
|
|
|
|
2021-06-16 16:54:07 +00:00
|
|
|
/// 0 = LOADING, 1 = ERROR LOADING, 2 = LOADED
|
2021-07-02 18:46:18 +00:00
|
|
|
private fun updateVisStatus(state: Int) {
|
2021-06-16 16:54:07 +00:00
|
|
|
when (state) {
|
|
|
|
0 -> {
|
|
|
|
result_loading.visibility = VISIBLE
|
|
|
|
result_finish_loading.visibility = GONE
|
2021-07-08 18:03:17 +00:00
|
|
|
result_loading_error.visibility = GONE
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
|
|
|
1 -> {
|
|
|
|
result_loading.visibility = GONE
|
|
|
|
result_finish_loading.visibility = GONE
|
2021-07-08 18:03:17 +00:00
|
|
|
result_loading_error.visibility = VISIBLE
|
2021-06-16 16:54:07 +00:00
|
|
|
result_reload_connection_open_in_browser.visibility = if (url == null) GONE else VISIBLE
|
|
|
|
}
|
|
|
|
2 -> {
|
|
|
|
result_loading.visibility = GONE
|
|
|
|
result_finish_loading.visibility = VISIBLE
|
2021-07-08 18:03:17 +00:00
|
|
|
result_loading_error.visibility = GONE
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-15 16:07:20 +00:00
|
|
|
private var currentPoster: String? = null
|
2021-07-17 14:14:25 +00:00
|
|
|
private var currentId: Int? = null
|
2021-06-16 22:31:41 +00:00
|
|
|
private var currentIsMovie: Boolean? = null
|
2021-07-25 20:50:16 +00:00
|
|
|
private var episodeRanges: List<String>? = null
|
2021-06-10 15:15:14 +00:00
|
|
|
|
2021-06-16 16:54:07 +00:00
|
|
|
var url: String? = null
|
|
|
|
|
2021-06-26 22:15:19 +00:00
|
|
|
private fun fromIndexToSeasonText(selection: Int?): String {
|
|
|
|
return when (selection) {
|
|
|
|
null -> "No Season"
|
|
|
|
-2 -> "No Season"
|
|
|
|
else -> "Season $selection"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-29 15:16:08 +00:00
|
|
|
var startAction: Int? = null
|
|
|
|
|
2021-08-11 18:26:19 +00:00
|
|
|
private fun lateFixDownloadButton(show: Boolean) {
|
2021-08-22 17:14:48 +00:00
|
|
|
if (!show || currentType?.isMovieType() == false) {
|
2021-08-11 18:26:19 +00:00
|
|
|
result_movie_parent.visibility = GONE
|
|
|
|
result_episodes_text.visibility = VISIBLE
|
|
|
|
result_episodes.visibility = VISIBLE
|
2021-08-14 17:31:27 +00:00
|
|
|
} else {
|
|
|
|
result_movie_parent.visibility = VISIBLE
|
|
|
|
result_episodes_text.visibility = GONE
|
|
|
|
result_episodes.visibility = GONE
|
2021-08-11 18:26:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-18 13:43:32 +00:00
|
|
|
@SuppressLint("SetTextI18n")
|
2021-05-16 18:28:00 +00:00
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
|
|
super.onViewCreated(view, savedInstanceState)
|
2021-07-28 01:04:32 +00:00
|
|
|
activity?.window?.decorView?.clearFocus()
|
|
|
|
hideKeyboard()
|
|
|
|
|
2021-05-18 13:43:32 +00:00
|
|
|
activity?.fixPaddingStatusbar(result_scroll)
|
|
|
|
activity?.fixPaddingStatusbar(result_barstatus)
|
2021-06-06 18:06:01 +00:00
|
|
|
|
2021-06-16 16:54:07 +00:00
|
|
|
val backParameter = result_back.layoutParams as CoordinatorLayout.LayoutParams
|
2021-07-02 18:46:18 +00:00
|
|
|
backParameter.setMargins(
|
|
|
|
backParameter.leftMargin,
|
2021-06-16 16:54:07 +00:00
|
|
|
backParameter.topMargin + requireContext().getStatusBarHeight(),
|
|
|
|
backParameter.rightMargin,
|
2021-07-02 18:46:18 +00:00
|
|
|
backParameter.bottomMargin
|
|
|
|
)
|
2021-06-16 16:54:07 +00:00
|
|
|
result_back.layoutParams = backParameter
|
|
|
|
|
2021-05-20 15:22:28 +00:00
|
|
|
// activity?.fixPaddingStatusbar(result_toolbar)
|
2021-05-18 13:43:32 +00:00
|
|
|
|
2021-06-16 16:54:07 +00:00
|
|
|
url = arguments?.getString("url")
|
2021-07-23 23:44:54 +00:00
|
|
|
val apiName = arguments?.getString("apiName") ?: return
|
2021-07-29 15:16:08 +00:00
|
|
|
startAction = arguments?.getInt("startAction") ?: START_ACTION_NORMAL
|
2021-07-23 23:44:54 +00:00
|
|
|
|
|
|
|
val api = getApiFromName(apiName)
|
|
|
|
if (media_route_button != null) {
|
|
|
|
val chromecastSupport = api.hasChromecastSupport
|
|
|
|
|
|
|
|
media_route_button?.alpha = if (chromecastSupport) 1f else 0.3f
|
|
|
|
if (!chromecastSupport) {
|
|
|
|
media_route_button.setOnClickListener {
|
|
|
|
Toast.makeText(it.context, "This provider has no chromecast support", Toast.LENGTH_LONG).show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-30 21:03:46 +00:00
|
|
|
activity?.let {
|
|
|
|
if (it.isCastApiAvailable()) {
|
|
|
|
CastButtonFactory.setUpMediaRouteButton(it, media_route_button)
|
|
|
|
val castContext = CastContext.getSharedInstance(it.applicationContext)
|
2021-05-18 13:43:32 +00:00
|
|
|
|
2021-07-30 21:03:46 +00:00
|
|
|
if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE
|
|
|
|
castContext.addCastStateListener { state ->
|
|
|
|
if (media_route_button != null) {
|
|
|
|
if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else {
|
|
|
|
if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE
|
|
|
|
}
|
2021-07-23 23:44:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-06 18:06:01 +00:00
|
|
|
result_scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, _ ->
|
2021-05-20 15:22:28 +00:00
|
|
|
if (result_poster_blur == null) return@OnScrollChangeListener
|
2021-06-06 18:06:01 +00:00
|
|
|
result_poster_blur.alpha = maxOf(0f, (0.7f - scrollY / 1000f))
|
2021-06-16 16:54:07 +00:00
|
|
|
val setAlpha = 1f - scrollY / 200f
|
|
|
|
result_back.alpha = setAlpha
|
2021-06-06 18:06:01 +00:00
|
|
|
result_poster_blur_holder.translationY = -scrollY.toFloat()
|
2021-06-16 16:54:07 +00:00
|
|
|
// result_back.translationY = -scrollY.toFloat()
|
2021-06-06 18:06:01 +00:00
|
|
|
//result_barstatus.alpha = scrollY / 200f
|
|
|
|
//result_barstatus.visibility = if (scrollY > 0) View.VISIBLE else View.GONE§
|
2021-06-16 16:54:07 +00:00
|
|
|
result_back.visibility = if (setAlpha > 0) VISIBLE else GONE
|
2021-05-18 13:43:32 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
result_toolbar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
|
|
|
|
result_toolbar.setNavigationOnClickListener {
|
|
|
|
activity?.onBackPressed()
|
|
|
|
}
|
|
|
|
|
2021-06-16 16:54:07 +00:00
|
|
|
result_back.setOnClickListener {
|
2021-07-30 21:03:46 +00:00
|
|
|
activity?.popCurrentPage()
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
2021-06-10 15:15:14 +00:00
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
fun handleAction(episodeClick: EpisodeClickEvent): Job = main {
|
2021-06-16 16:54:07 +00:00
|
|
|
//val id = episodeClick.data.id
|
|
|
|
val index = episodeClick.data.index
|
|
|
|
val buildInPlayer = true
|
|
|
|
currentLoadingCount++
|
2021-07-17 14:14:25 +00:00
|
|
|
var currentLinks: ArrayList<ExtractorLink>? = null
|
|
|
|
var currentSubs: ArrayList<SubtitleFile>? = null
|
|
|
|
|
2021-07-17 15:56:26 +00:00
|
|
|
val showTitle = episodeClick.data.name ?: "Episode ${episodeClick.data.episode}"
|
|
|
|
|
|
|
|
suspend fun requireLinks(isCasting: Boolean): Boolean {
|
2021-07-17 14:14:25 +00:00
|
|
|
val currentLinksTemp =
|
|
|
|
if (allEpisodes.containsKey(episodeClick.data.id)) allEpisodes[episodeClick.data.id] else null
|
|
|
|
val currentSubsTemp =
|
2021-07-17 15:56:26 +00:00
|
|
|
if (allEpisodesSubs.containsKey(episodeClick.data.id)) allEpisodesSubs[episodeClick.data.id] else null
|
2021-07-17 14:14:25 +00:00
|
|
|
if (currentLinksTemp != null && currentLinksTemp.size > 0) {
|
|
|
|
currentLinks = currentLinksTemp
|
2021-07-17 15:56:26 +00:00
|
|
|
currentSubs = currentSubsTemp
|
2021-07-17 14:14:25 +00:00
|
|
|
return true
|
|
|
|
}
|
2021-06-16 22:31:41 +00:00
|
|
|
|
2021-08-06 20:55:11 +00:00
|
|
|
val skipLoading = getApiFromName(apiName).instantLinkLoading
|
2021-06-16 16:54:07 +00:00
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
var loadingDialog: AlertDialog? = null
|
|
|
|
val currentLoad = currentLoadingCount
|
2021-06-16 22:31:41 +00:00
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
if (!skipLoading) {
|
|
|
|
val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustomTransparent)
|
|
|
|
val customLayout = layoutInflater.inflate(R.layout.dialog_loading, null)
|
|
|
|
builder.setView(customLayout)
|
2021-06-16 16:54:07 +00:00
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
loadingDialog = builder.create()
|
|
|
|
|
|
|
|
loadingDialog.show()
|
|
|
|
loadingDialog.setOnDismissListener {
|
|
|
|
currentLoadingCount++
|
2021-06-10 15:15:14 +00:00
|
|
|
}
|
2021-07-17 14:14:25 +00:00
|
|
|
}
|
2021-06-16 22:31:41 +00:00
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
val data = viewModel.loadEpisode(episodeClick.data, isCasting)
|
|
|
|
if (currentLoadingCount != currentLoad) return false
|
|
|
|
loadingDialog?.dismiss()
|
2021-06-16 16:54:07 +00:00
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
when (data) {
|
|
|
|
is Resource.Success -> {
|
|
|
|
currentLinks = data.value.links
|
|
|
|
currentSubs = data.value.subs
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
is Resource.Failure -> {
|
|
|
|
Toast.makeText(requireContext(), R.string.error_loading_links, Toast.LENGTH_SHORT).show()
|
|
|
|
}
|
|
|
|
else -> {
|
2021-06-16 22:31:41 +00:00
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-08-22 17:14:48 +00:00
|
|
|
fun acquireSingeExtractorLink(
|
|
|
|
links: List<ExtractorLink>,
|
|
|
|
title: String,
|
|
|
|
callback: (ExtractorLink) -> Unit
|
|
|
|
) {
|
2021-07-17 15:56:26 +00:00
|
|
|
val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom)
|
|
|
|
|
|
|
|
builder.setTitle(title)
|
|
|
|
builder.setItems(links.map { it.name }.toTypedArray()) { dia, which ->
|
|
|
|
callback.invoke(links[which])
|
|
|
|
dia?.dismiss()
|
|
|
|
}
|
|
|
|
builder.create().show()
|
|
|
|
}
|
|
|
|
|
2021-08-22 17:14:48 +00:00
|
|
|
fun acquireSingeExtractorLink(title: String, callback: (ExtractorLink) -> Unit) {
|
|
|
|
acquireSingeExtractorLink(currentLinks ?: return, title, callback)
|
2021-07-17 15:56:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fun startChromecast(startIndex: Int) {
|
|
|
|
val eps = currentEpisodes ?: return
|
|
|
|
context?.startCast(
|
2021-08-19 20:05:18 +00:00
|
|
|
apiName,
|
2021-07-17 15:56:26 +00:00
|
|
|
currentIsMovie ?: return,
|
|
|
|
currentHeaderName,
|
|
|
|
currentPoster,
|
|
|
|
episodeClick.data.index,
|
|
|
|
eps,
|
|
|
|
sortUrls(currentLinks ?: return),
|
2021-07-18 13:02:30 +00:00
|
|
|
currentSubs ?: ArrayList(),
|
2021-07-17 15:56:26 +00:00
|
|
|
startTime = episodeClick.data.getRealPosition(),
|
|
|
|
startIndex = startIndex
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-08-22 17:14:48 +00:00
|
|
|
fun startDownload(links: List<ExtractorLink>, subs: List<SubtitleFile>?) {
|
2021-07-17 15:56:26 +00:00
|
|
|
val isMovie = currentIsMovie ?: return
|
|
|
|
val titleName = sanitizeFilename(currentHeaderName ?: return)
|
|
|
|
|
|
|
|
val meta = VideoDownloadManager.DownloadEpisodeMetadata(
|
|
|
|
episodeClick.data.id,
|
|
|
|
titleName,
|
2021-07-25 14:25:09 +00:00
|
|
|
apiName,
|
2021-07-17 15:56:26 +00:00
|
|
|
episodeClick.data.poster ?: currentPoster,
|
|
|
|
episodeClick.data.name,
|
|
|
|
if (isMovie) null else episodeClick.data.season,
|
|
|
|
if (isMovie) null else episodeClick.data.episode
|
|
|
|
)
|
|
|
|
|
|
|
|
val folder = when (currentType) {
|
|
|
|
TvType.Anime -> "Anime/$titleName"
|
|
|
|
TvType.Movie -> "Movies"
|
|
|
|
TvType.TvSeries -> "TVSeries/$titleName"
|
|
|
|
TvType.ONA -> "ONA"
|
2021-08-14 17:31:27 +00:00
|
|
|
TvType.Cartoon -> "Cartoons/$titleName"
|
2021-07-17 15:56:26 +00:00
|
|
|
else -> null
|
|
|
|
}
|
|
|
|
|
|
|
|
context?.let { ctx ->
|
2021-07-30 01:14:53 +00:00
|
|
|
val parentId = currentId ?: return@let
|
|
|
|
val src = "$DOWNLOAD_NAVIGATE_TO/$parentId" // url ?: return@let
|
|
|
|
|
2021-07-17 15:56:26 +00:00
|
|
|
// SET VISUAL KEYS
|
|
|
|
ctx.setKey(
|
2021-07-30 01:14:53 +00:00
|
|
|
DOWNLOAD_HEADER_CACHE, parentId.toString(),
|
2021-07-17 15:56:26 +00:00
|
|
|
VideoDownloadHelper.DownloadHeaderCached(
|
|
|
|
apiName,
|
|
|
|
url ?: return@let,
|
|
|
|
currentType ?: return@let,
|
|
|
|
currentHeaderName ?: return@let,
|
|
|
|
currentPoster ?: return@let,
|
2021-07-30 21:03:46 +00:00
|
|
|
currentId ?: return@let,
|
|
|
|
System.currentTimeMillis(),
|
2021-07-17 15:56:26 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
val epData = episodeClick.data
|
|
|
|
ctx.setKey(
|
2021-07-23 23:44:54 +00:00
|
|
|
getFolderName(
|
|
|
|
DOWNLOAD_EPISODE_CACHE,
|
2021-07-30 01:14:53 +00:00
|
|
|
parentId.toString()
|
2021-07-23 23:44:54 +00:00
|
|
|
), // 3 deep folder for faster acess
|
2021-07-17 15:56:26 +00:00
|
|
|
epData.id.toString(),
|
|
|
|
VideoDownloadHelper.DownloadEpisodeCached(
|
|
|
|
epData.name,
|
|
|
|
epData.poster,
|
|
|
|
epData.episode,
|
|
|
|
epData.season,
|
|
|
|
epData.id,
|
2021-07-30 01:14:53 +00:00
|
|
|
parentId,
|
2021-07-17 15:56:26 +00:00
|
|
|
epData.rating,
|
2021-07-30 21:03:46 +00:00
|
|
|
epData.descript,
|
|
|
|
System.currentTimeMillis(),
|
2021-07-17 15:56:26 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
// DOWNLOAD VIDEO
|
|
|
|
VideoDownloadManager.downloadEpisode(
|
|
|
|
ctx,
|
2021-07-30 01:14:53 +00:00
|
|
|
src,//url ?: return,
|
2021-07-17 15:56:26 +00:00
|
|
|
folder,
|
|
|
|
meta,
|
|
|
|
links
|
|
|
|
)
|
2021-08-22 17:14:48 +00:00
|
|
|
// 1. Checks if the lang should be downloaded
|
|
|
|
// 2. Makes it into the download format
|
|
|
|
// 3. Downloads it as a .vtt file
|
|
|
|
val downloadList = ctx.getDownloadSubsLanguageISO639_1()
|
|
|
|
main {
|
|
|
|
subs?.let { subsList ->
|
|
|
|
subsList.filter { downloadList.contains(SubtitleHelper.fromLanguageToTwoLetters(it.lang)) }
|
|
|
|
.map { ExtractorSubtitleLink(it.lang, it.url, "") }
|
|
|
|
.forEach { link ->
|
|
|
|
val epName = meta.name ?: "Episode ${meta.episode}"
|
|
|
|
val fileName =
|
|
|
|
sanitizeFilename(epName + if (downloadList.size > 1) " ${link.name}" else "")
|
|
|
|
val topFolder = "$folder"
|
|
|
|
|
|
|
|
withContext(Dispatchers.IO) {
|
|
|
|
VideoDownloadManager.downloadThing(
|
|
|
|
ctx,
|
|
|
|
link,
|
|
|
|
fileName,
|
|
|
|
topFolder,
|
|
|
|
"vtt",
|
|
|
|
false,
|
|
|
|
null
|
|
|
|
) {
|
|
|
|
// no notification
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-17 15:56:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
val isLoaded = when (episodeClick.action) {
|
|
|
|
ACTION_PLAY_EPISODE_IN_PLAYER -> true
|
2021-07-29 15:16:08 +00:00
|
|
|
ACTION_CLICK_DEFAULT -> true
|
2021-07-17 14:14:25 +00:00
|
|
|
ACTION_CHROME_CAST_EPISODE -> requireLinks(true)
|
|
|
|
ACTION_CHROME_CAST_MIRROR -> requireLinks(true)
|
2021-07-17 15:56:26 +00:00
|
|
|
else -> requireLinks(false)
|
2021-07-17 14:14:25 +00:00
|
|
|
}
|
|
|
|
if (!isLoaded) return@main // CANT LOAD
|
|
|
|
|
|
|
|
when (episodeClick.action) {
|
2021-07-23 23:44:54 +00:00
|
|
|
ACTION_CLICK_DEFAULT -> {
|
|
|
|
context?.let { ctx ->
|
|
|
|
if (ctx.isConnectedToChromecast()) {
|
|
|
|
handleAction(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, episodeClick.data))
|
|
|
|
} else {
|
|
|
|
handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, episodeClick.data))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
ACTION_SHOW_OPTIONS -> {
|
|
|
|
val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom)
|
|
|
|
var dialog: AlertDialog? = null
|
2021-07-17 15:56:26 +00:00
|
|
|
builder.setTitle(showTitle)
|
2021-07-17 14:14:25 +00:00
|
|
|
val options = requireContext().resources.getStringArray(R.array.episode_long_click_options)
|
|
|
|
val optionsValues =
|
|
|
|
requireContext().resources.getIntArray(R.array.episode_long_click_options_values)
|
|
|
|
|
|
|
|
val verifiedOptions = ArrayList<String>()
|
|
|
|
val verifiedOptionsValues = ArrayList<Int>()
|
|
|
|
|
2021-07-23 23:44:54 +00:00
|
|
|
val hasDownloadSupport = api.hasDownloadSupport
|
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
for (i in options.indices) {
|
|
|
|
val opv = optionsValues[i]
|
|
|
|
val op = options[i]
|
|
|
|
|
|
|
|
val isConnected = requireContext().isConnectedToChromecast()
|
|
|
|
val add = when (opv) {
|
|
|
|
ACTION_CHROME_CAST_EPISODE -> isConnected
|
|
|
|
ACTION_CHROME_CAST_MIRROR -> isConnected
|
2021-07-23 23:44:54 +00:00
|
|
|
ACTION_DOWNLOAD_EPISODE -> hasDownloadSupport
|
|
|
|
ACTION_DOWNLOAD_MIRROR -> hasDownloadSupport
|
2021-07-17 21:36:50 +00:00
|
|
|
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> context?.isAppInstalled(VLC_PACKAGE) ?: false
|
2021-07-17 14:14:25 +00:00
|
|
|
else -> true
|
|
|
|
}
|
|
|
|
if (add) {
|
|
|
|
verifiedOptions.add(op)
|
|
|
|
verifiedOptionsValues.add(opv)
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
2021-05-23 17:07:43 +00:00
|
|
|
}
|
2021-07-17 14:14:25 +00:00
|
|
|
|
|
|
|
builder.setItems(
|
|
|
|
verifiedOptions.toTypedArray()
|
|
|
|
) { _, which ->
|
|
|
|
handleAction(EpisodeClickEvent(verifiedOptionsValues[which], episodeClick.data))
|
|
|
|
dialog?.dismiss()
|
|
|
|
}
|
|
|
|
|
|
|
|
dialog = builder.create()
|
|
|
|
dialog.show()
|
|
|
|
}
|
2021-07-17 15:56:26 +00:00
|
|
|
ACTION_COPY_LINK -> {
|
2021-08-22 17:14:48 +00:00
|
|
|
acquireSingeExtractorLink("Copy Link") { link ->
|
2021-07-17 15:56:26 +00:00
|
|
|
val serviceClipboard =
|
|
|
|
(requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager?)
|
2021-08-22 17:14:48 +00:00
|
|
|
?: return@acquireSingeExtractorLink
|
2021-07-17 15:56:26 +00:00
|
|
|
val clip = ClipData.newPlainText(link.name, link.url)
|
|
|
|
serviceClipboard.setPrimaryClip(clip)
|
|
|
|
Toast.makeText(requireContext(), "Text Copied", Toast.LENGTH_SHORT).show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ACTION_PLAY_EPISODE_IN_BROWSER -> {
|
2021-08-22 17:14:48 +00:00
|
|
|
acquireSingeExtractorLink("Play in Browser") { link ->
|
2021-07-23 23:44:54 +00:00
|
|
|
val i = Intent(ACTION_VIEW)
|
2021-07-17 15:56:26 +00:00
|
|
|
i.data = Uri.parse(link.url)
|
|
|
|
startActivity(i)
|
|
|
|
}
|
|
|
|
}
|
2021-07-17 14:14:25 +00:00
|
|
|
|
2021-07-17 15:56:26 +00:00
|
|
|
ACTION_CHROME_CAST_MIRROR -> {
|
2021-08-22 17:14:48 +00:00
|
|
|
acquireSingeExtractorLink("Cast Mirror") { link ->
|
2021-07-17 15:56:26 +00:00
|
|
|
val mirrorIndex = currentLinks?.indexOf(link) ?: -1
|
|
|
|
startChromecast(if (mirrorIndex == -1) 0 else mirrorIndex)
|
|
|
|
}
|
|
|
|
}
|
2021-07-17 14:14:25 +00:00
|
|
|
|
|
|
|
ACTION_CHROME_CAST_EPISODE -> {
|
2021-07-17 15:56:26 +00:00
|
|
|
startChromecast(0)
|
|
|
|
}
|
2021-07-17 14:14:25 +00:00
|
|
|
|
2021-07-17 21:36:50 +00:00
|
|
|
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> {
|
2021-07-30 21:03:46 +00:00
|
|
|
activity?.let { act ->
|
|
|
|
if (!act.checkWrite()) {
|
|
|
|
act.requestRW()
|
|
|
|
if (act.checkWrite()) return@main
|
2021-07-17 15:56:26 +00:00
|
|
|
}
|
2021-07-30 21:03:46 +00:00
|
|
|
val data = currentLinks ?: return@main
|
|
|
|
val subs = currentSubs
|
|
|
|
|
|
|
|
val outputDir = requireContext().cacheDir
|
2021-07-31 12:33:15 +00:00
|
|
|
val outputFile = withContext(Dispatchers.IO) {
|
2021-07-30 21:03:46 +00:00
|
|
|
File.createTempFile("mirrorlist", ".m3u8", outputDir)
|
|
|
|
}
|
|
|
|
var text = "#EXTM3U"
|
|
|
|
if (subs != null) {
|
|
|
|
for (sub in subs) {
|
|
|
|
text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.lang}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.lang}\",URI=\"${sub.url}\""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (link in data.sortedBy { -it.quality }) {
|
|
|
|
text += "\n#EXTINF:, ${link.name}\n${link.url}"
|
|
|
|
}
|
|
|
|
outputFile.writeText(text)
|
2021-07-17 15:56:26 +00:00
|
|
|
|
|
|
|
|
2021-07-30 21:03:46 +00:00
|
|
|
val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
|
2021-07-17 15:56:26 +00:00
|
|
|
|
2021-07-30 21:03:46 +00:00
|
|
|
vlcIntent.setPackage(VLC_PACKAGE)
|
|
|
|
vlcIntent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
|
|
|
vlcIntent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION)
|
|
|
|
vlcIntent.addFlags(FLAG_GRANT_READ_URI_PERMISSION)
|
|
|
|
vlcIntent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION)
|
2021-07-17 15:56:26 +00:00
|
|
|
|
2021-07-30 21:03:46 +00:00
|
|
|
vlcIntent.setDataAndType(
|
|
|
|
FileProvider.getUriForFile(
|
|
|
|
act,
|
|
|
|
act.applicationContext.packageName + ".provider",
|
|
|
|
outputFile
|
|
|
|
), "video/*"
|
|
|
|
)
|
2021-07-17 15:56:26 +00:00
|
|
|
|
2021-07-30 21:03:46 +00:00
|
|
|
val startId = VLC_FROM_PROGRESS
|
2021-07-17 15:56:26 +00:00
|
|
|
|
2021-07-30 21:03:46 +00:00
|
|
|
var position = startId
|
|
|
|
if (startId == VLC_FROM_START) {
|
|
|
|
position = 1
|
|
|
|
} else if (startId == VLC_FROM_PROGRESS) {
|
|
|
|
position = 0
|
|
|
|
}
|
2021-07-17 15:56:26 +00:00
|
|
|
|
2021-07-30 21:03:46 +00:00
|
|
|
vlcIntent.putExtra("position", position)
|
2021-07-17 15:56:26 +00:00
|
|
|
|
2021-07-30 21:03:46 +00:00
|
|
|
vlcIntent.component = VLC_COMPONENT
|
|
|
|
requireContext().setKey(VLC_LAST_ID_KEY, episodeClick.data.id)
|
|
|
|
act.startActivityForResult(vlcIntent, VLC_REQUEST_CODE)
|
|
|
|
}
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
2021-06-10 15:15:14 +00:00
|
|
|
|
2021-06-16 16:54:07 +00:00
|
|
|
ACTION_PLAY_EPISODE_IN_PLAYER -> {
|
|
|
|
if (buildInPlayer) {
|
2021-07-30 21:03:46 +00:00
|
|
|
(activity as AppCompatActivity?)?.supportFragmentManager?.beginTransaction()
|
|
|
|
?.setCustomAnimations(
|
2021-07-02 18:46:18 +00:00
|
|
|
R.anim.enter_anim,
|
2021-06-16 16:54:07 +00:00
|
|
|
R.anim.exit_anim,
|
|
|
|
R.anim.pop_enter,
|
2021-07-02 18:46:18 +00:00
|
|
|
R.anim.pop_exit
|
2021-07-30 21:03:46 +00:00
|
|
|
)?.add(
|
2021-07-02 18:46:18 +00:00
|
|
|
R.id.homeRoot,
|
|
|
|
PlayerFragment.newInstance(
|
|
|
|
PlayerData(index, null, 0),
|
|
|
|
episodeClick.data.getRealPosition()
|
|
|
|
)
|
2021-07-30 21:03:46 +00:00
|
|
|
)?.commit()
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
2021-05-23 17:07:43 +00:00
|
|
|
}
|
2021-07-17 15:56:26 +00:00
|
|
|
|
2021-06-16 16:54:07 +00:00
|
|
|
ACTION_RELOAD_EPISODE -> {
|
2021-07-17 14:14:25 +00:00
|
|
|
viewModel.loadEpisode(episodeClick.data, false)
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
2021-07-17 14:14:25 +00:00
|
|
|
|
2021-07-17 15:56:26 +00:00
|
|
|
ACTION_DOWNLOAD_EPISODE -> {
|
2021-08-22 17:14:48 +00:00
|
|
|
startDownload(currentLinks ?: return@main, currentSubs)
|
2021-07-17 15:56:26 +00:00
|
|
|
}
|
2021-07-17 14:14:25 +00:00
|
|
|
|
2021-07-17 15:56:26 +00:00
|
|
|
ACTION_DOWNLOAD_MIRROR -> {
|
2021-08-22 17:14:48 +00:00
|
|
|
acquireSingeExtractorLink(
|
2021-07-17 15:56:26 +00:00
|
|
|
(currentLinks ?: return@main).filter { !it.isM3u8 },
|
|
|
|
"Download Mirror"
|
|
|
|
) { link ->
|
2021-08-22 17:14:48 +00:00
|
|
|
startDownload(listOf(link), currentSubs)
|
2021-06-29 23:14:48 +00:00
|
|
|
}
|
|
|
|
}
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
|
2021-06-16 16:54:07 +00:00
|
|
|
EpisodeAdapter(
|
|
|
|
ArrayList(),
|
2021-07-23 23:44:54 +00:00
|
|
|
api.hasDownloadSupport,
|
2021-07-24 20:50:57 +00:00
|
|
|
{ episodeClick ->
|
|
|
|
handleAction(episodeClick)
|
|
|
|
},
|
|
|
|
{ downloadClickEvent ->
|
|
|
|
handleDownloadClick(activity, currentHeaderName, downloadClickEvent)
|
|
|
|
}
|
|
|
|
)
|
2021-07-17 14:14:25 +00:00
|
|
|
|
2021-05-23 17:07:43 +00:00
|
|
|
result_episodes.adapter = adapter
|
|
|
|
result_episodes.layoutManager = GridLayoutManager(context, 1)
|
|
|
|
|
2021-06-15 23:25:58 +00:00
|
|
|
result_bookmark_button.setOnClickListener {
|
|
|
|
it.popupMenuNoIcons(
|
|
|
|
items = WatchType.values()
|
|
|
|
.map { watchType -> Pair(watchType.internalId, watchType.stringRes) },
|
2021-06-16 00:15:07 +00:00
|
|
|
//.map { watchType -> Triple(watchType.internalId, watchType.iconRes, watchType.stringRes) },
|
2021-06-15 23:25:58 +00:00
|
|
|
) {
|
|
|
|
context?.let { localContext ->
|
|
|
|
viewModel.updateWatchStatus(localContext, WatchType.fromInternalId(this.itemId))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
observe(viewModel.watchStatus) {
|
|
|
|
//result_bookmark_button.setIconResource(it.iconRes)
|
|
|
|
result_bookmark_button.text = getString(it.stringRes)
|
|
|
|
}
|
|
|
|
|
2021-07-29 15:16:08 +00:00
|
|
|
observe(viewModel.episodes) { episodeList ->
|
2021-08-22 17:14:48 +00:00
|
|
|
lateFixDownloadButton(episodeList.size <= 1) // movies can have multible parts but still be *movies* this will fix this
|
2021-08-11 18:26:19 +00:00
|
|
|
|
2021-07-29 15:16:08 +00:00
|
|
|
when (startAction) {
|
|
|
|
START_ACTION_RESUME_LATEST -> {
|
|
|
|
for (ep in episodeList) {
|
|
|
|
if (ep.getWatchProgress() > 0.90f) { // watched too much
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, ep))
|
|
|
|
startAction = null
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else -> {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-20 20:56:21 +00:00
|
|
|
observe(viewModel.allEpisodes) {
|
|
|
|
allEpisodes = it
|
|
|
|
}
|
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
observe(viewModel.allEpisodesSubs) {
|
|
|
|
allEpisodesSubs = it
|
|
|
|
}
|
|
|
|
|
2021-06-26 22:15:19 +00:00
|
|
|
observe(viewModel.selectedSeason) { season ->
|
|
|
|
result_season_button?.text = fromIndexToSeasonText(season)
|
|
|
|
}
|
|
|
|
|
|
|
|
observe(viewModel.seasonSelections) { seasonList ->
|
|
|
|
result_season_button?.visibility = if (seasonList.size <= 1) GONE else VISIBLE
|
|
|
|
result_season_button?.setOnClickListener {
|
2021-07-25 20:50:16 +00:00
|
|
|
result_season_button?.popupMenuNoIconsAndNoStringRes(
|
2021-06-26 22:15:19 +00:00
|
|
|
items = seasonList
|
|
|
|
.map { Pair(it ?: -2, fromIndexToSeasonText(it)) },
|
|
|
|
) {
|
|
|
|
val id = this.itemId
|
|
|
|
context?.let {
|
|
|
|
viewModel.changeSeason(it, if (id == -2) null else id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
observe(viewModel.publicEpisodes) { episodes ->
|
2021-05-28 13:38:06 +00:00
|
|
|
if (result_episodes == null || result_episodes.adapter == null) return@observe
|
2021-06-14 16:58:43 +00:00
|
|
|
currentEpisodes = episodes
|
2021-07-31 12:33:15 +00:00
|
|
|
(result_episodes?.adapter as EpisodeAdapter?)?.cardList = episodes
|
|
|
|
(result_episodes?.adapter as EpisodeAdapter?)?.updateLayout()
|
|
|
|
(result_episodes?.adapter as EpisodeAdapter?)?.notifyDataSetChanged()
|
2021-05-22 22:25:56 +00:00
|
|
|
}
|
|
|
|
|
2021-07-25 20:50:16 +00:00
|
|
|
observe(viewModel.selectedRange) { range ->
|
|
|
|
result_episode_select?.text = range
|
|
|
|
}
|
|
|
|
|
|
|
|
observe(viewModel.rangeOptions) { range ->
|
|
|
|
episodeRanges = range
|
|
|
|
result_episode_select?.visibility = if (range.size <= 1) GONE else VISIBLE
|
|
|
|
}
|
|
|
|
|
|
|
|
result_episode_select.setOnClickListener {
|
|
|
|
val ranges = episodeRanges
|
|
|
|
if (ranges != null) {
|
|
|
|
it.popupMenuNoIconsAndNoStringRes(ranges.mapIndexed { index, s -> Pair(index, s) }.toList()) {
|
|
|
|
viewModel.changeRange(requireContext(), itemId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
observe(viewModel.publicEpisodesCount) { count ->
|
|
|
|
result_episodes_text.text = "$count Episode${if (count == 1) "" else "s"}"
|
|
|
|
}
|
|
|
|
|
2021-07-17 14:14:25 +00:00
|
|
|
observe(viewModel.id) {
|
|
|
|
currentId = it
|
|
|
|
}
|
|
|
|
|
2021-05-18 13:43:32 +00:00
|
|
|
observe(viewModel.resultResponse) { data ->
|
|
|
|
when (data) {
|
|
|
|
is Resource.Success -> {
|
|
|
|
val d = data.value
|
|
|
|
if (d is LoadResponse) {
|
2021-06-16 16:54:07 +00:00
|
|
|
updateVisStatus(2)
|
|
|
|
|
2021-05-18 13:43:32 +00:00
|
|
|
result_bookmark_button.text = "Watching"
|
|
|
|
|
2021-06-14 00:00:29 +00:00
|
|
|
currentHeaderName = d.name
|
2021-07-04 00:59:51 +00:00
|
|
|
currentType = d.type
|
2021-06-14 00:00:29 +00:00
|
|
|
|
2021-06-10 15:15:14 +00:00
|
|
|
currentPoster = d.posterUrl
|
2021-06-16 22:31:41 +00:00
|
|
|
currentIsMovie = !d.isEpisodeBased()
|
2021-06-10 15:15:14 +00:00
|
|
|
|
|
|
|
result_openinbrower.setOnClickListener {
|
2021-07-23 23:44:54 +00:00
|
|
|
val i = Intent(ACTION_VIEW)
|
2021-06-10 15:15:14 +00:00
|
|
|
i.data = Uri.parse(d.url)
|
|
|
|
startActivity(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
result_share.setOnClickListener {
|
2021-07-23 23:44:54 +00:00
|
|
|
val i = Intent(ACTION_SEND)
|
2021-06-10 15:15:14 +00:00
|
|
|
i.type = "text/plain"
|
2021-07-23 23:44:54 +00:00
|
|
|
i.putExtra(EXTRA_SUBJECT, d.name)
|
|
|
|
i.putExtra(EXTRA_TEXT, d.url)
|
|
|
|
startActivity(createChooser(i, d.name))
|
2021-06-10 15:15:14 +00:00
|
|
|
}
|
|
|
|
|
2021-06-26 17:03:22 +00:00
|
|
|
val metadataInfoArray = ArrayList<Pair<String, String>>()
|
|
|
|
if (d is AnimeLoadResponse) {
|
|
|
|
val status = when (d.showStatus) {
|
|
|
|
null -> null
|
|
|
|
ShowStatus.Ongoing -> "Ongoing"
|
|
|
|
ShowStatus.Completed -> "Completed"
|
|
|
|
}
|
|
|
|
if (status != null) {
|
|
|
|
metadataInfoArray.add(Pair("Status", status))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (d.year != null) metadataInfoArray.add(Pair("Year", d.year.toString()))
|
|
|
|
val rating = d.rating
|
2021-07-02 18:46:18 +00:00
|
|
|
if (rating != null) metadataInfoArray.add(
|
|
|
|
Pair(
|
|
|
|
"Rating",
|
|
|
|
"%.1f/10.0".format(rating.toFloat() / 10f).replace(",", ".")
|
|
|
|
)
|
|
|
|
)
|
2021-06-26 17:03:22 +00:00
|
|
|
val duration = d.duration
|
|
|
|
if (duration != null) metadataInfoArray.add(Pair("Duration", duration))
|
|
|
|
|
2021-07-23 23:44:54 +00:00
|
|
|
metadataInfoArray.add(Pair("Site", d.apiName))
|
|
|
|
|
2021-06-26 17:03:22 +00:00
|
|
|
if (metadataInfoArray.size > 0) {
|
|
|
|
result_metadata.visibility = VISIBLE
|
|
|
|
val text = SpannableStringBuilder()
|
|
|
|
val grayColor = ContextCompat.getColor(requireContext(), R.color.grayTextColor)
|
|
|
|
val textColor = ContextCompat.getColor(requireContext(), R.color.textColor)
|
|
|
|
for (meta in metadataInfoArray) {
|
|
|
|
text.color(grayColor) { append("${meta.first}: ") }
|
|
|
|
.color(textColor) { append("${meta.second}\n") }
|
|
|
|
}
|
|
|
|
result_metadata.text = text
|
2021-05-18 13:43:32 +00:00
|
|
|
} else {
|
2021-06-26 17:03:22 +00:00
|
|
|
result_metadata.visibility = GONE
|
2021-05-18 13:43:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (d.posterUrl != null) {
|
2021-08-19 20:05:18 +00:00
|
|
|
result_poster?.setImage(d.posterUrl)
|
2021-05-20 20:56:21 +00:00
|
|
|
}
|
|
|
|
|
2021-05-22 22:25:56 +00:00
|
|
|
if (d.plot != null) {
|
|
|
|
var syno = d.plot!!
|
|
|
|
if (syno.length > MAX_SYNO_LENGH) {
|
|
|
|
syno = syno.substring(0, MAX_SYNO_LENGH) + "..."
|
2021-05-18 13:43:32 +00:00
|
|
|
}
|
2021-05-22 22:25:56 +00:00
|
|
|
result_descript.setOnClickListener {
|
|
|
|
val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
|
|
|
|
builder.setMessage(d.plot).setTitle("Synopsis")
|
|
|
|
.show()
|
2021-05-18 13:43:32 +00:00
|
|
|
}
|
2021-05-22 22:25:56 +00:00
|
|
|
result_descript.text = syno
|
2021-05-18 13:43:32 +00:00
|
|
|
} else {
|
2021-05-22 22:25:56 +00:00
|
|
|
result_descript.text = "No Plot found"
|
|
|
|
}
|
|
|
|
|
2021-06-06 18:06:01 +00:00
|
|
|
result_tag.removeAllViews()
|
2021-06-15 16:07:20 +00:00
|
|
|
result_tag_holder.visibility = GONE
|
2021-06-26 17:03:22 +00:00
|
|
|
// result_status.visibility = GONE
|
2021-06-06 18:06:01 +00:00
|
|
|
|
2021-06-26 14:44:53 +00:00
|
|
|
val tags = d.tags
|
|
|
|
if (tags == null) {
|
|
|
|
result_tag_holder.visibility = GONE
|
|
|
|
} else {
|
|
|
|
result_tag_holder.visibility = VISIBLE
|
|
|
|
|
|
|
|
for ((index, tag) in tags.withIndex()) {
|
|
|
|
val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
|
|
|
|
val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
|
|
|
|
btt.text = tag
|
|
|
|
|
|
|
|
result_tag.addView(viewBtt, index)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-14 19:35:26 +00:00
|
|
|
if (d.type.isMovieType()) {
|
2021-07-25 20:50:16 +00:00
|
|
|
val hasDownloadSupport = api.hasDownloadSupport
|
2021-08-11 18:26:19 +00:00
|
|
|
lateFixDownloadButton(true)
|
2021-07-23 23:44:54 +00:00
|
|
|
|
|
|
|
result_play_movie.setOnClickListener {
|
|
|
|
val card = currentEpisodes?.first() ?: return@setOnClickListener
|
|
|
|
handleAction(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
2021-07-25 16:08:34 +00:00
|
|
|
|
|
|
|
result_play_movie.setOnLongClickListener {
|
|
|
|
val card = currentEpisodes?.first() ?: return@setOnLongClickListener true
|
2021-07-23 23:44:54 +00:00
|
|
|
handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
|
2021-07-25 16:08:34 +00:00
|
|
|
return@setOnLongClickListener true
|
|
|
|
}
|
|
|
|
|
2021-07-25 20:50:16 +00:00
|
|
|
|
2021-07-25 16:08:34 +00:00
|
|
|
// result_options.setOnClickListener {
|
|
|
|
// val card = currentEpisodes?.first() ?: return@setOnClickListener
|
|
|
|
// handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
|
|
|
|
// }
|
|
|
|
|
2021-07-25 20:50:16 +00:00
|
|
|
result_download_movie.visibility = if (hasDownloadSupport) VISIBLE else GONE
|
|
|
|
if (hasDownloadSupport) {
|
|
|
|
val localId = d.getId()
|
|
|
|
val file =
|
|
|
|
VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(requireContext(), localId)
|
2021-07-28 19:14:45 +00:00
|
|
|
downloadButton?.dispose()
|
|
|
|
downloadButton = EasyDownloadButton()
|
|
|
|
downloadButton?.setUpMaterialButton(
|
2021-07-25 20:50:16 +00:00
|
|
|
file?.fileLength,
|
|
|
|
file?.totalBytes,
|
|
|
|
result_movie_progress_downloaded,
|
|
|
|
result_download_movie,
|
|
|
|
result_movie_text_progress,
|
|
|
|
VideoDownloadHelper.DownloadEpisodeCached(
|
|
|
|
d.name,
|
|
|
|
d.posterUrl,
|
|
|
|
0,
|
|
|
|
null,
|
|
|
|
localId,
|
|
|
|
localId,
|
|
|
|
d.rating,
|
2021-07-30 21:03:46 +00:00
|
|
|
d.plot,
|
|
|
|
System.currentTimeMillis(),
|
2021-07-25 20:50:16 +00:00
|
|
|
)
|
|
|
|
) { downloadClickEvent ->
|
|
|
|
if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
|
2021-08-11 18:26:19 +00:00
|
|
|
currentEpisodes?.first()?.let { episode ->
|
|
|
|
handleAction(
|
|
|
|
EpisodeClickEvent(
|
|
|
|
ACTION_DOWNLOAD_EPISODE,
|
|
|
|
ResultEpisode(
|
|
|
|
d.name,
|
|
|
|
null,
|
|
|
|
0,
|
|
|
|
null,
|
|
|
|
episode.data,
|
|
|
|
d.apiName,
|
|
|
|
localId,
|
|
|
|
0,
|
|
|
|
0L,
|
|
|
|
0L,
|
|
|
|
null,
|
|
|
|
null
|
|
|
|
)
|
2021-07-25 20:50:16 +00:00
|
|
|
)
|
2021-07-25 16:08:34 +00:00
|
|
|
)
|
2021-08-11 18:26:19 +00:00
|
|
|
}
|
2021-07-25 20:50:16 +00:00
|
|
|
} else {
|
|
|
|
handleDownloadClick(activity, currentHeaderName, downloadClickEvent)
|
|
|
|
}
|
2021-07-25 16:08:34 +00:00
|
|
|
}
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
2021-07-23 23:44:54 +00:00
|
|
|
} else {
|
2021-08-11 18:26:19 +00:00
|
|
|
lateFixDownloadButton(false)
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
|
|
|
|
2021-05-22 22:25:56 +00:00
|
|
|
when (d) {
|
|
|
|
is AnimeLoadResponse -> {
|
2021-06-06 18:06:01 +00:00
|
|
|
|
|
|
|
// val preferEnglish = true
|
|
|
|
//val titleName = (if (preferEnglish) d.engName else d.japName) ?: d.name
|
|
|
|
val titleName = d.name
|
2021-05-22 22:25:56 +00:00
|
|
|
result_title.text = titleName
|
|
|
|
result_toolbar.title = titleName
|
|
|
|
}
|
|
|
|
else -> result_title.text = d.name
|
2021-05-18 13:43:32 +00:00
|
|
|
}
|
2021-06-16 16:54:07 +00:00
|
|
|
} else {
|
|
|
|
updateVisStatus(1)
|
2021-05-18 13:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
is Resource.Failure -> {
|
2021-07-08 18:03:17 +00:00
|
|
|
result_error_text.text = data.errorString
|
2021-06-16 16:54:07 +00:00
|
|
|
updateVisStatus(1)
|
|
|
|
}
|
|
|
|
is Resource.Loading -> {
|
|
|
|
updateVisStatus(0)
|
2021-05-18 13:43:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-30 14:09:57 +00:00
|
|
|
val tempUrl = url
|
|
|
|
if (tempUrl != null) {
|
2021-06-16 16:54:07 +00:00
|
|
|
result_reload_connectionerror.setOnClickListener {
|
2021-07-30 14:09:57 +00:00
|
|
|
viewModel.load(requireContext(), tempUrl, apiName)
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
|
|
|
|
2021-07-30 14:09:57 +00:00
|
|
|
result_reload_connection_open_in_browser.setOnClickListener {
|
|
|
|
val i = Intent(ACTION_VIEW)
|
|
|
|
i.data = Uri.parse(tempUrl)
|
|
|
|
startActivity(i)
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (viewModel.resultResponse.value == null)
|
2021-07-30 14:09:57 +00:00
|
|
|
viewModel.load(requireContext(), tempUrl, apiName)
|
2021-06-16 16:54:07 +00:00
|
|
|
}
|
2021-05-16 18:28:00 +00:00
|
|
|
}
|
|
|
|
}
|