Reworked backend to make list titles use UiText

This commit is contained in:
Blatzar 2023-01-25 19:20:53 +01:00
parent 9bc90438ec
commit 26320bb535
9 changed files with 106 additions and 89 deletions

View file

@ -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<LibraryItem>
val title: UiText, var items: List<LibraryItem>
) {
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<String>,
/** Not necessarily sorted list of all library items, will be grouped by listName */
val allLibraryItems: List<LibraryItem>
val allLibraryLists: List<LibraryList>
)
data class LibraryList(
val name: UiText,
val items: List<LibraryItem>
)
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 */

View file

@ -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 {
return SyncAPI.LibraryMetadata(
emptyList(),
getAniListAnimeListSmart()?.map {
it.entries.mapNotNull { entry ->
entry.toLibraryItem(
entry.status ?: it.status
)
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<SyncAPI.LibraryItem>()
}
}?.flatten() ?: emptyList()
return SyncAPI.LibraryMetadata(
(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
{

View file

@ -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()
}
}
}
return SyncAPI.LibraryMetadata(
WatchType.values().mapNotNull {
val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate {
// None is not something to display
if (it == WatchType.NONE) return@mapNotNull null
// Dirty hack for context!
txt(it.stringRes).asStringNull(AcraApplication.context)
},
list
it.stringRes to emptyList<SyncAPI.LibraryItem>()
}
return SyncAPI.LibraryMetadata(
(baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }
)
}

View file

@ -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<SyncAPI.LibraryItem>()
}
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,

View file

@ -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()
}

View file

@ -92,18 +92,13 @@ class LibraryViewModel : ViewModel() {
repo.requireLibraryRefresh = false
val listSubset = library.allLibraryItems.groupBy { it.listName }
val allLists =
library.allListNames.associateWith { emptyList<SyncAPI.LibraryItem>() }
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))
}
}

View file

@ -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"
}
}
}

View file

@ -55,12 +55,11 @@ object DataStoreHelper {
@JsonProperty("quality") override var quality: SearchQuality? = null,
@JsonProperty("posterHeaders") override var posterHeaders: Map<String, String>? = 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,

View file

@ -13,7 +13,7 @@
<androidx.cardview.widget.CardView
android:id="@+id/background_card"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:layout_marginBottom="2dp"
android:elevation="10dp"