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…
Reference in a new issue