From 26320bb535b6cb4aa2789e3ee40a16f501ead02b Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Wed, 25 Jan 2023 19:20:53 +0100 Subject: [PATCH] Reworked backend to make list titles use UiText --- .../cloudstream3/syncproviders/SyncAPI.kt | 15 ++-- .../syncproviders/providers/AniListApi.kt | 47 ++++++------ .../syncproviders/providers/LocalList.kt | 22 +++--- .../syncproviders/providers/MALApi.kt | 72 +++++++++++-------- .../ui/library/LibraryFragment.kt | 2 +- .../ui/library/LibraryViewModel.kt | 13 ++-- .../cloudstream3/ui/library/PageAdapter.kt | 19 +++-- .../cloudstream3/utils/DataStoreHelper.kt | 3 +- .../layout/search_result_grid_expanded.xml | 2 +- 9 files changed, 106 insertions(+), 89 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt index ab9db4ea..71ea374d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt @@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.syncproviders import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.ui.library.ListSorting +import com.lagradost.cloudstream3.ui.result.UiText import me.xdrop.fuzzywuzzy.FuzzySearch enum class SyncIdName { @@ -101,7 +102,7 @@ interface SyncAPI : OAuth2API { data class Page( - val title: String, var items: List + val title: UiText, var items: List ) { fun sort(method: ListSorting?, query: String? = null) { items = when (method) { @@ -123,11 +124,12 @@ interface SyncAPI : OAuth2API { } data class LibraryMetadata( - /** List of all available pages, useful to show empty pages - * if the user has no entry on that page */ - val allListNames: List, - /** Not necessarily sorted list of all library items, will be grouped by listName */ - val allLibraryItems: List + val allLibraryLists: List + ) + + data class LibraryList( + val name: UiText, + val items: List ) data class LibraryItem( @@ -135,7 +137,6 @@ interface SyncAPI : OAuth2API { override val url: String, /** Unique unchanging string used for data storage */ val syncId: String, - val listName: String, val episodesCompleted: Int?, val episodesTotal: Int?, /** Out of 100 */ diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt index 6b3c408d..ed0c3f78 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt @@ -1,9 +1,9 @@ package com.lagradost.cloudstream3.syncproviders.providers +import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.APIHolder.capitalize import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.setKey @@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncIdName +import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.AppUtils.toJson @@ -316,14 +317,14 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } // Changing names of these will show up in UI - enum class AniListStatusType(var value: Int) { - Watching(0), - Completed(1), - Paused(2), - Dropped(3), - Planning(4), - ReWatching(5), - None(-1) + enum class AniListStatusType(var value: Int, @StringRes val stringRes: Int) { + Watching(0, R.string.type_watching), + Completed(1, R.string.type_completed), + Paused(2, R.string.type_on_hold), + Dropped(3, R.string.type_dropped), + Planning(4, R.string.type_plan_to_watch), + ReWatching(5, R.string.type_re_watching), + None(-1, R.string.none) } fun fromIntToAnimeStatus(inp: Int): AniListStatusType {//= AniListStatusType.values().first { it.value == inp } @@ -339,7 +340,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } - fun convertAnilistStringToStatus(string: String): AniListStatusType { + fun convertAniListStringToStatus(string: String): AniListStatusType { return fromIntToAnimeStatus(aniListStatusString.indexOf(string)) } @@ -609,7 +610,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("private") val private: Boolean, @JsonProperty("media") val media: Media ) { - fun toLibraryItem(listName: String?): SyncAPI.LibraryItem? { + fun toLibraryItem(): SyncAPI.LibraryItem { return SyncAPI.LibraryItem( // English title first this.media.title.english ?: this.media.title.romaji @@ -617,7 +618,6 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { ?: "", "https://anilist.co/anime/${this.media.id}/", this.media.id.toString(), - listName?.lowercase()?.capitalize() ?: return null, this.progress, this.media.episodes, this.score, @@ -665,15 +665,20 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata { + val list = getAniListAnimeListSmart()?.groupBy { + convertAniListStringToStatus(it.status ?: "").stringRes + }?.mapValues { group -> + group.value.map { it.entries.map { entry -> entry.toLibraryItem() } }.flatten() + } ?: emptyMap() + + // To fill empty lists when AniList does not return them + val baseMap = + AniListStatusType.values().filter { it.value >= 0 }.associate { + it.stringRes to emptyList() + } + return SyncAPI.LibraryMetadata( - emptyList(), - getAniListAnimeListSmart()?.map { - it.entries.mapNotNull { entry -> - entry.toLibraryItem( - entry.status ?: it.status - ) - } - }?.flatten() ?: emptyList() + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) } ) } @@ -695,7 +700,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { startedAt { year month day } updatedAt progress - score + score (format: POINT_100) private media { diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt index 7a53e6a4..44384c92 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt @@ -1,7 +1,6 @@ package com.lagradost.cloudstream3.syncproviders.providers import androidx.fragment.app.FragmentActivity -import com.lagradost.cloudstream3.AcraApplication import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI @@ -68,20 +67,21 @@ class LocalList : SyncAPI { }?.distinctBy { it.first } ?: return null val list = ioWork { - watchStatusIds.mapNotNull { - getBookmarkedData(it.first)?.toLibraryItem(it.second) + watchStatusIds.groupBy { + it.second.stringRes + }.mapValues { group -> + group.value.mapNotNull { + getBookmarkedData(it.first)?.toLibraryItem() + } } } + val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate { + // None is not something to display + it.stringRes to emptyList() + } return SyncAPI.LibraryMetadata( - WatchType.values().mapNotNull { - // None is not something to display - if (it == WatchType.NONE) return@mapNotNull null - - // Dirty hack for context! - txt(it.stringRes).asStringNull(AcraApplication.context) - }, - list + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) } ) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt index 7745f0d2..b91c9759 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.syncproviders.providers import android.util.Base64 +import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.AcraApplication.Companion.getKey @@ -15,6 +16,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncIdName +import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject @@ -257,6 +259,32 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { const val MAL_UNIXTIME_KEY: String = "mal_unixtime" // When token expires const val MAL_REFRESH_TOKEN_KEY: String = "mal_refresh_token" // refresh token const val MAL_TOKEN_KEY: String = "mal_token" // anilist token for api + + fun convertToStatus(string: String): MalStatusType { + return fromIntToAnimeStatus(malStatusAsString.indexOf(string)) + } + + enum class MalStatusType(var value: Int, @StringRes val stringRes: Int) { + Watching(0, R.string.type_watching), + Completed(1, R.string.type_completed), + OnHold(2, R.string.type_on_hold), + Dropped(3, R.string.type_dropped), + PlanToWatch(4, R.string.type_plan_to_watch), + None(-1, R.string.type_none) + } + + private fun fromIntToAnimeStatus(inp: Int): MalStatusType {//= AniListStatusType.values().first { it.value == inp } + return when (inp) { + -1 -> MalStatusType.None + 0 -> MalStatusType.Watching + 1 -> MalStatusType.Completed + 2 -> MalStatusType.OnHold + 3 -> MalStatusType.Dropped + 4 -> MalStatusType.PlanToWatch + 5 -> MalStatusType.Watching + else -> MalStatusType.None + } + } } override suspend fun handleRedirect(url: String): Boolean { @@ -394,10 +422,9 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { this.node.title, "https://myanimelist.net/anime/${this.node.id}/", this.node.id.toString(), - this.list_status?.status?.lowercase()?.capitalize()?.replace("_", " ") ?: "NONE", this.list_status?.num_episodes_watched, this.node.num_episodes, - this.list_status?.score, + this.list_status?.score?.times(10), "MAL", TvType.Anime, this.node.main_picture?.large ?: this.node.main_picture?.medium, @@ -448,9 +475,20 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata { + val list = getMalAnimeListSmart()?.groupBy { + convertToStatus(it.list_status?.status ?: "").stringRes + }?.mapValues { group -> + group.value.map { it.toLibraryItem() } + } ?: emptyMap() + + // To fill empty lists when MAL does not return them + val baseMap = + MalStatusType.values().filter { it.value >= 0 }.associate { + it.stringRes to emptyList() + } + return SyncAPI.LibraryMetadata( - emptyList(), - getMalAnimeListSmart()?.map { it.toLibraryItem() } ?: emptyList() + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) } ) } @@ -469,10 +507,6 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return fullList.toTypedArray() } - fun convertToStatus(string: String): MalStatusType { - return fromIntToAnimeStatus(malStatusAsString.indexOf(string)) - } - private suspend fun getMalAnimeListSlice(offset: Int = 0): MalList? { val user = "@me" val auth = getAuth() ?: return null @@ -586,28 +620,6 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return user } - enum class MalStatusType(var value: Int) { - Watching(0), - Completed(1), - OnHold(2), - Dropped(3), - PlanToWatch(4), - None(-1) - } - - private fun fromIntToAnimeStatus(inp: Int): MalStatusType {//= AniListStatusType.values().first { it.value == inp } - return when (inp) { - -1 -> MalStatusType.None - 0 -> MalStatusType.Watching - 1 -> MalStatusType.Completed - 2 -> MalStatusType.OnHold - 3 -> MalStatusType.Dropped - 4 -> MalStatusType.PlanToWatch - 5 -> MalStatusType.Watching - else -> MalStatusType.None - } - } - private suspend fun setScoreRequest( id: Int, status: MalStatusType? = null, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt index 87ab0821..baf8f71e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt @@ -297,7 +297,7 @@ class LibraryFragment : Fragment() { library_tab_layout, viewpager, ) { tab, position -> - tab.text = pages.getOrNull(position)?.title + tab.text = pages.getOrNull(position)?.title?.asStringNull(context) }.attach() loading_indicator?.hide() } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt index 51bfb16f..d01c699a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt @@ -92,18 +92,13 @@ class LibraryViewModel : ViewModel() { repo.requireLibraryRefresh = false - val listSubset = library.allLibraryItems.groupBy { it.listName } - val allLists = - library.allListNames.associateWith { emptyList() } - - val filledLists = allLists + listSubset - - val pages = filledLists.map { + val pages = library.allLibraryLists.map { SyncAPI.Page( - it.key, - it.value + it.name, + it.items ) } + _pages.postValue(Resource.Success(pages)) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt index 0909d845..2435f8be 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt @@ -49,10 +49,10 @@ class PageAdapter( return ColorUtils.calculateLuminance(color) < 0.5 } - fun getDifferentColor(color: Int, ratio : Float = 0.7f) : Int { - return if(isDark(color)) { + fun getDifferentColor(color: Int, ratio: Float = 0.7f): Int { + return if (isDark(color)) { ColorUtils.blendARGB(color, Color.WHITE, ratio) - } else{ + } else { ColorUtils.blendARGB(color, Color.BLACK, ratio) } } @@ -74,7 +74,7 @@ class PageAdapter( itemView, colorCallback = { palette -> AcraApplication.context?.let { ctx -> - val defColor = ContextCompat.getColor(ctx,R.color.ratingColorBg) + val defColor = ContextCompat.getColor(ctx, R.color.ratingColorBg) var bg = palette.getDarkVibrantColor(defColor) if (bg == defColor) { bg = palette.getDarkMutedColor(defColor) @@ -83,11 +83,12 @@ class PageAdapter( bg = palette.getVibrantColor(defColor) } - val fg = getDifferentColor(bg)//palette.getVibrantColor(ContextCompat.getColor(ctx,R.color.ratingColor)) + val fg = + getDifferentColor(bg)//palette.getVibrantColor(ContextCompat.getColor(ctx,R.color.ratingColor)) itemView.text_rating.apply { setTextColor(ColorStateList.valueOf(fg)) } - itemView.text_rating_holder?.backgroundTintList =ColorStateList.valueOf(bg) + itemView.text_rating_holder?.backgroundTintList = ColorStateList.valueOf(bg) itemView.watchProgress?.apply { progressTintList = ColorStateList.valueOf(fg) progressBackgroundTintList = ColorStateList.valueOf(bg) @@ -118,7 +119,11 @@ class PageAdapter( val showRating = (item.personalRating ?: 0) != 0 itemView.text_rating_holder.isVisible = showRating if (showRating) { - itemView.text_rating.text = "★ ${item.personalRating.toString()}" + // We want to show 8.5 but not 8.0 hence the replace + val rating = ((item.personalRating ?: 0).toDouble() / 10).toString() + .replace(".0", "") + + itemView.text_rating.text = "★ $rating" } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt index 074fcfcc..df6f209d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -55,12 +55,11 @@ object DataStoreHelper { @JsonProperty("quality") override var quality: SearchQuality? = null, @JsonProperty("posterHeaders") override var posterHeaders: Map? = null, ) : SearchResponse { - fun toLibraryItem(state: WatchType): SyncAPI.LibraryItem { + fun toLibraryItem(): SyncAPI.LibraryItem { return SyncAPI.LibraryItem( name, url, url, - state.name.lowercase().capitalize(), null, null, null, diff --git a/app/src/main/res/layout/search_result_grid_expanded.xml b/app/src/main/res/layout/search_result_grid_expanded.xml index 850191ba..d6deec2f 100644 --- a/app/src/main/res/layout/search_result_grid_expanded.xml +++ b/app/src/main/res/layout/search_result_grid_expanded.xml @@ -13,7 +13,7 @@