AquaStream/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt

479 lines
18 KiB
Kotlin
Raw Normal View History

2021-05-16 18:28:00 +00:00
package com.lagradost.cloudstream3.ui.result
2022-04-03 01:13:02 +00:00
import android.util.Log
2021-10-19 20:17:06 +00:00
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
2021-05-22 22:25:56 +00:00
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
2022-03-20 18:02:52 +00:00
import com.lagradost.cloudstream3.APIHolder.getApiFromUrlNull
2021-07-25 16:08:34 +00:00
import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
2021-05-18 13:43:32 +00:00
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.safeApiCall
2022-04-03 01:13:02 +00:00
import com.lagradost.cloudstream3.syncproviders.SyncAPI
2021-07-29 00:19:42 +00:00
import com.lagradost.cloudstream3.ui.APIRepository
2021-06-15 23:25:58 +00:00
import com.lagradost.cloudstream3.ui.WatchType
2022-01-07 19:27:25 +00:00
import com.lagradost.cloudstream3.ui.player.IGenerator
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
import com.lagradost.cloudstream3.ui.player.SubtitleData
2022-02-06 15:43:29 +00:00
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
import com.lagradost.cloudstream3.utils.DataStoreHelper
2021-07-30 21:03:46 +00:00
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDub
2021-06-26 22:15:19 +00:00
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason
2021-06-15 23:25:58 +00:00
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
2021-06-15 16:07:20 +00:00
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
2021-07-30 14:09:57 +00:00
import com.lagradost.cloudstream3.utils.DataStoreHelper.setBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.setDub
2021-06-26 22:15:19 +00:00
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason
2021-06-15 23:25:58 +00:00
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
2022-02-06 15:43:29 +00:00
import com.lagradost.cloudstream3.utils.ExtractorLink
2021-09-19 22:36:32 +00:00
import com.lagradost.cloudstream3.utils.FillerEpisodeCheck.getFillerEpisodes
2022-02-06 15:43:29 +00:00
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
2021-07-31 12:33:15 +00:00
import kotlinx.coroutines.Dispatchers
2021-05-18 13:43:32 +00:00
import kotlinx.coroutines.launch
2021-07-31 12:33:15 +00:00
import kotlinx.coroutines.withContext
2021-10-19 20:17:06 +00:00
import kotlin.collections.set
2021-05-16 18:28:00 +00:00
2021-07-25 20:50:16 +00:00
const val EPISODE_RANGE_SIZE = 50
const val EPISODE_RANGE_OVERLOAD = 60
2021-05-16 18:28:00 +00:00
class ResultViewModel : ViewModel() {
2021-10-19 20:17:06 +00:00
private var repo: APIRepository? = null
2022-01-07 19:27:25 +00:00
private var generator: IGenerator? = null
2022-04-03 01:13:02 +00:00
private val _resultResponse: MutableLiveData<Resource<LoadResponse>> = MutableLiveData()
2021-06-15 16:07:20 +00:00
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
2021-12-27 21:56:47 +00:00
private val episodeById: MutableLiveData<HashMap<Int, Int>> =
MutableLiveData() // lookup by ID to get Index
2021-08-25 15:28:25 +00:00
2021-09-19 22:36:32 +00:00
private val _publicEpisodes: MutableLiveData<Resource<List<ResultEpisode>>> = MutableLiveData()
2021-07-25 20:50:16 +00:00
private val _publicEpisodesCount: MutableLiveData<Int> = MutableLiveData() // before the sorting
private val _rangeOptions: MutableLiveData<List<String>> = MutableLiveData()
val selectedRange: MutableLiveData<String> = MutableLiveData()
private val selectedRangeInt: MutableLiveData<Int> = MutableLiveData()
val rangeOptions: LiveData<List<String>> = _rangeOptions
2022-04-03 01:13:02 +00:00
val result: LiveData<Resource<LoadResponse>> get() = _resultResponse
2021-06-15 16:07:20 +00:00
val episodes: LiveData<List<ResultEpisode>> get() = _episodes
2021-09-19 22:36:32 +00:00
val publicEpisodes: LiveData<Resource<List<ResultEpisode>>> get() = _publicEpisodes
2021-07-25 20:50:16 +00:00
val publicEpisodesCount: LiveData<Int> get() = _publicEpisodesCount
2021-06-26 22:15:19 +00:00
2021-11-05 21:39:56 +00:00
val dubStatus: LiveData<DubStatus> get() = _dubStatus
2021-11-02 15:09:29 +00:00
private val _dubStatus: MutableLiveData<DubStatus> = MutableLiveData()
2021-05-16 18:28:00 +00:00
2021-06-15 23:25:58 +00:00
private val page: MutableLiveData<LoadResponse> = MutableLiveData()
2021-07-17 14:14:25 +00:00
val id: MutableLiveData<Int> = MutableLiveData()
2021-06-26 22:15:19 +00:00
val selectedSeason: MutableLiveData<Int> = MutableLiveData(-2)
val seasonSelections: MutableLiveData<List<Int?>> = MutableLiveData()
2021-06-15 23:25:58 +00:00
2021-11-05 21:39:56 +00:00
val dubSubSelections: LiveData<Set<DubStatus>> get() = _dubSubSelections
2021-11-02 15:09:29 +00:00
private val _dubSubSelections: MutableLiveData<Set<DubStatus>> = MutableLiveData()
2021-11-05 21:39:56 +00:00
val dubSubEpisodes: LiveData<Map<DubStatus, List<ResultEpisode>>?> get() = _dubSubEpisodes
2021-12-27 21:56:47 +00:00
private val _dubSubEpisodes: MutableLiveData<Map<DubStatus, List<ResultEpisode>>?> =
MutableLiveData()
2021-11-02 15:09:29 +00:00
2021-06-15 23:25:58 +00:00
private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData()
val watchStatus: LiveData<WatchType> get() = _watchStatus
fun updateWatchStatus(status: WatchType) = viewModelScope.launch {
2021-07-31 12:33:15 +00:00
val currentId = id.value ?: return@launch
2021-06-15 23:25:58 +00:00
_watchStatus.postValue(status)
2021-07-30 14:09:57 +00:00
val resultPage = page.value
2021-07-31 12:33:15 +00:00
withContext(Dispatchers.IO) {
setResultWatchState(currentId, status.internalId)
2021-07-31 12:33:15 +00:00
if (resultPage != null) {
val current = getBookmarkedData(currentId)
2021-07-31 12:33:15 +00:00
val currentTime = System.currentTimeMillis()
setBookmarkedData(
2021-07-30 14:09:57 +00:00
currentId,
2021-07-31 12:33:15 +00:00
DataStoreHelper.BookmarkedData(
currentId,
current?.bookmarkedTime ?: currentTime,
currentTime,
resultPage.name,
resultPage.url,
resultPage.apiName,
resultPage.type,
resultPage.posterUrl,
resultPage.year
)
2021-07-30 14:09:57 +00:00
)
2021-07-31 12:33:15 +00:00
}
2021-07-30 14:09:57 +00:00
}
2021-06-15 23:25:58 +00:00
}
2022-04-03 01:13:02 +00:00
companion object {
const val TAG = "RVM"
}
var lastMeta: SyncAPI.SyncResult? = null
private fun applyMeta(resp: LoadResponse, meta: SyncAPI.SyncResult?): LoadResponse {
if (meta == null) return resp
lastMeta = meta
return resp.apply {
Log.i(TAG, "applyMeta")
duration = duration ?: meta.duration
rating = rating ?: meta.publicScore
tags = tags ?: meta.genres
plot = if (plot.isNullOrBlank()) meta.synopsis else plot
trailerUrl = trailerUrl ?: meta.trailerUrl
posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl
actors = actors ?: meta.actors?.map {
ActorData(
Actor(
name = it.name,
image = it.posterUrl
)
)
}
}
}
fun setMeta(meta: SyncAPI.SyncResult) {
Log.i(TAG, "setMeta")
(result.value as? Resource.Success<LoadResponse>?)?.value?.let { resp ->
_resultResponse.postValue(Resource.Success(applyMeta(resp, meta)))
}
}
private fun loadWatchStatus(localId: Int? = null) {
2021-06-15 23:25:58 +00:00
val currentId = localId ?: id.value ?: return
val currentWatch = getResultWatchState(currentId)
2021-06-15 23:25:58 +00:00
_watchStatus.postValue(currentWatch)
}
private fun filterEpisodes(list: List<ResultEpisode>?, selection: Int?, range: Int?) {
2021-06-26 22:15:19 +00:00
if (list == null) return
val seasonTypes = HashMap<Int?, Boolean>()
for (i in list) {
if (!seasonTypes.containsKey(i.season)) {
seasonTypes[i.season] = true
}
}
val seasons = seasonTypes.toList().map { it.first }.sortedBy { it }
2021-06-26 22:15:19 +00:00
seasonSelections.postValue(seasons)
2021-08-25 15:28:25 +00:00
if (seasons.isEmpty()) { // WHAT THE FUCK DID YOU DO????? HOW DID YOU DO THIS
2021-11-30 17:59:52 +00:00
_publicEpisodes.postValue(Resource.Success(emptyList()))
2021-07-31 12:33:15 +00:00
return
}
val realSelection = if (!seasonTypes.containsKey(selection)) seasons.first() else selection
2021-06-26 22:15:19 +00:00
val internalId = id.value
if (internalId != null) setResultSeason(internalId, realSelection)
2021-06-26 22:15:19 +00:00
selectedSeason.postValue(realSelection ?: -2)
2021-07-25 20:50:16 +00:00
var currentList = list.filter { it.season == realSelection }
_publicEpisodesCount.postValue(currentList.size)
val rangeList = ArrayList<String>()
for (i in currentList.indices step EPISODE_RANGE_SIZE) {
if (i + EPISODE_RANGE_SIZE < currentList.size) {
rangeList.add("${i + 1}-${i + EPISODE_RANGE_SIZE}")
} else {
rangeList.add("${i + 1}-${currentList.size}")
}
}
val cRange = range ?: if (selection != null) {
0
} else {
selectedRangeInt.value ?: 0
}
val realRange = if (cRange * EPISODE_RANGE_SIZE > currentList.size) {
currentList.size / EPISODE_RANGE_SIZE
} else {
cRange
}
if (currentList.size > EPISODE_RANGE_OVERLOAD) {
currentList = currentList.subList(
realRange * EPISODE_RANGE_SIZE,
minOf(currentList.size, (realRange + 1) * EPISODE_RANGE_SIZE)
)
2021-08-14 17:31:27 +00:00
_rangeOptions.postValue(rangeList)
selectedRangeInt.postValue(realRange)
selectedRange.postValue(rangeList[realRange])
} else {
2021-08-25 15:28:25 +00:00
val allRange = "1-${currentList.size}"
2021-08-14 17:31:27 +00:00
_rangeOptions.postValue(listOf(allRange))
selectedRangeInt.postValue(0)
selectedRange.postValue(allRange)
2021-07-25 20:50:16 +00:00
}
2021-11-02 15:09:29 +00:00
_publicEpisodes.postValue(Resource.Success(currentList))
2021-06-26 22:15:19 +00:00
}
fun changeSeason(selection: Int?) {
filterEpisodes(_episodes.value, selection, null)
2021-07-25 20:50:16 +00:00
}
fun changeRange(range: Int?) {
filterEpisodes(_episodes.value, null, range)
2021-06-26 22:15:19 +00:00
}
fun changeDubStatus(status: DubStatus?) {
if (status == null) return
2021-11-02 15:09:29 +00:00
dubSubEpisodes.value?.get(status)?.let { episodes ->
id.value?.let {
2022-01-08 23:59:50 +00:00
setDub(it, status)
}
2022-02-06 15:43:29 +00:00
_dubStatus.postValue(status!!)
updateEpisodes(null, episodes, null)
2021-11-02 15:09:29 +00:00
}
}
2022-01-07 19:27:25 +00:00
suspend fun loadEpisode(
episode: ResultEpisode,
isCasting: Boolean,
clearCache: Boolean = false
): Resource<Pair<Set<ExtractorLink>, Set<SubtitleData>>> {
return safeApiCall {
2022-01-08 23:59:50 +00:00
val index = _episodes.value?.indexOf(episode) ?: episode.index
2022-01-07 19:27:25 +00:00
val currentLinks = mutableSetOf<ExtractorLink>()
val currentSubs = mutableSetOf<SubtitleData>()
generator?.goto(index)
generator?.generateLinks(clearCache, isCasting, {
it.first?.let { link ->
currentLinks.add(link)
}
}, { sub ->
currentSubs.add(sub)
})
return@safeApiCall Pair(
currentLinks.toSet(),
currentSubs.toSet()
)
2022-01-07 19:27:25 +00:00
}
}
2022-01-08 23:59:50 +00:00
fun getGenerator(episode: ResultEpisode): IGenerator? {
val index = _episodes.value?.indexOf(episode) ?: episode.index
generator?.goto(index)
2022-01-07 19:27:25 +00:00
return generator
}
private fun updateEpisodes(localId: Int?, list: List<ResultEpisode>, selection: Int?) {
2021-06-26 22:15:19 +00:00
_episodes.postValue(list)
2022-01-07 19:27:25 +00:00
generator = RepoLinkGenerator(list)
2021-08-25 15:28:25 +00:00
val set = HashMap<Int, Int>()
val range = selectedRangeInt.value
2021-08-25 15:28:25 +00:00
list.withIndex().forEach { set[it.value.id] = it.index }
episodeById.postValue(set)
2021-07-02 18:46:18 +00:00
filterEpisodes(
2021-06-26 22:15:19 +00:00
list,
if (selection == -1) getResultSeason(localId ?: id.value ?: return) else selection,
range
2021-07-02 18:46:18 +00:00
)
2021-06-26 22:15:19 +00:00
}
fun reloadEpisodes() {
2021-06-15 16:07:20 +00:00
val current = _episodes.value ?: return
val copy = current.map {
val posDur = getViewPos(it.id)
2021-06-15 16:07:20 +00:00
it.copy(position = posDur?.position ?: 0, duration = posDur?.duration ?: 0)
}
updateEpisodes(null, copy, selectedSeason.value)
2021-06-15 16:07:20 +00:00
}
2021-09-19 22:36:32 +00:00
private fun filterName(name: String?): String? {
if (name == null) return null
Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let {
2021-09-19 22:36:32 +00:00
if (it.isEmpty())
return null
}
return name
}
fun load(url: String, apiName: String, showFillers: Boolean) = viewModelScope.launch {
2021-06-16 16:54:07 +00:00
_resultResponse.postValue(Resource.Loading(url))
2021-09-19 22:36:32 +00:00
_publicEpisodes.postValue(Resource.Loading())
2021-06-16 16:54:07 +00:00
2021-05-22 22:25:56 +00:00
_apiName.postValue(apiName)
2022-03-20 18:02:52 +00:00
val api = getApiFromNameNull(apiName) ?: getApiFromUrlNull(url)
if (api == null) {
2021-12-27 21:56:47 +00:00
_resultResponse.postValue(
Resource.Failure(
false,
null,
null,
"This provider does not exist"
)
)
return@launch
}
2021-07-29 00:19:42 +00:00
repo = APIRepository(api)
2022-01-07 19:27:25 +00:00
val data = repo?.load(url) ?: return@launch
2021-06-16 16:54:07 +00:00
2021-05-18 13:43:32 +00:00
_resultResponse.postValue(data)
2021-05-22 22:25:56 +00:00
when (data) {
is Resource.Success -> {
2022-04-03 01:13:02 +00:00
val d = applyMeta(data.value, lastMeta)
2021-07-30 14:09:57 +00:00
page.postValue(d)
val mainId = d.getId()
id.postValue(mainId)
loadWatchStatus(mainId)
2021-07-30 14:09:57 +00:00
setKey(
2021-08-25 15:28:25 +00:00
DOWNLOAD_HEADER_CACHE,
mainId.toString(),
VideoDownloadHelper.DownloadHeaderCached(
apiName,
url,
d.type,
d.name,
d.posterUrl,
mainId,
System.currentTimeMillis(),
)
)
2021-07-30 14:09:57 +00:00
when (d) {
is AnimeLoadResponse -> {
if (d.episodes.isEmpty()) {
_dubSubEpisodes.postValue(emptyMap())
return@launch
}
2021-06-15 23:25:58 +00:00
val status = getDub(mainId)
val statuses = d.episodes.map { it.key }
val dubStatus = if (statuses.contains(status)) status else statuses.first()
2021-07-30 14:09:57 +00:00
2021-12-27 21:56:47 +00:00
val fillerEpisodes =
if (showFillers) safeApiCall { getFillerEpisodes(d.name) } else null
2021-09-19 22:36:32 +00:00
2021-11-02 15:27:47 +00:00
var idIndex = 0
2021-11-02 15:09:29 +00:00
val res = d.episodes.map { ep ->
2021-05-22 22:25:56 +00:00
val episodes = ArrayList<ResultEpisode>()
2021-11-02 15:09:29 +00:00
for ((index, i) in ep.value.withIndex()) {
2021-10-03 00:09:13 +00:00
val episode = i.episode ?: (index + 1)
episodes.add(buildResultEpisode(
2022-01-07 19:27:25 +00:00
d.name,
filterName(i.name),
i.posterUrl,
episode,
null, // TODO FIX SEASON
i.url,
apiName,
mainId + index + 1 + idIndex * 100000,
index,
i.rating,
i.description,
if (fillerEpisodes is Resource.Success) fillerEpisodes.value?.let {
it.contains(episode) && it[episode] == true
} ?: false else false,
2022-01-07 19:27:25 +00:00
d.type,
mainId
))
2021-05-22 22:25:56 +00:00
}
2021-11-02 15:27:47 +00:00
idIndex++
2022-03-12 14:34:44 +00:00
episodes.sortBy { it.episode }
2021-11-02 15:09:29 +00:00
Pair(ep.key, episodes)
}.toMap()
// These posts needs to be in this order as to make the preferDub in ResultFragment work
2021-11-02 15:09:29 +00:00
_dubSubEpisodes.postValue(res)
res[dubStatus]?.let { episodes ->
updateEpisodes(mainId, episodes, -1)
2021-05-22 22:25:56 +00:00
}
_dubStatus.postValue(dubStatus)
_dubSubSelections.postValue(d.episodes.keys)
2021-07-30 14:09:57 +00:00
}
is TvSeriesLoadResponse -> {
val episodes = ArrayList<ResultEpisode>()
for ((index, i) in d.episodes.withIndex()) {
episodes.add(
buildResultEpisode(
2022-01-07 19:27:25 +00:00
d.name,
filterName(i.name),
2021-07-30 14:09:57 +00:00
i.posterUrl,
i.episode ?: (index + 1),
i.season,
i.data,
apiName,
(mainId + index + 1).hashCode(),
index,
i.rating,
2021-12-13 18:41:33 +00:00
i.description,
2021-09-19 22:36:32 +00:00
null,
2022-01-07 19:27:25 +00:00
d.type,
mainId
2021-07-30 14:09:57 +00:00
)
2021-07-02 18:46:18 +00:00
)
2021-05-22 22:25:56 +00:00
}
2022-03-12 14:34:44 +00:00
episodes.sortBy { (it.season?.times(10000) ?: 0) + it.episode }
updateEpisodes(mainId, episodes, -1)
2021-07-30 14:09:57 +00:00
}
is MovieLoadResponse -> {
buildResultEpisode(
2022-01-07 19:27:25 +00:00
d.name,
d.name,
null,
0,
null,
d.dataUrl,
d.apiName,
(mainId), // HAS SAME ID
0,
null,
null,
null,
2022-01-07 19:27:25 +00:00
d.type,
mainId
).let {
updateEpisodes(mainId, listOf(it), -1)
}
2021-05-22 22:25:56 +00:00
}
2021-08-30 17:11:04 +00:00
is TorrentLoadResponse -> {
updateEpisodes(
mainId, listOf(
buildResultEpisode(
2022-01-07 19:27:25 +00:00
d.name,
2021-08-30 17:11:04 +00:00
d.name,
null,
0,
null,
d.torrent ?: d.magnet ?: "",
d.apiName,
(mainId), // HAS SAME ID
0,
null,
null,
2021-09-19 22:36:32 +00:00
null,
2022-01-07 19:27:25 +00:00
d.type,
mainId
2021-08-30 17:11:04 +00:00
)
), -1
)
}
2021-05-22 22:25:56 +00:00
}
}
2022-01-07 19:27:25 +00:00
else -> Unit
2021-05-22 22:25:56 +00:00
}
2021-05-18 13:43:32 +00:00
}
2021-05-20 15:22:28 +00:00
2021-05-23 12:10:39 +00:00
private var _apiName: MutableLiveData<String> = MutableLiveData()
2021-06-14 16:58:43 +00:00
val apiName: LiveData<String> get() = _apiName
2021-05-16 18:28:00 +00:00
}