More library fixes

This commit is contained in:
Blatzar 2023-01-24 20:27:22 +01:00
parent c4afb5e673
commit daaab9d35b
11 changed files with 93 additions and 32 deletions

View File

@ -13,6 +13,10 @@ enum class SyncIdName {
}
interface SyncAPI : OAuth2API {
/**
* Set this to true if the user updates something on the list like watch status or score
**/
var requireLibraryRefresh: Boolean
val mainUrl: String
/**

View File

@ -12,6 +12,11 @@ class SyncRepo(private val repo: SyncAPI) {
val mainUrl = repo.mainUrl
val requiresLogin = repo.requiresLogin
val syncIdName = repo.syncIdName
var requireLibraryRefresh: Boolean
get() = repo.requireLibraryRefresh
set(value) {
repo.requireLibraryRefresh = value
}
suspend fun score(id: String, status: SyncAPI.SyncStatus): Resource<Boolean> {
return safeApiCall { repo.score(id, status) }

View File

@ -28,6 +28,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
override val key = "6871"
override val redirectUrl = "anilistlogin"
override val idPrefix = "anilist"
override var requireLibraryRefresh = true
override var mainUrl = "https://anilist.co"
override val icon = R.drawable.ic_anilist_icon
override val requiresLogin = false
@ -47,6 +48,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
}
override fun logOut() {
requireLibraryRefresh = true
removeAccountKeys()
}
@ -68,6 +70,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
setKey(accountId, ANILIST_TOKEN_KEY, token)
setKey(ANILIST_SHOULD_UPDATE_LIST, true)
val user = getUser()
requireLibraryRefresh = true
return user != null
}
@ -173,7 +176,9 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
fromIntToAnimeStatus(status.status),
status.score,
status.watchedEpisodes
)
).also {
requireLibraryRefresh = requireLibraryRefresh || it
}
}
companion object {
@ -295,15 +300,13 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
val shows = searchShows(name.replace(blackListRegex, ""))
shows?.data?.Page?.media?.find {
malId ?: "NONE" == it.idMal.toString()
(malId ?: "NONE") == it.idMal.toString()
}?.let { return it }
val filtered =
shows?.data?.Page?.media?.filter {
(
it.startDate.year ?: year.toString() == year.toString()
|| year == null
)
(((it.startDate.year ?: year.toString()) == year.toString()
|| year == null))
}
filtered?.forEach {
it.title.romaji?.let { romaji ->
@ -529,7 +532,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
app.post(
"https://graphql.anilist.co/",
headers = mapOf(
"Authorization" to "Bearer " + (getAuth() ?: return@suspendSafeApiCall null),
"Authorization" to "Bearer " + (getAuth()
?: return@suspendSafeApiCall null),
if (cache) "Cache-Control" to "max-stale=$maxStale" else "Cache-Control" to "no-cache"
),
cacheTime = 0,
@ -722,9 +726,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
}
}
"""
val text = postApi(query).also {
println("REPONSE $it")
}
val text = postApi(query)
return text?.toKotlinObject()
}

View File

@ -19,6 +19,7 @@ class LocalList : SyncAPI {
override val requiresLogin = false
override val createAccountUrl: Nothing? = null
override val idPrefix = "local"
override var requireLibraryRefresh = true
override fun loginInfo(): AuthAPI.LoginInfo {
return AuthAPI.LoginInfo(

View File

@ -33,14 +33,15 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
override val redirectUrl = "mallogin"
override val idPrefix = "mal"
override var mainUrl = "https://myanimelist.net"
val apiUrl = "https://api.myanimelist.net"
private val apiUrl = "https://api.myanimelist.net"
override val icon = R.drawable.mal_logo
override val requiresLogin = false
override val syncIdName = SyncIdName.MyAnimeList
override var requireLibraryRefresh = true
override val createAccountUrl = "$mainUrl/register.php"
override fun logOut() {
requireLibraryRefresh = true
removeAccountKeys()
}
@ -93,7 +94,9 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
fromIntToAnimeStatus(status.status),
status.score,
status.watchedEpisodes
)
).also {
requireLibraryRefresh = requireLibraryRefresh || it
}
}
data class MalAnime(
@ -311,9 +314,10 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
setKey(accountId, MAL_UNIXTIME_KEY, (token.expires_in + unixTime))
setKey(accountId, MAL_REFRESH_TOKEN_KEY, token.refresh_token)
setKey(accountId, MAL_TOKEN_KEY, token.access_token)
requireLibraryRefresh = true
}
} catch (e: Exception) {
e.printStackTrace()
logError(e)
}
}
@ -332,7 +336,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
).text
storeToken(res)
} catch (e: Exception) {
e.printStackTrace()
logError(e)
}
}
@ -433,7 +437,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
return getKey(MAL_CACHED_LIST) as? Array<Data>
}
suspend fun getMalAnimeListSmart(): Array<Data>? {
private suspend fun getMalAnimeListSmart(): Array<Data>? {
if (getAuth() == null) return null
return if (getKey(MAL_SHOULD_UPDATE_LIST, true) == true) {
val list = getMalAnimeList()

View File

@ -9,6 +9,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.annotation.StringRes
import androidx.appcompat.widget.SearchView
import androidx.core.view.isVisible
import androidx.fragment.app.activityViewModels
import com.google.android.material.tabs.TabLayoutMediator
import com.lagradost.cloudstream3.APIHolder
@ -26,6 +27,7 @@ import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
import com.lagradost.cloudstream3.utils.AppUtils.reduceDragSensitivity
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import kotlinx.android.synthetic.main.fragment_library.*
@ -264,8 +266,11 @@ class LibraryFragment : Fragment() {
}
viewpager?.offscreenPageLimit = 2
viewpager?.reduceDragSensitivity()
observe(libraryViewModel.pages) { pages ->
empty_list_textview?.isVisible = pages.all { it.items.isEmpty() }
(viewpager.adapter as? ViewpagerAdapter)?.pages = pages
// Using notifyItemRangeChanged keeps the animations when sorting
viewpager.adapter?.notifyItemRangeChanged(0, viewpager.adapter?.itemCount ?: 0)

View File

@ -4,6 +4,8 @@ import androidx.annotation.StringRes
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
@ -20,20 +22,29 @@ enum class ListSorting(@StringRes val stringRes: Int) {
AlphabeticalZ(R.string.sort_alphabetical_z),
}
const val LAST_SYNC_API_KEY = "last_sync_api"
class LibraryViewModel : ViewModel() {
private val _pages: MutableLiveData<List<SyncAPI.Page>> = MutableLiveData(emptyList())
private val _pages: MutableLiveData<List<SyncAPI.Page>> = MutableLiveData(null)
val pages: LiveData<List<SyncAPI.Page>> = _pages
private val _currentApiName: MutableLiveData<String> = MutableLiveData("")
val currentApiName: LiveData<String> = _currentApiName
private val availableSyncApis = SyncApis.filter { it.hasAccount() }
private val availableSyncApis
get() = SyncApis.filter { it.hasAccount() }
// TODO REMEMBER SELECTION
var currentSyncApi = availableSyncApis.firstOrNull()
private set
var currentSyncApi = availableSyncApis.let { allApis ->
val lastSelection = getKey<String>(LAST_SYNC_API_KEY)
availableSyncApis.firstOrNull { it.name == lastSelection } ?: allApis.firstOrNull()
}
private set(value) {
field = value
setKey(LAST_SYNC_API_KEY, field?.name)
}
val availableApiNames: List<String> = availableSyncApis.map { it.name }
val availableApiNames: List<String>
get() = availableSyncApis.map { it.name }
val sortingMethods = listOf(
ListSorting.RatingHigh,
@ -64,15 +75,19 @@ class LibraryViewModel : ViewModel() {
fun reloadPages(forceReload: Boolean) {
// Only skip loading if its not forced and pages is not empty
if (!forceReload && pages.value?.isNotEmpty() == true) return
if (!forceReload && pages.value?.isNotEmpty() == true &&
currentSyncApi?.requireLibraryRefresh != true
) return
ioSafe {
currentSyncApi?.let { repo ->
_currentApiName.postValue(repo.name)
val library = (repo.getPersonalLibrary() as? Resource.Success)?.value ?: return@let
repo.requireLibraryRefresh = false
val listSubset = library.allLibraryItems.groupBy { it.listName }
val allLists = library.allListNames.associateWith { emptyList<SyncAPI.LibraryItem>() }
val allLists =
library.allListNames.associateWith { emptyList<SyncAPI.LibraryItem>() }
val filledLists = allLists + listSubset

View File

@ -33,6 +33,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.tvprovider.media.tv.*
import androidx.tvprovider.media.tv.WatchNextProgram.fromCursor
import androidx.viewpager2.widget.ViewPager2
import com.fasterxml.jackson.module.kotlin.readValue
import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.cast.framework.CastState
@ -166,6 +167,18 @@ object AppUtils {
return builder.build()
}
// https://stackoverflow.com/a/67441735/13746422
fun ViewPager2.reduceDragSensitivity(f: Int = 4) {
val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
recyclerViewField.isAccessible = true
val recyclerView = recyclerViewField.get(this) as RecyclerView
val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop")
touchSlopField.isAccessible = true
val touchSlop = touchSlopField.get(recyclerView) as Int
touchSlopField.set(recyclerView, touchSlop * f) // "8" was obtained experimentally
}
@SuppressLint("RestrictedApi")
fun getAllWatchNextPrograms(context: Context): Set<Long> {
val COLUMN_WATCH_NEXT_ID_INDEX = 0

View File

@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.SearchQuality
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.result.VideoWatchState
@ -195,6 +196,7 @@ object DataStoreHelper {
fun setBookmarkedData(id: Int?, data: BookmarkedData) {
if (id == null) return
setKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString(), data)
AccountManager.localListApi.requireLibraryRefresh = true
}
fun getBookmarkedData(id: Int?): BookmarkedData? {

View File

@ -7,6 +7,17 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/empty_list_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="30dp"
android:gravity="center"
android:text="@string/empty_library_message"
android:visibility="gone"
tools:visibility="visible" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/search_bar"
android:layout_width="match_parent"
@ -16,13 +27,14 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:orientation="horizontal"
app:layout_scrollFlags="scroll|enterAlways">
<ImageView
android:id="@+id/provider_selector"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="end|center_vertical"
android:layout_marginStart="10dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/change_providers_img_des"
@ -62,15 +74,14 @@
<ImageView
android:id="@+id/list_selector"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_gravity="end|center_vertical"
android:layout_margin="10dp"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/change_providers_img_des"
android:nextFocusLeft="@id/main_search"
android:nextFocusRight="@id/main_search"
android:padding="10dp"
android:src="@drawable/ic_baseline_filter_list_24"
app:tint="?attr/textColor" />
@ -123,10 +134,8 @@
android:background="?attr/primaryGrayBackground"
android:descendantFocusability="blocksDescendants"
android:focusable="false"
android:paddingHorizontal="5dp"
app:layout_scrollFlags="noScroll"
app:tabBackground="?attr/primaryGrayBackground"
app:tabGravity="center"
app:tabIndicator="@drawable/indicator_background"
app:tabIndicatorColor="@color/textColor"

View File

@ -627,4 +627,5 @@
<string name="sort_alphabetical_z">Alphabetical (Z to A)</string>
<string name="select_library">Select Library</string>
<string name="open_with">Open with</string>
<string name="empty_library_message">Looks like your library is empty :(\nLogin to a library account or add shows to your local library</string>
</resources>