forked from recloudstream/cloudstream
		
	: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,9 +1194,7 @@ 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)) |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -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,13 +1516,28 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 START_ACTION_LOAD_EP -> { |                 START_ACTION_LOAD_EP -> { | ||||||
|                     for (ep in episodeList) { |                     if(episodeList.size == 1) { | ||||||
|                         if (ep.id == startValue) { // watched too much |                         handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, episodeList.first())) | ||||||
|                             println("WATCH STATUS::: START_ACTION_LOAD_EP S${ep.season} E ${ep.episode} - ${ep.getWatchProgress()}") |                     } else { | ||||||
|                             handleAction(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, ep)) |                         var found = false | ||||||
|                             break |                         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 |                 else -> Unit | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue