forked from recloudstream/cloudstream
		
	autosync
This commit is contained in:
		
							parent
							
								
									8c5db9de11
								
							
						
					
					
						commit
						f85d32a308
					
				
					 10 changed files with 142 additions and 34 deletions
				
			
		|  | @ -12,7 +12,6 @@ import com.fasterxml.jackson.databind.json.JsonMapper | ||||||
| import com.fasterxml.jackson.module.kotlin.KotlinModule | import com.fasterxml.jackson.module.kotlin.KotlinModule | ||||||
| import com.lagradost.cloudstream3.animeproviders.* | import com.lagradost.cloudstream3.animeproviders.* | ||||||
| import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider | import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider | ||||||
| import com.lagradost.cloudstream3.metaproviders.MultiAnimeProvider |  | ||||||
| import com.lagradost.cloudstream3.movieproviders.* | import com.lagradost.cloudstream3.movieproviders.* | ||||||
| import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi | import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi | ||||||
| import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.malApi | import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.malApi | ||||||
|  | @ -310,7 +309,7 @@ abstract class MainAPI { | ||||||
|         var overrideData: HashMap<String, ProvidersInfoJson>? = null |         var overrideData: HashMap<String, ProvidersInfoJson>? = null | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public fun overrideWithNewData(data: ProvidersInfoJson) { |     fun overrideWithNewData(data: ProvidersInfoJson) { | ||||||
|         this.name = data.name |         this.name = data.name | ||||||
|         this.mainUrl = data.url |         this.mainUrl = data.url | ||||||
|     } |     } | ||||||
|  | @ -760,6 +759,10 @@ interface LoadResponse { | ||||||
|             // TODO add kitsu sync |             // TODO add kitsu sync | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         fun LoadResponse.addTMDbId(id: String?) { | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         fun LoadResponse.setDuration(input: String?) { |         fun LoadResponse.setDuration(input: String?) { | ||||||
|             val cleanInput = input?.trim()?.replace(" ", "") ?: return |             val cleanInput = input?.trim()?.replace(" ", "") ?: return | ||||||
|             Regex("([0-9]*)h.*?([0-9]*)m").find(cleanInput)?.groupValues?.let { values -> |             Regex("([0-9]*)h.*?([0-9]*)m").find(cleanInput)?.groupValues?.let { values -> | ||||||
|  | @ -985,7 +988,7 @@ fun fetchUrls(text: String?): List<String> { | ||||||
|         return listOf() |         return listOf() | ||||||
|     } |     } | ||||||
|     val linkRegex = |     val linkRegex = | ||||||
|         Regex("""(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/\/=]*))""") |         Regex("""(https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*))""") | ||||||
|     return linkRegex.findAll(text).map { it.value.trim().removeSurrounding("\"") }.toList() |     return linkRegex.findAll(text).map { it.value.trim().removeSurrounding("\"") }.toList() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,6 +26,10 @@ fun <T, R> Iterable<T>.pmap( | ||||||
|     return ArrayList<R>(destination) |     return ArrayList<R>(destination) | ||||||
| }*/ | }*/ | ||||||
| 
 | 
 | ||||||
|  | fun <K, V, R> Map<out K, V>.apmap(f: suspend (Map.Entry<K, V>) -> R): List<R> = runBlocking { | ||||||
|  |     map { async { f(it) } }.map { it.await() } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| fun <A, B> List<A>.apmap(f: suspend (A) -> B): List<B> = runBlocking { | 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() } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -56,7 +56,10 @@ const val SKIP_OP_VIDEO_PERCENTAGE = 50 | ||||||
| const val PRELOAD_NEXT_EPISODE_PERCENTAGE = 80 | const val PRELOAD_NEXT_EPISODE_PERCENTAGE = 80 | ||||||
| 
 | 
 | ||||||
| // when the player should mark the episode as watched and resume watching the next | // when the player should mark the episode as watched and resume watching the next | ||||||
| const val NEXT_WATCH_EPISODE_PERCENTAGE = 95 | const val NEXT_WATCH_EPISODE_PERCENTAGE = 90 | ||||||
|  | 
 | ||||||
|  | // when the player should sync the progress of "watched", TODO MAKE SETTING | ||||||
|  | const val UPDATE_SYNC_PROGRESS_PERCENTAGE = 80 | ||||||
| 
 | 
 | ||||||
| abstract class AbstractPlayerFragment( | abstract class AbstractPlayerFragment( | ||||||
|     val player: IPlayer = CS3IPlayer() |     val player: IPlayer = CS3IPlayer() | ||||||
|  | @ -329,6 +332,7 @@ abstract class AbstractPlayerFragment( | ||||||
|                 SKIP_OP_VIDEO_PERCENTAGE, |                 SKIP_OP_VIDEO_PERCENTAGE, | ||||||
|                 PRELOAD_NEXT_EPISODE_PERCENTAGE, |                 PRELOAD_NEXT_EPISODE_PERCENTAGE, | ||||||
|                 NEXT_WATCH_EPISODE_PERCENTAGE, |                 NEXT_WATCH_EPISODE_PERCENTAGE, | ||||||
|  |                 UPDATE_SYNC_PROGRESS_PERCENTAGE, | ||||||
|             ), subtitlesUpdates = ::subtitlesChanged |             ), subtitlesUpdates = ::subtitlesChanged | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ package com.lagradost.cloudstream3.ui.player | ||||||
| import android.annotation.SuppressLint | import android.annotation.SuppressLint | ||||||
| import android.content.Intent | import android.content.Intent | ||||||
| import android.os.Bundle | import android.os.Bundle | ||||||
|  | import android.util.Log | ||||||
| import android.view.LayoutInflater | import android.view.LayoutInflater | ||||||
| import android.view.View | import android.view.View | ||||||
| import android.view.ViewGroup | import android.view.ViewGroup | ||||||
|  | @ -12,6 +13,7 @@ import androidx.appcompat.app.AlertDialog | ||||||
| import androidx.core.view.isGone | import androidx.core.view.isGone | ||||||
| import androidx.core.view.isVisible | import androidx.core.view.isVisible | ||||||
| import androidx.lifecycle.ViewModelProvider | import androidx.lifecycle.ViewModelProvider | ||||||
|  | import androidx.preference.PreferenceManager | ||||||
| import com.google.android.exoplayer2.util.MimeTypes | import com.google.android.exoplayer2.util.MimeTypes | ||||||
| import com.google.android.material.button.MaterialButton | import com.google.android.material.button.MaterialButton | ||||||
| import com.hippo.unifile.UniFile | import com.hippo.unifile.UniFile | ||||||
|  | @ -25,6 +27,7 @@ import com.lagradost.cloudstream3.mvvm.observe | ||||||
| import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType | import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType | ||||||
| import com.lagradost.cloudstream3.ui.result.ResultEpisode | import com.lagradost.cloudstream3.ui.result.ResultEpisode | ||||||
| import com.lagradost.cloudstream3.ui.result.ResultFragment | import com.lagradost.cloudstream3.ui.result.ResultFragment | ||||||
|  | import com.lagradost.cloudstream3.ui.result.SyncViewModel | ||||||
| import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings | import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings | ||||||
| import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment | import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment | ||||||
| import com.lagradost.cloudstream3.utils.* | import com.lagradost.cloudstream3.utils.* | ||||||
|  | @ -39,13 +42,18 @@ import kotlinx.coroutines.Job | ||||||
| class GeneratorPlayer : FullScreenPlayer() { | class GeneratorPlayer : FullScreenPlayer() { | ||||||
|     companion object { |     companion object { | ||||||
|         private var lastUsedGenerator: IGenerator? = null |         private var lastUsedGenerator: IGenerator? = null | ||||||
|         fun newInstance(generator: IGenerator): Bundle { |         fun newInstance(generator: IGenerator, syncData: HashMap<String, String>? = null): Bundle { | ||||||
|  |             Log.i(TAG, "newInstance = $syncData") | ||||||
|             lastUsedGenerator = generator |             lastUsedGenerator = generator | ||||||
|             return Bundle() |             return Bundle().apply { | ||||||
|  |                 if (syncData != null) | ||||||
|  |                     putSerializable("syncData", syncData) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private lateinit var viewModel: PlayerGeneratorViewModel //by activityViewModels() |     private lateinit var viewModel: PlayerGeneratorViewModel //by activityViewModels() | ||||||
|  |     private lateinit var sync: SyncViewModel | ||||||
|     private var currentLinks: Set<Pair<ExtractorLink?, ExtractorUri?>> = setOf() |     private var currentLinks: Set<Pair<ExtractorLink?, ExtractorUri?>> = setOf() | ||||||
|     private var currentSubs: Set<SubtitleData> = setOf() |     private var currentSubs: Set<SubtitleData> = setOf() | ||||||
| 
 | 
 | ||||||
|  | @ -123,7 +131,11 @@ class GeneratorPlayer : FullScreenPlayer() { | ||||||
|                     if (isNextEpisode) 0L else getPos() |                     if (isNextEpisode) 0L else getPos() | ||||||
|                 }, |                 }, | ||||||
|                 currentSubs, |                 currentSubs, | ||||||
|                 (if(sameEpisode) currentSelectedSubtitles else null) ?: getAutoSelectSubtitle(currentSubs, settings = true, downloads = true), |                 (if (sameEpisode) currentSelectedSubtitles else null) ?: getAutoSelectSubtitle( | ||||||
|  |                     currentSubs, | ||||||
|  |                     settings = true, | ||||||
|  |                     downloads = true | ||||||
|  |                 ), | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -422,6 +434,18 @@ class GeneratorPlayer : FullScreenPlayer() { | ||||||
|         var isOpVisible = false |         var isOpVisible = false | ||||||
|         when (val meta = currentMeta) { |         when (val meta = currentMeta) { | ||||||
|             is ResultEpisode -> { |             is ResultEpisode -> { | ||||||
|  |                 if (percentage >= UPDATE_SYNC_PROGRESS_PERCENTAGE) { | ||||||
|  |                     context?.let { ctx -> | ||||||
|  |                         val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx) | ||||||
|  |                         if (settingsManager.getBoolean( | ||||||
|  |                                 ctx.getString(R.string.episode_sync_enabled_key), | ||||||
|  |                                 true | ||||||
|  |                             ) | ||||||
|  |                         ) | ||||||
|  |                             sync.modifyMaxEpisode(meta.episode) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 if (meta.tvType.isAnimeOp()) |                 if (meta.tvType.isAnimeOp()) | ||||||
|                     isOpVisible = percentage < SKIP_OP_VIDEO_PERCENTAGE |                     isOpVisible = percentage < SKIP_OP_VIDEO_PERCENTAGE | ||||||
|             } |             } | ||||||
|  | @ -429,7 +453,7 @@ class GeneratorPlayer : FullScreenPlayer() { | ||||||
|         player_skip_op?.isVisible = isOpVisible |         player_skip_op?.isVisible = isOpVisible | ||||||
|         player_skip_episode?.isVisible = !isOpVisible && viewModel.hasNextEpisode() == true |         player_skip_episode?.isVisible = !isOpVisible && viewModel.hasNextEpisode() == true | ||||||
| 
 | 
 | ||||||
|         if (percentage > PRELOAD_NEXT_EPISODE_PERCENTAGE) { |         if (percentage >= PRELOAD_NEXT_EPISODE_PERCENTAGE) { | ||||||
|             viewModel.preLoadNextLinks() |             viewModel.preLoadNextLinks() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -554,6 +578,13 @@ class GeneratorPlayer : FullScreenPlayer() { | ||||||
|         setPlayerDimen(widthHeight) |         setPlayerDimen(widthHeight) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private fun unwrapBundle(savedInstanceState: Bundle?) { | ||||||
|  |         Log.i(TAG, "unwrapBundle = $savedInstanceState") | ||||||
|  |         savedInstanceState?.let { bundle -> | ||||||
|  |             sync.addSyncs(bundle.getSerializable("syncData") as? HashMap<String, String>?) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     override fun onCreateView( |     override fun onCreateView( | ||||||
|         inflater: LayoutInflater, |         inflater: LayoutInflater, | ||||||
|         container: ViewGroup?, |         container: ViewGroup?, | ||||||
|  | @ -564,13 +595,23 @@ class GeneratorPlayer : FullScreenPlayer() { | ||||||
|             if (context?.isTvSettings() == true) R.layout.fragment_player_tv else R.layout.fragment_player |             if (context?.isTvSettings() == true) R.layout.fragment_player_tv else R.layout.fragment_player | ||||||
| 
 | 
 | ||||||
|         viewModel = ViewModelProvider(this)[PlayerGeneratorViewModel::class.java] |         viewModel = ViewModelProvider(this)[PlayerGeneratorViewModel::class.java] | ||||||
|  |         sync = ViewModelProvider(this)[SyncViewModel::class.java] | ||||||
|  | 
 | ||||||
|         viewModel.attachGenerator(lastUsedGenerator) |         viewModel.attachGenerator(lastUsedGenerator) | ||||||
|  |         unwrapBundle(savedInstanceState) | ||||||
|  |         unwrapBundle(arguments) | ||||||
|  | 
 | ||||||
|         return super.onCreateView(inflater, container, savedInstanceState) |         return super.onCreateView(inflater, container, savedInstanceState) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||||
|         super.onViewCreated(view, savedInstanceState) |         super.onViewCreated(view, savedInstanceState) | ||||||
| 
 | 
 | ||||||
|  |         unwrapBundle(savedInstanceState) | ||||||
|  |         unwrapBundle(arguments) | ||||||
|  | 
 | ||||||
|  |         sync.updateUserData() | ||||||
|  | 
 | ||||||
|         preferredAutoSelectSubtitles = SubtitlesFragment.getAutoSelectLanguageISO639_1() |         preferredAutoSelectSubtitles = SubtitlesFragment.getAutoSelectLanguageISO639_1() | ||||||
| 
 | 
 | ||||||
|         if (currentSelectedLink == null) { |         if (currentSelectedLink == null) { | ||||||
|  |  | ||||||
|  | @ -12,8 +12,8 @@ interface IGenerator { | ||||||
|     fun prev() |     fun prev() | ||||||
|     fun goto(index: Int) |     fun goto(index: Int) | ||||||
| 
 | 
 | ||||||
|     fun getCurrentId(): Int?   // this is used to save data or read data about this id |     fun getCurrentId(): Int?                    // this is used to save data or read data about this id | ||||||
|     fun getCurrent(offset : Int = 0): Any?     // this is used to get metadata about the current playing, can return null |     fun getCurrent(offset : Int = 0): Any?      // this is used to get metadata about the current playing, can return null | ||||||
| 
 | 
 | ||||||
|     /* not safe, must use try catch */ |     /* not safe, must use try catch */ | ||||||
|     suspend fun generateLinks( |     suspend fun generateLinks( | ||||||
|  |  | ||||||
|  | @ -428,7 +428,8 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio | ||||||
|     private var currentHeaderName: String? = null |     private var currentHeaderName: String? = null | ||||||
|     private var currentType: TvType? = null |     private var currentType: TvType? = null | ||||||
|     private var currentEpisodes: List<ResultEpisode>? = null |     private var currentEpisodes: List<ResultEpisode>? = null | ||||||
|     var downloadButton: EasyDownloadButton? = null |     private var downloadButton: EasyDownloadButton? = null | ||||||
|  |     private var syncdata: Map<String, String>? = null | ||||||
| 
 | 
 | ||||||
|     override fun onCreateView( |     override fun onCreateView( | ||||||
|         inflater: LayoutInflater, |         inflater: LayoutInflater, | ||||||
|  | @ -645,6 +646,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun updateUI() { |     private fun updateUI() { | ||||||
|  |         syncModel.updateUserData() | ||||||
|         viewModel.reloadEpisodes() |         viewModel.reloadEpisodes() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1089,10 +1091,11 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio | ||||||
|                 ACTION_PLAY_EPISODE_IN_PLAYER -> { |                 ACTION_PLAY_EPISODE_IN_PLAYER -> { | ||||||
|                     viewModel.getGenerator(episodeClick.data) |                     viewModel.getGenerator(episodeClick.data) | ||||||
|                         ?.let { generator -> |                         ?.let { generator -> | ||||||
|  |                             println("LANUCJ:::: $syncdata") | ||||||
|                             activity?.navigate( |                             activity?.navigate( | ||||||
|                                 R.id.global_to_navigation_player, |                                 R.id.global_to_navigation_player, | ||||||
|                                 GeneratorPlayer.newInstance( |                                 GeneratorPlayer.newInstance( | ||||||
|                                     generator |                                     generator, syncdata?.let { HashMap(it) } | ||||||
|                                 ) |                                 ) | ||||||
|                             ) |                             ) | ||||||
|                         } |                         } | ||||||
|  | @ -1296,10 +1299,16 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio | ||||||
|                 list.filter { it.isSynced && it.hasAccount }.joinToString { it.name } |                 list.filter { it.isSynced && it.hasAccount }.joinToString { it.name } | ||||||
| 
 | 
 | ||||||
|             val newList = list.filter { it.isSynced } |             val newList = list.filter { it.isSynced } | ||||||
|  | 
 | ||||||
|             result_mini_sync?.isVisible = newList.isNotEmpty() |             result_mini_sync?.isVisible = newList.isNotEmpty() | ||||||
|             (result_mini_sync?.adapter as? ImageAdapter?)?.updateList(newList.map { it.icon }) |             (result_mini_sync?.adapter as? ImageAdapter?)?.updateList(newList.map { it.icon }) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         observe(syncModel.syncIds) { | ||||||
|  |             println("VALUES::: $it") | ||||||
|  |             syncdata = it | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         var currentSyncProgress = 0 |         var currentSyncProgress = 0 | ||||||
| 
 | 
 | ||||||
|         fun setSyncMaxEpisodes(totalEpisodes: Int?) { |         fun setSyncMaxEpisodes(totalEpisodes: Int?) { | ||||||
|  | @ -1642,12 +1651,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio | ||||||
|                     setActors(d.actors) |                     setActors(d.actors) | ||||||
| 
 | 
 | ||||||
|                     if (SettingsFragment.accountEnabled) { |                     if (SettingsFragment.accountEnabled) { | ||||||
|                         var isValid = false |                         if (syncModel.addSyncs(d.syncData)) { | ||||||
|                         for ((prefix, id) in d.syncData) { |  | ||||||
|                             isValid = isValid || syncModel.addSync(prefix, id) |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         if (isValid) { |  | ||||||
|                             syncModel.updateMetaAndUser() |                             syncModel.updateMetaAndUser() | ||||||
|                             syncModel.updateSynced() |                             syncModel.updateSynced() | ||||||
|                         } else { |                         } else { | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import androidx.lifecycle.LiveData | ||||||
| import androidx.lifecycle.MutableLiveData | import androidx.lifecycle.MutableLiveData | ||||||
| import androidx.lifecycle.ViewModel | import androidx.lifecycle.ViewModel | ||||||
| import androidx.lifecycle.viewModelScope | import androidx.lifecycle.viewModelScope | ||||||
|  | import com.lagradost.cloudstream3.apmap | ||||||
| import com.lagradost.cloudstream3.mvvm.Resource | import com.lagradost.cloudstream3.mvvm.Resource | ||||||
| import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.SyncApis | import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.SyncApis | ||||||
| import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi | import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi | ||||||
|  | @ -40,7 +41,10 @@ class SyncViewModel : ViewModel() { | ||||||
|     val userData: LiveData<Resource<SyncAPI.SyncStatus>?> get() = _userDataResponse |     val userData: LiveData<Resource<SyncAPI.SyncStatus>?> get() = _userDataResponse | ||||||
| 
 | 
 | ||||||
|     // prefix, id |     // prefix, id | ||||||
|     private val syncIds = hashMapOf<String, String>() |     private var syncs = mutableMapOf<String, String>() | ||||||
|  |     private val _syncIds: MutableLiveData<MutableMap<String, String>> = | ||||||
|  |         MutableLiveData(mutableMapOf()) | ||||||
|  |     val syncIds: LiveData<MutableMap<String, String>> get() = _syncIds | ||||||
| 
 | 
 | ||||||
|     private val _currentSynced: MutableLiveData<List<CurrentSynced>> = |     private val _currentSynced: MutableLiveData<List<CurrentSynced>> = | ||||||
|         MutableLiveData(getMissing()) |         MutableLiveData(getMissing()) | ||||||
|  | @ -53,7 +57,7 @@ class SyncViewModel : ViewModel() { | ||||||
|             CurrentSynced( |             CurrentSynced( | ||||||
|                 it.name, |                 it.name, | ||||||
|                 it.idPrefix, |                 it.idPrefix, | ||||||
|                 syncIds.containsKey(it.idPrefix), |                 syncs.containsKey(it.idPrefix), | ||||||
|                 it.hasAccount(), |                 it.hasAccount(), | ||||||
|                 it.icon, |                 it.icon, | ||||||
|             ) |             ) | ||||||
|  | @ -65,19 +69,30 @@ class SyncViewModel : ViewModel() { | ||||||
|         _currentSynced.postValue(getMissing()) |         _currentSynced.postValue(getMissing()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun addSync(idPrefix: String, id : String) : Boolean { |     private fun addSync(idPrefix: String, id: String): Boolean { | ||||||
|         if(syncIds[idPrefix] == id) return false |         if (syncs[idPrefix] == id) return false | ||||||
|         Log.i(TAG, "addSync $idPrefix = $id") |         Log.i(TAG, "addSync $idPrefix = $id") | ||||||
|         syncIds[idPrefix] = id | 
 | ||||||
|  |         syncs[idPrefix] = id | ||||||
|  |         _syncIds.postValue(syncs) | ||||||
|         return true |         return true | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun setMalId(id: String?) : Boolean { |     fun addSyncs(map: Map<String, String>?): Boolean { | ||||||
|         return addSync(malApi.idPrefix,id ?: return false) |         var isValid = false | ||||||
|  | 
 | ||||||
|  |         map?.forEach { (prefix, id) -> | ||||||
|  |             isValid = isValid || addSync(prefix, id) | ||||||
|  |         } | ||||||
|  |         return isValid | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun setAniListId(id: String?) : Boolean { |     fun setMalId(id: String?): Boolean { | ||||||
|         return addSync(aniListApi.idPrefix,id ?: return false) |         return addSync(malApi.idPrefix, id ?: return false) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun setAniListId(id: String?): Boolean { | ||||||
|  |         return addSync(aniListApi.idPrefix, id ?: return false) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     var hasAddedFromUrl: HashSet<String> = hashSetOf() |     var hasAddedFromUrl: HashSet<String> = hashSetOf() | ||||||
|  | @ -153,20 +168,45 @@ class SyncViewModel : ViewModel() { | ||||||
|         Log.i(TAG, "publishUserData") |         Log.i(TAG, "publishUserData") | ||||||
|         val user = userData.value |         val user = userData.value | ||||||
|         if (user is Resource.Success) { |         if (user is Resource.Success) { | ||||||
|             for ((prefix, id) in syncIds) { |             syncs.forEach { (prefix, id) -> | ||||||
|                 repos.firstOrNull { it.idPrefix == prefix }?.score(id, user.value) |                 repos.firstOrNull { it.idPrefix == prefix }?.score(id, user.value) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         updateUserData() |         updateUserData() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun updateUserData() = viewModelScope.launch { |     fun modifyMaxEpisode(episodeNum: Int) { | ||||||
|  |         Log.i(TAG, "modifyMaxEpisode = $episodeNum") | ||||||
|  |         modifyData { status -> | ||||||
|  |             status.copy(watchedEpisodes = maxOf(episodeNum, status.watchedEpisodes ?: return@modifyData null)) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// modifies the current sync data, return null if you don't want to change it | ||||||
|  |     private fun modifyData(update: ((SyncAPI.SyncStatus) -> (SyncAPI.SyncStatus?))) = | ||||||
|  |         viewModelScope.launch { | ||||||
|  |             syncs.apmap { (prefix, id) -> | ||||||
|  |                 repos.firstOrNull { it.idPrefix == prefix }?.let { repo -> | ||||||
|  |                     if (repo.hasAccount()) { | ||||||
|  |                         val result = repo.getStatus(id) | ||||||
|  |                         if (result is Resource.Success) { | ||||||
|  |                             update(result.value)?.let { newData -> | ||||||
|  |                                 Log.i(TAG, "modifyData ${repo.name} => $newData") | ||||||
|  |                                 repo.score(id, newData) | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     fun updateUserData() = viewModelScope.launch { | ||||||
|         Log.i(TAG, "updateUserData") |         Log.i(TAG, "updateUserData") | ||||||
|         _userDataResponse.postValue(Resource.Loading()) |         _userDataResponse.postValue(Resource.Loading()) | ||||||
|         var lastError: Resource<SyncAPI.SyncStatus> = Resource.Failure(false, null, null, "No data") |         var lastError: Resource<SyncAPI.SyncStatus> = Resource.Failure(false, null, null, "No data") | ||||||
|         for ((prefix, id) in syncIds) { |         syncs.forEach { (prefix, id) -> | ||||||
|             repos.firstOrNull { it.idPrefix == prefix }?.let { repo -> |             repos.firstOrNull { it.idPrefix == prefix }?.let { repo -> | ||||||
|                 if(repo.hasAccount()) { |                 if (repo.hasAccount()) { | ||||||
|                     val result = repo.getStatus(id) |                     val result = repo.getStatus(id) | ||||||
|                     if (result is Resource.Success) { |                     if (result is Resource.Success) { | ||||||
|                         _userDataResponse.postValue(result) |                         _userDataResponse.postValue(result) | ||||||
|  | @ -185,9 +225,9 @@ class SyncViewModel : ViewModel() { | ||||||
| 
 | 
 | ||||||
|         _metaResponse.postValue(Resource.Loading()) |         _metaResponse.postValue(Resource.Loading()) | ||||||
|         var lastError: Resource<SyncAPI.SyncResult> = Resource.Failure(false, null, null, "No data") |         var lastError: Resource<SyncAPI.SyncResult> = Resource.Failure(false, null, null, "No data") | ||||||
|         for ((prefix, id) in syncIds) { |         syncs.forEach { (prefix, id) -> | ||||||
|             repos.firstOrNull { it.idPrefix == prefix }?.let { repo -> |             repos.firstOrNull { it.idPrefix == prefix }?.let { repo -> | ||||||
|                 if(repo.hasAccount()) { |                 if (repo.hasAccount()) { | ||||||
|                     val result = repo.getResult(id) |                     val result = repo.getResult(id) | ||||||
|                     if (result is Resource.Success) { |                     if (result is Resource.Success) { | ||||||
|                         _metaResponse.postValue(result) |                         _metaResponse.postValue(result) | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
|     android:height="24dp" |     android:height="24dp" | ||||||
|     android:viewportWidth="24" |     android:viewportWidth="24" | ||||||
|     android:viewportHeight="24" |     android:viewportHeight="24" | ||||||
|     android:tint="?attr/colorControlNormal"> |     android:tint="?attr/white"> | ||||||
|   <path |   <path | ||||||
|       android:fillColor="@android:color/white" |       android:fillColor="@android:color/white" | ||||||
|       android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z"/> |       android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z"/> | ||||||
|  |  | ||||||
|  | @ -39,6 +39,7 @@ | ||||||
|     <string name="backup_key" translatable="false">backup_key</string> |     <string name="backup_key" translatable="false">backup_key</string> | ||||||
|     <string name="prefer_media_type_key" translatable="false">prefer_media_type_key</string> |     <string name="prefer_media_type_key" translatable="false">prefer_media_type_key</string> | ||||||
|     <string name="app_theme_key" translatable="false">app_theme_key</string> |     <string name="app_theme_key" translatable="false">app_theme_key</string> | ||||||
|  |     <string name="episode_sync_enabled_key" translatable="false">episode_sync_enabled_key</string> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG --> |     <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG --> | ||||||
|  | @ -202,6 +203,9 @@ | ||||||
|         overlay |         overlay | ||||||
|     </string> |     </string> | ||||||
| 
 | 
 | ||||||
|  |     <string name="episode_sync_settings">Update watch progress</string> | ||||||
|  |     <string name="episode_sync_settings_des">Automatically sync your current episode progress</string> | ||||||
|  | 
 | ||||||
|     <string name="restore_settings">Restore data from backup</string> |     <string name="restore_settings">Restore data from backup</string> | ||||||
|     <string name="killswitch_settings">Download latest metadata from github</string> |     <string name="killswitch_settings">Download latest metadata from github</string> | ||||||
|     <string name="killswitch_settings_des">If you want access to all providers (even broken ones) turn this off</string> |     <string name="killswitch_settings_des">If you want access to all providers (even broken ones) turn this off</string> | ||||||
|  |  | ||||||
|  | @ -63,6 +63,14 @@ | ||||||
|                 android:title="@string/double_tap_to_pause_settings" |                 android:title="@string/double_tap_to_pause_settings" | ||||||
|                 android:summary="@string/double_tap_to_pause_settings_des" |                 android:summary="@string/double_tap_to_pause_settings_des" | ||||||
|                 app:defaultValue="false" /> |                 app:defaultValue="false" /> | ||||||
|  | 
 | ||||||
|  |         <SwitchPreference | ||||||
|  |                 android:icon="@drawable/baseline_sync_24" | ||||||
|  |                 app:key="@string/episode_sync_enabled_key" | ||||||
|  |                 android:title="@string/episode_sync_settings" | ||||||
|  |                 android:summary="@string/episode_sync_settings_des" | ||||||
|  |                 app:defaultValue="true" /> | ||||||
|  | 
 | ||||||
|         <Preference |         <Preference | ||||||
|                 android:key="@string/video_buffer_disk_key" |                 android:key="@string/video_buffer_disk_key" | ||||||
|                 android:title="@string/video_buffer_disk_settings" |                 android:title="@string/video_buffer_disk_settings" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue