mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
:okand: restored lost resume watching
This commit is contained in:
parent
109e1b9f17
commit
16b2924375
10 changed files with 136 additions and 32 deletions
|
@ -36,7 +36,7 @@ android {
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
|
|
||||||
versionCode 46
|
versionCode 46
|
||||||
versionName "2.9.22"
|
versionName "2.9.23"
|
||||||
|
|
||||||
resValue "string", "app_version",
|
resValue "string", "app_version",
|
||||||
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
|
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
|
||||||
|
|
|
@ -141,10 +141,13 @@ object APIHolder {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun LoadResponse.getId(): Int {
|
fun getLoadResponseIdFromUrl(url : String, apiName: String) : Int {
|
||||||
return url.replace(getApiFromName(apiName).mainUrl, "").replace("/", "").hashCode()
|
return url.replace(getApiFromName(apiName).mainUrl, "").replace("/", "").hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun LoadResponse.getId(): Int {
|
||||||
|
return getLoadResponseIdFromUrl(url,apiName)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the website captcha token
|
* Gets the website captcha token
|
||||||
|
|
|
@ -56,6 +56,7 @@ import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.removeKey
|
import com.lagradost.cloudstream3.utils.DataStore.removeKey
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos
|
||||||
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
|
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState
|
import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState
|
||||||
|
@ -680,6 +681,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
logError(e)
|
logError(e)
|
||||||
}
|
}
|
||||||
println("Loaded everything")
|
println("Loaded everything")
|
||||||
|
|
||||||
|
ioSafe {
|
||||||
|
migrateResumeWatching()
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
val relativePath = (Environment.DIRECTORY_DOWNLOADS) + File.separatorChar
|
val relativePath = (Environment.DIRECTORY_DOWNLOADS) + File.separatorChar
|
||||||
val displayName = "output.dex" //""output.dex"
|
val displayName = "output.dex" //""output.dex"
|
||||||
|
|
|
@ -34,6 +34,10 @@ fun <A, B> List<A>.apmap(f: suspend (A) -> B): List<B> = runBlocking {
|
||||||
map { async { f(it) } }.map { it.await() }
|
map { async { f(it) } }.map { it.await() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <A, B> List<A>.apmapIndexed(f: suspend (index: Int, A) -> B): List<B> = runBlocking {
|
||||||
|
mapIndexed { index, a -> async { f(index, a) } }.map { it.await() }
|
||||||
|
}
|
||||||
|
|
||||||
// run code in parallel
|
// run code in parallel
|
||||||
/*fun <R> argpmap(
|
/*fun <R> argpmap(
|
||||||
vararg transforms: () -> R,
|
vararg transforms: () -> R,
|
||||||
|
|
|
@ -222,9 +222,9 @@ open class SflixProvider : MainAPI() {
|
||||||
var seasonItems = seasonsDocument.select("div.dropdown-menu.dropdown-menu-model > a")
|
var seasonItems = seasonsDocument.select("div.dropdown-menu.dropdown-menu-model > a")
|
||||||
if (seasonItems.isNullOrEmpty())
|
if (seasonItems.isNullOrEmpty())
|
||||||
seasonItems = seasonsDocument.select("div.dropdown-menu > a.dropdown-item")
|
seasonItems = seasonsDocument.select("div.dropdown-menu > a.dropdown-item")
|
||||||
seasonItems.forEachIndexed { season, element ->
|
seasonItems.apmapIndexed { season, element ->
|
||||||
val seasonId = element.attr("data-id")
|
val seasonId = element.attr("data-id")
|
||||||
if (seasonId.isNullOrBlank()) return@forEachIndexed
|
if (seasonId.isNullOrBlank()) return@apmapIndexed
|
||||||
|
|
||||||
var episode = 0
|
var episode = 0
|
||||||
val seasonEpisodes = app.get("$mainUrl/ajax/v2/season/episodes/$seasonId").document
|
val seasonEpisodes = app.get("$mainUrl/ajax/v2/season/episodes/$seasonId").document
|
||||||
|
|
|
@ -187,6 +187,33 @@ fun ResultEpisode.getWatchProgress(): Float {
|
||||||
|
|
||||||
class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegionsListener {
|
class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegionsListener {
|
||||||
companion object {
|
companion object {
|
||||||
|
const val URL_BUNDLE = "url"
|
||||||
|
const val API_NAME_BUNDLE = "apiName"
|
||||||
|
const val SEASON_BUNDLE = "season"
|
||||||
|
const val EPISODE_BUNDLE = "episode"
|
||||||
|
const val START_ACTION_BUNDLE = "startAction"
|
||||||
|
const val START_VALUE_BUNDLE = "startValue"
|
||||||
|
const val RESTART_BUNDLE = "restart"
|
||||||
|
fun newInstance(
|
||||||
|
card: SearchResponse, startAction: Int = 0, startValue: Int? = null
|
||||||
|
): Bundle {
|
||||||
|
return Bundle().apply {
|
||||||
|
putString(URL_BUNDLE, card.url)
|
||||||
|
putString(API_NAME_BUNDLE, card.apiName)
|
||||||
|
if (card is DataStoreHelper.ResumeWatchingResult) {
|
||||||
|
println("CARD::::: $card")
|
||||||
|
if (card.season != null)
|
||||||
|
putInt(SEASON_BUNDLE, card.season)
|
||||||
|
if (card.episode != null)
|
||||||
|
putInt(EPISODE_BUNDLE, card.episode)
|
||||||
|
}
|
||||||
|
putInt(START_ACTION_BUNDLE, startAction)
|
||||||
|
if (startValue != null)
|
||||||
|
putInt(START_VALUE_BUNDLE, startValue)
|
||||||
|
putBoolean(RESTART_BUNDLE, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
url: String,
|
url: String,
|
||||||
apiName: String,
|
apiName: String,
|
||||||
|
@ -194,11 +221,11 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
startValue: Int = 0
|
startValue: Int = 0
|
||||||
): Bundle {
|
): Bundle {
|
||||||
return Bundle().apply {
|
return Bundle().apply {
|
||||||
putString("url", url)
|
putString(URL_BUNDLE, url)
|
||||||
putString("apiName", apiName)
|
putString(API_NAME_BUNDLE, apiName)
|
||||||
putInt("startAction", startAction)
|
putInt(START_ACTION_BUNDLE, startAction)
|
||||||
putInt("startValue", startValue)
|
putInt(START_VALUE_BUNDLE, startValue)
|
||||||
putBoolean("restart", true)
|
putBoolean(RESTART_BUNDLE, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,9 +709,9 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
|
|
||||||
updateUIListener = ::updateUI
|
updateUIListener = ::updateUI
|
||||||
|
|
||||||
val restart = arguments?.getBoolean("restart") ?: false
|
val restart = arguments?.getBoolean(RESTART_BUNDLE) ?: false
|
||||||
if (restart) {
|
if (restart) {
|
||||||
arguments?.putBoolean("restart", false)
|
arguments?.putBoolean(RESTART_BUNDLE, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
activity?.window?.decorView?.clearFocus()
|
activity?.window?.decorView?.clearFocus()
|
||||||
|
@ -705,10 +732,12 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
|
|
||||||
// activity?.fixPaddingStatusbar(result_toolbar)
|
// activity?.fixPaddingStatusbar(result_toolbar)
|
||||||
|
|
||||||
url = arguments?.getString("url")
|
url = arguments?.getString(URL_BUNDLE)
|
||||||
val apiName = arguments?.getString("apiName") ?: return
|
val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return
|
||||||
startAction = arguments?.getInt("startAction") ?: START_ACTION_NORMAL
|
startAction = arguments?.getInt(START_ACTION_BUNDLE) ?: START_ACTION_NORMAL
|
||||||
startValue = arguments?.getInt("startValue") ?: START_VALUE_NORMAL
|
startValue = arguments?.getInt(START_VALUE_BUNDLE)
|
||||||
|
val resumeEpisode = arguments?.getInt(EPISODE_BUNDLE)
|
||||||
|
val resumeSeason = arguments?.getInt(SEASON_BUNDLE)
|
||||||
syncModel.addFromUrl(url)
|
syncModel.addFromUrl(url)
|
||||||
|
|
||||||
val api = getApiFromName(apiName)
|
val api = getApiFromName(apiName)
|
||||||
|
@ -1165,11 +1194,9 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
.map { watchType -> Pair(watchType.internalId, watchType.stringRes) },
|
.map { watchType -> Pair(watchType.internalId, watchType.stringRes) },
|
||||||
//.map { watchType -> Triple(watchType.internalId, watchType.iconRes, watchType.stringRes) },
|
//.map { watchType -> Triple(watchType.internalId, watchType.iconRes, watchType.stringRes) },
|
||||||
) {
|
) {
|
||||||
context?.let { localContext ->
|
|
||||||
viewModel.updateWatchStatus(WatchType.fromInternalId(this.itemId))
|
viewModel.updateWatchStatus(WatchType.fromInternalId(this.itemId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
observe(viewModel.watchStatus) { watchType ->
|
observe(viewModel.watchStatus) { watchType ->
|
||||||
result_bookmark_button?.text = getString(watchType.stringRes)
|
result_bookmark_button?.text = getString(watchType.stringRes)
|
||||||
|
@ -1475,6 +1502,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
?.let {
|
?.let {
|
||||||
result_play_movie?.text = it
|
result_play_movie?.text = it
|
||||||
}
|
}
|
||||||
|
println("startAction = $startAction")
|
||||||
|
|
||||||
when (startAction) {
|
when (startAction) {
|
||||||
START_ACTION_RESUME_LATEST -> {
|
START_ACTION_RESUME_LATEST -> {
|
||||||
|
@ -1488,14 +1516,29 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
START_ACTION_LOAD_EP -> {
|
START_ACTION_LOAD_EP -> {
|
||||||
|
if(episodeList.size == 1) {
|
||||||
|
handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, episodeList.first()))
|
||||||
|
} else {
|
||||||
|
var found = false
|
||||||
for (ep in episodeList) {
|
for (ep in episodeList) {
|
||||||
if (ep.id == startValue) { // watched too much
|
if (ep.id == startValue) { // watched too much
|
||||||
|
println("WATCH STATUS::: START_ACTION_LOAD_EP S${ep.season} E ${ep.episode} - ${ep.getWatchProgress()}")
|
||||||
|
handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, ep))
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
for (ep in episodeList) {
|
||||||
|
if (ep.episode == resumeEpisode && ep.season == resumeSeason) {
|
||||||
println("WATCH STATUS::: START_ACTION_LOAD_EP S${ep.season} E ${ep.episode} - ${ep.getWatchProgress()}")
|
println("WATCH STATUS::: START_ACTION_LOAD_EP S${ep.season} E ${ep.episode} - ${ep.getWatchProgress()}")
|
||||||
handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, ep))
|
handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, ep))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
arguments?.remove("startValue")
|
arguments?.remove("startValue")
|
||||||
|
|
|
@ -19,7 +19,7 @@ object SearchHelper {
|
||||||
activity.loadSearchResult(card)
|
activity.loadSearchResult(card)
|
||||||
}
|
}
|
||||||
SEARCH_ACTION_PLAY_FILE -> {
|
SEARCH_ACTION_PLAY_FILE -> {
|
||||||
if (card is DataStoreHelper.ResumeWatchingResult && card.id != null) {
|
if (card is DataStoreHelper.ResumeWatchingResult) {
|
||||||
if (card.isFromDownload) {
|
if (card.isFromDownload) {
|
||||||
handleDownloadClick(
|
handleDownloadClick(
|
||||||
activity, card.name, DownloadClickEvent(
|
activity, card.name, DownloadClickEvent(
|
||||||
|
@ -38,7 +38,7 @@ object SearchHelper {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
activity.loadSearchResult(card, START_ACTION_LOAD_EP, card.id!!)
|
activity.loadSearchResult(card, START_ACTION_LOAD_EP, card.id)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handleSearchClickCallback(
|
handleSearchClickCallback(
|
||||||
|
|
|
@ -298,9 +298,16 @@ object AppUtils {
|
||||||
fun Activity?.loadSearchResult(
|
fun Activity?.loadSearchResult(
|
||||||
card: SearchResponse,
|
card: SearchResponse,
|
||||||
startAction: Int = 0,
|
startAction: Int = 0,
|
||||||
startValue: Int = 0
|
startValue: Int? = null,
|
||||||
) {
|
) {
|
||||||
(this as? AppCompatActivity?)?.loadResult(card.url, card.apiName, startAction, startValue)
|
this?.runOnUiThread {
|
||||||
|
// viewModelStore.clear()
|
||||||
|
this.navigate(
|
||||||
|
R.id.global_to_navigation_results,
|
||||||
|
ResultFragment.newInstance(card, startAction, startValue)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
//(this as? AppCompatActivity?)?.loadResult(card.url, card.apiName, startAction, startValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Activity.requestLocalAudioFocus(focusRequest: AudioFocusRequest?) {
|
fun Activity.requestLocalAudioFocus(focusRequest: AudioFocusRequest?) {
|
||||||
|
|
|
@ -15,6 +15,8 @@ const val VIDEO_POS_DUR = "video_pos_dur"
|
||||||
const val RESULT_WATCH_STATE = "result_watch_state"
|
const val RESULT_WATCH_STATE = "result_watch_state"
|
||||||
const val RESULT_WATCH_STATE_DATA = "result_watch_state_data"
|
const val RESULT_WATCH_STATE_DATA = "result_watch_state_data"
|
||||||
const val RESULT_RESUME_WATCHING = "result_resume_watching_2" // changed due to id changes
|
const val RESULT_RESUME_WATCHING = "result_resume_watching_2" // changed due to id changes
|
||||||
|
const val RESULT_RESUME_WATCHING_OLD = "result_resume_watching"
|
||||||
|
const val RESULT_RESUME_WATCHING_HAS_MIGRATED = "result_resume_watching_migrated"
|
||||||
const val RESULT_SEASON = "result_season"
|
const val RESULT_SEASON = "result_season"
|
||||||
const val RESULT_DUB = "result_dub"
|
const val RESULT_DUB = "result_dub"
|
||||||
|
|
||||||
|
@ -65,7 +67,7 @@ object DataStoreHelper {
|
||||||
@JsonProperty("posterHeaders") override var posterHeaders: Map<String, String>? = null,
|
@JsonProperty("posterHeaders") override var posterHeaders: Map<String, String>? = null,
|
||||||
) : SearchResponse
|
) : SearchResponse
|
||||||
|
|
||||||
var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION
|
private var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION
|
||||||
|
|
||||||
fun getAllWatchStateIds(): List<Int>? {
|
fun getAllWatchStateIds(): List<Int>? {
|
||||||
val folder = "$currentAccount/$RESULT_WATCH_STATE"
|
val folder = "$currentAccount/$RESULT_WATCH_STATE"
|
||||||
|
@ -81,14 +83,41 @@ object DataStoreHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getAllResumeStateIdsOld(): List<Int>? {
|
||||||
|
val folder = "$currentAccount/$RESULT_RESUME_WATCHING_OLD"
|
||||||
|
return getKeys(folder)?.mapNotNull {
|
||||||
|
it.removePrefix("$folder/").toIntOrNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun migrateResumeWatching() {
|
||||||
|
// if (getKey(RESULT_RESUME_WATCHING_HAS_MIGRATED, false) != true) {
|
||||||
|
setKey(RESULT_RESUME_WATCHING_HAS_MIGRATED, true)
|
||||||
|
getAllResumeStateIdsOld()?.forEach { id ->
|
||||||
|
getLastWatchedOld(id)?.let {
|
||||||
|
setLastWatched(
|
||||||
|
it.parentId,
|
||||||
|
null,
|
||||||
|
it.episode,
|
||||||
|
it.season,
|
||||||
|
it.isFromDownload,
|
||||||
|
it.updateTime
|
||||||
|
)
|
||||||
|
removeLastWatchedOld(it.parentId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
fun setLastWatched(
|
fun setLastWatched(
|
||||||
parentId: Int?,
|
parentId: Int?,
|
||||||
episodeId: Int?,
|
episodeId: Int?,
|
||||||
episode: Int?,
|
episode: Int?,
|
||||||
season: Int?,
|
season: Int?,
|
||||||
isFromDownload: Boolean = false
|
isFromDownload: Boolean = false,
|
||||||
|
updateTime: Long? = null,
|
||||||
) {
|
) {
|
||||||
if (parentId == null || episodeId == null) return
|
if (parentId == null) return
|
||||||
setKey(
|
setKey(
|
||||||
"$currentAccount/$RESULT_RESUME_WATCHING",
|
"$currentAccount/$RESULT_RESUME_WATCHING",
|
||||||
parentId.toString(),
|
parentId.toString(),
|
||||||
|
@ -97,12 +126,17 @@ object DataStoreHelper {
|
||||||
episodeId,
|
episodeId,
|
||||||
episode,
|
episode,
|
||||||
season,
|
season,
|
||||||
System.currentTimeMillis(),
|
updateTime ?: System.currentTimeMillis(),
|
||||||
isFromDownload
|
isFromDownload
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun removeLastWatchedOld(parentId: Int?) {
|
||||||
|
if (parentId == null) return
|
||||||
|
removeKey("$currentAccount/$RESULT_RESUME_WATCHING_OLD", parentId.toString())
|
||||||
|
}
|
||||||
|
|
||||||
fun removeLastWatched(parentId: Int?) {
|
fun removeLastWatched(parentId: Int?) {
|
||||||
if (parentId == null) return
|
if (parentId == null) return
|
||||||
removeKey("$currentAccount/$RESULT_RESUME_WATCHING", parentId.toString())
|
removeKey("$currentAccount/$RESULT_RESUME_WATCHING", parentId.toString())
|
||||||
|
@ -116,6 +150,14 @@ object DataStoreHelper {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getLastWatchedOld(id: Int?): VideoDownloadHelper.ResumeWatching? {
|
||||||
|
if (id == null) return null
|
||||||
|
return getKey(
|
||||||
|
"$currentAccount/$RESULT_RESUME_WATCHING_OLD",
|
||||||
|
id.toString(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun setBookmarkedData(id: Int?, data: BookmarkedData) {
|
fun setBookmarkedData(id: Int?, data: BookmarkedData) {
|
||||||
if (id == null) return
|
if (id == null) return
|
||||||
setKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString(), data)
|
setKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString(), data)
|
||||||
|
|
|
@ -29,7 +29,7 @@ object VideoDownloadHelper {
|
||||||
|
|
||||||
data class ResumeWatching(
|
data class ResumeWatching(
|
||||||
@JsonProperty("parentId") val parentId: Int,
|
@JsonProperty("parentId") val parentId: Int,
|
||||||
@JsonProperty("episodeId") val episodeId: Int,
|
@JsonProperty("episodeId") val episodeId: Int?,
|
||||||
@JsonProperty("episode") val episode: Int?,
|
@JsonProperty("episode") val episode: Int?,
|
||||||
@JsonProperty("season") val season: Int?,
|
@JsonProperty("season") val season: Int?,
|
||||||
@JsonProperty("updateTime") val updateTime: Long,
|
@JsonProperty("updateTime") val updateTime: Long,
|
||||||
|
|
Loading…
Reference in a new issue