diff --git a/app/build.gradle b/app/build.gradle index fc3665e1..f1fbb206 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,7 +36,7 @@ android { targetSdkVersion 30 versionCode 46 - versionName "2.9.22" + versionName "2.9.23" resValue "string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}" diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 83797ec2..a3426106 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -141,10 +141,13 @@ object APIHolder { return null } - fun LoadResponse.getId(): Int { + fun getLoadResponseIdFromUrl(url : String, apiName: String) : Int { return url.replace(getApiFromName(apiName).mainUrl, "").replace("/", "").hashCode() } + fun LoadResponse.getId(): Int { + return getLoadResponseIdFromUrl(url,apiName) + } /** * Gets the website captcha token diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 4b5913e9..a3568d35 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -56,6 +56,7 @@ import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.removeKey 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.InAppUpdater.Companion.runAutoUpdate import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState @@ -680,6 +681,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { logError(e) } println("Loaded everything") + + ioSafe { + migrateResumeWatching() + } /* val relativePath = (Environment.DIRECTORY_DOWNLOADS) + File.separatorChar val displayName = "output.dex" //""output.dex" diff --git a/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt b/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt index 2009ace8..badb6631 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt @@ -34,6 +34,10 @@ fun List.apmap(f: suspend (A) -> B): List = runBlocking { map { async { f(it) } }.map { it.await() } } +fun List.apmapIndexed(f: suspend (index: Int, A) -> B): List = runBlocking { + mapIndexed { index, a -> async { f(index, a) } }.map { it.await() } +} + // run code in parallel /*fun argpmap( vararg transforms: () -> R, diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt index 2c293741..145e91d5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt @@ -222,9 +222,9 @@ open class SflixProvider : MainAPI() { var seasonItems = seasonsDocument.select("div.dropdown-menu.dropdown-menu-model > a") if (seasonItems.isNullOrEmpty()) seasonItems = seasonsDocument.select("div.dropdown-menu > a.dropdown-item") - seasonItems.forEachIndexed { season, element -> + seasonItems.apmapIndexed { season, element -> val seasonId = element.attr("data-id") - if (seasonId.isNullOrBlank()) return@forEachIndexed + if (seasonId.isNullOrBlank()) return@apmapIndexed var episode = 0 val seasonEpisodes = app.get("$mainUrl/ajax/v2/season/episodes/$seasonId").document 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 fb832ea8..3029256d 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 @@ -187,6 +187,33 @@ fun ResultEpisode.getWatchProgress(): Float { class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegionsListener { 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( url: String, apiName: String, @@ -194,11 +221,11 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio startValue: Int = 0 ): Bundle { return Bundle().apply { - putString("url", url) - putString("apiName", apiName) - putInt("startAction", startAction) - putInt("startValue", startValue) - putBoolean("restart", true) + putString(URL_BUNDLE, url) + putString(API_NAME_BUNDLE, apiName) + putInt(START_ACTION_BUNDLE, startAction) + putInt(START_VALUE_BUNDLE, startValue) + putBoolean(RESTART_BUNDLE, true) } } @@ -682,9 +709,9 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio updateUIListener = ::updateUI - val restart = arguments?.getBoolean("restart") ?: false + val restart = arguments?.getBoolean(RESTART_BUNDLE) ?: false if (restart) { - arguments?.putBoolean("restart", false) + arguments?.putBoolean(RESTART_BUNDLE, false) } activity?.window?.decorView?.clearFocus() @@ -705,10 +732,12 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio // activity?.fixPaddingStatusbar(result_toolbar) - url = arguments?.getString("url") - val apiName = arguments?.getString("apiName") ?: return - startAction = arguments?.getInt("startAction") ?: START_ACTION_NORMAL - startValue = arguments?.getInt("startValue") ?: START_VALUE_NORMAL + url = arguments?.getString(URL_BUNDLE) + val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return + startAction = arguments?.getInt(START_ACTION_BUNDLE) ?: START_ACTION_NORMAL + startValue = arguments?.getInt(START_VALUE_BUNDLE) + val resumeEpisode = arguments?.getInt(EPISODE_BUNDLE) + val resumeSeason = arguments?.getInt(SEASON_BUNDLE) syncModel.addFromUrl(url) val api = getApiFromName(apiName) @@ -1165,9 +1194,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio .map { watchType -> Pair(watchType.internalId, 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)) } } @@ -1475,6 +1502,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio ?.let { result_play_movie?.text = it } + println("startAction = $startAction") when (startAction) { START_ACTION_RESUME_LATEST -> { @@ -1488,13 +1516,28 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio } } START_ACTION_LOAD_EP -> { - for (ep in episodeList) { - 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)) - break + if(episodeList.size == 1) { + handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, episodeList.first())) + } else { + var found = false + for (ep in episodeList) { + 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()}") + handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, ep)) + break + } + } } + } else -> Unit } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt index 1211962d..5f71c0a8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt @@ -19,7 +19,7 @@ object SearchHelper { activity.loadSearchResult(card) } SEARCH_ACTION_PLAY_FILE -> { - if (card is DataStoreHelper.ResumeWatchingResult && card.id != null) { + if (card is DataStoreHelper.ResumeWatchingResult) { if (card.isFromDownload) { handleDownloadClick( activity, card.name, DownloadClickEvent( @@ -38,7 +38,7 @@ object SearchHelper { ) ) } else { - activity.loadSearchResult(card, START_ACTION_LOAD_EP, card.id!!) + activity.loadSearchResult(card, START_ACTION_LOAD_EP, card.id) } } else { handleSearchClickCallback( diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index 881d8fb5..52bdb270 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -298,9 +298,16 @@ object AppUtils { fun Activity?.loadSearchResult( card: SearchResponse, 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?) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt index 1d5488db..f1744f6e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -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_DATA = "result_watch_state_data" 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_DUB = "result_dub" @@ -65,7 +67,7 @@ object DataStoreHelper { @JsonProperty("posterHeaders") override var posterHeaders: Map? = null, ) : SearchResponse - var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION + private var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION fun getAllWatchStateIds(): List? { val folder = "$currentAccount/$RESULT_WATCH_STATE" @@ -81,14 +83,41 @@ object DataStoreHelper { } } + private fun getAllResumeStateIdsOld(): List? { + 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( parentId: Int?, episodeId: Int?, episode: Int?, season: Int?, - isFromDownload: Boolean = false + isFromDownload: Boolean = false, + updateTime: Long? = null, ) { - if (parentId == null || episodeId == null) return + if (parentId == null) return setKey( "$currentAccount/$RESULT_RESUME_WATCHING", parentId.toString(), @@ -97,12 +126,17 @@ object DataStoreHelper { episodeId, episode, season, - System.currentTimeMillis(), + updateTime ?: System.currentTimeMillis(), isFromDownload ) ) } + fun removeLastWatchedOld(parentId: Int?) { + if (parentId == null) return + removeKey("$currentAccount/$RESULT_RESUME_WATCHING_OLD", parentId.toString()) + } + fun removeLastWatched(parentId: Int?) { if (parentId == null) return 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) { if (id == null) return setKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString(), data) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadHelper.kt index 0a41aa4f..a76cc115 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadHelper.kt @@ -29,7 +29,7 @@ object VideoDownloadHelper { data class ResumeWatching( @JsonProperty("parentId") val parentId: Int, - @JsonProperty("episodeId") val episodeId: Int, + @JsonProperty("episodeId") val episodeId: Int?, @JsonProperty("episode") val episode: Int?, @JsonProperty("season") val season: Int?, @JsonProperty("updateTime") val updateTime: Long,