From cc4997e39355325e1e59dc1f375b52d9fd8455da Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Thu, 17 Jul 2025 16:06:26 +0000 Subject: [PATCH] Added new class Score --- .../cloudstream3/syncproviders/SyncApi.kt | 7 +- .../syncproviders/providers/AniListApi.kt | 2 +- .../syncproviders/providers/MALApi.kt | 3 +- .../cloudstream3/ui/result/EpisodeAdapter.kt | 8 +- .../cloudstream3/ui/result/ResultFragment.kt | 5 +- .../ui/result/ResultFragmentPhone.kt | 4 +- .../ui/result/ResultViewModel2.kt | 39 +- .../cloudstream3/ui/search/SearchHelper.kt | 2 +- .../cloudstream3/utils/DataStoreHelper.kt | 160 ++++++-- .../cloudstream3/utils/VideoDownloadHelper.kt | 18 +- app/src/main/res/values-hr/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-zh-rTW/strings.xml | 1 - app/src/main/res/values-zh/strings.xml | 1 - app/src/main/res/values/strings.xml | 2 +- .../com/lagradost/cloudstream3/MainAPI.kt | 376 +++++++++++++++--- .../cloudstream3/metaproviders/MyDramaList.kt | 5 +- .../metaproviders/TmdbProvider.kt | 4 +- .../metaproviders/TraktProvider.kt | 6 +- 19 files changed, 512 insertions(+), 133 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 9d43685c830..478960e53f0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncApi.kt @@ -80,8 +80,7 @@ interface SyncAPI : OAuth2API { var totalEpisodes: Int? = null, var title: String? = null, - /**1-1000*/ - var publicScore: Int? = null, + var publicScore: Score? = null, /**In minutes*/ var duration: Int? = null, var synopsis: String? = null, @@ -154,7 +153,7 @@ interface SyncAPI : OAuth2API { val episodesCompleted: Int?, val episodesTotal: Int?, /** Out of 100 */ - val personalRating: Int?, + val personalRating: Int?, // TODO also update this to Score val lastUpdatedUnixTime: Long?, override val apiName: String, override var type: TvType?, @@ -164,7 +163,7 @@ interface SyncAPI : OAuth2API { val releaseDate: Date?, override var id: Int? = null, val plot : String? = null, - val rating: Int? = null, + val rating: Score? = null, val tags: List? = null ) : SearchResponse } \ No newline at end of file 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 f8e82409522..0b09c5504e1 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 @@ -141,7 +141,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } ) }, - publicScore = season.averageScore?.times(100), + publicScore = Score.from100(season.averageScore), recommendations = season.recommendations?.edges?.mapNotNull { rec -> val recMedia = rec.node.mediaRecommendation SyncAPI.SyncSearchResult( 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 4836eca131d..2cecf862d25 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 @@ -8,6 +8,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.Score import com.lagradost.cloudstream3.ShowStatus import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.app @@ -214,7 +215,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { id = internalId.toString(), totalEpisodes = malAnime.numEpisodes, title = malAnime.title, - publicScore = malAnime.mean?.toFloat()?.times(1000)?.toInt(), + publicScore = Score.from10(malAnime.mean), duration = malAnime.averageEpisodeDuration, synopsis = malAnime.synopsis, airStatus = when (malAnime.status) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt index 1e0438cdd71..565c4240d49 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt @@ -188,7 +188,7 @@ class EpisodeAdapter( season = card.season, id = card.id, parentId = card.parentId, - rating = card.rating, + score = card.score, description = card.description, cacheTime = System.currentTimeMillis(), ), null @@ -230,9 +230,9 @@ class EpisodeAdapter( episodePoster.loadImage(card.poster) - if (card.rating != null) { + if (card.score != null) { episodeRating.text = episodeRating.context?.getString(R.string.rated_format) - ?.format(card.rating.toFloat() / 10f) + ?.format(card.score.toFloat(10)) // TODO Change rated_format to use card.score.toString() } else { episodeRating.text = "" } @@ -347,7 +347,7 @@ class EpisodeAdapter( season = card.season, id = card.id, parentId = card.parentId, - rating = card.rating, + score = card.score, description = card.description, cacheTime = System.currentTimeMillis(), ), null diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index c12f011831b..f0d6a5087fa 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -5,6 +5,7 @@ import androidx.fragment.app.Fragment import androidx.preference.PreferenceManager import com.lagradost.cloudstream3.DubStatus import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.Score import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.ui.result.EpisodeAdapter.Companion.getPlayerAction @@ -39,7 +40,7 @@ data class ResultEpisode( val index: Int, val position: Long, // time in MS val duration: Long, // duration in MS - val rating: Int?, + val score: Score?, val description: String?, val isFiller: Boolean?, val tvType: TvType, @@ -81,7 +82,7 @@ fun buildResultEpisode( apiName: String, id: Int, index: Int, - rating: Int? = null, + rating: Score? = null, description: String? = null, isFiller: Boolean? = null, tvType: TvType, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt index 3d992d87331..897b81e58bf 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt @@ -696,8 +696,8 @@ open class ResultFragmentPhone : FullScreenPlayer() { season = null, id = ep.id, parentId = ep.id, - rating = null, - description = null, + score = ep.score, + description = ep.description, cacheTime = System.currentTimeMillis(), ), null diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index 8641c62be45..86cd8e668c1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -264,8 +264,8 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData { ), yearText = txt(year?.toString()), apiName = txt(apiName), - ratingText = rating?.div(1000f) - ?.let { if (it <= 0.1f) null else txt(R.string.rating_format, it) }, + ratingText = score?.toStringNull(0.1, 10, 1, false, '.') + ?.let { txt(R.string.rating_format, it) }, contentRatingText = txt(contentRating), vpnText = txt( when (repo.vpnStatus) { @@ -738,7 +738,7 @@ class ResultViewModel2 : ViewModel() { season = episode.season, id = episode.id, parentId = parentId, - rating = episode.rating, + score = episode.score, description = episode.description, cacheTime = System.currentTimeMillis(), ) @@ -925,7 +925,7 @@ class ResultViewModel2 : ViewModel() { response.syncData, plot = response.plot, tags = response.tags, - rating = response.rating + score = response.score ) ) } @@ -1022,7 +1022,7 @@ class ResultViewModel2 : ViewModel() { response.year, response.syncData, plot = response.plot, - rating = response.rating, + score = response.score, tags = response.tags ) ) @@ -1093,7 +1093,7 @@ class ResultViewModel2 : ViewModel() { response.year, response.syncData, plot = response.plot, - rating = response.rating, + score = response.score, tags = response.tags ) ) @@ -1404,7 +1404,7 @@ class ResultViewModel2 : ViewModel() { }, isCasting = isCasting ) - } catch (e : CancellationException) { + } catch (e: CancellationException) { // Do nothing } catch (e: Exception) { logError(e) @@ -1660,13 +1660,14 @@ class ResultViewModel2 : ViewModel() { // Show player selection dialog val players = VideoClickActionHolder.getPlayers(ctx) val options = mutableListOf>() - + // Add internal player option options.add(txt(R.string.episode_action_play_in_app) to ACTION_PLAY_EPISODE_IN_PLAYER) - + // Add external player options options.addAll(players.filter { it !is AlwaysAskAction }.map { player -> - player.name to (VideoClickActionHolder.uniqueIdToId(player.uniqueId()) ?: ACTION_PLAY_EPISODE_IN_PLAYER) + player.name to (VideoClickActionHolder.uniqueIdToId(player.uniqueId()) + ?: ACTION_PLAY_EPISODE_IN_PLAYER) }) postPopup( @@ -1723,7 +1724,7 @@ class ResultViewModel2 : ViewModel() { if (meta != null) { duration = duration ?: meta.duration - rating = rating ?: meta.publicScore + score = score ?: meta.publicScore tags = tags ?: meta.genres plot = if (plot.isNullOrBlank()) meta.synopsis else plot posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl @@ -1942,8 +1943,8 @@ class ResultViewModel2 : ViewModel() { return when (sorting) { EpisodeSortType.NUMBER_ASC -> episodes.sortedBy { it.episode } EpisodeSortType.NUMBER_DESC -> episodes.sortedByDescending { it.episode } - EpisodeSortType.RATING_HIGH_LOW -> episodes.sortedByDescending { it.rating ?: 0 } - EpisodeSortType.RATING_LOW_HIGH -> episodes.sortedBy { it.rating ?: 0 } + EpisodeSortType.RATING_HIGH_LOW -> episodes.sortedByDescending { it.score?.toDouble() ?: 0.0 } + EpisodeSortType.RATING_LOW_HIGH -> episodes.sortedBy { it.score?.toDouble() ?: 0.0 } EpisodeSortType.DATE_NEWEST -> episodes.sortedByDescending { it.airDate } EpisodeSortType.DATE_OLDEST -> episodes.sortedBy { it.airDate } } @@ -2022,7 +2023,7 @@ class ResultViewModel2 : ViewModel() { return when (type) { EpisodeSortType.NUMBER_ASC, EpisodeSortType.NUMBER_DESC -> true EpisodeSortType.RATING_HIGH_LOW, EpisodeSortType.RATING_LOW_HIGH -> - episodes.any { it.rating != null } + episodes.any { it.score != null } EpisodeSortType.DATE_NEWEST, EpisodeSortType.DATE_OLDEST -> episodes.any { it.airDate != null } @@ -2274,7 +2275,7 @@ class ResultViewModel2 : ViewModel() { loadResponse.apiName, id, index, - i.rating, + i.score, i.description, fillers.getOrDefault(episode, false), loadResponse.type, @@ -2330,7 +2331,7 @@ class ResultViewModel2 : ViewModel() { loadResponse.apiName, id, index, - episode.rating, + episode.score, episode.description, null, loadResponse.type, @@ -2620,7 +2621,7 @@ class ResultViewModel2 : ViewModel() { override var posterUrl: String?, override var year: Int? = null, override var plot: String? = null, - override var rating: Int? = null, + override var score: Score? = null, override var tags: List? = null, override var duration: Int? = null, override var trailers: MutableList = mutableListOf(), @@ -2654,12 +2655,12 @@ class ResultViewModel2 : ViewModel() { ).apply { if (searchResponse is SyncAPI.LibraryItem) { this.plot = searchResponse.plot - this.rating = searchResponse.personalRating?.times(100) ?: searchResponse.rating + this.score = Score.from100(searchResponse.personalRating) ?: searchResponse.rating this.tags = searchResponse.tags } if (searchResponse is DataStoreHelper.BookmarkedData) { this.plot = searchResponse.plot - this.rating = searchResponse.rating + this.score = searchResponse.score this.tags = searchResponse.tags } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt index 01ec17b63f4..e176d6c9b6d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt @@ -38,7 +38,7 @@ object SearchHelper { season = card.season, id = id, parentId = card.parentId ?: return, - rating = null, + score = null, description = null, cacheTime = System.currentTimeMillis(), ) 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 650502dcaba..0fad6010280 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -15,6 +15,7 @@ import com.lagradost.cloudstream3.DubStatus import com.lagradost.cloudstream3.EpisodeResponse import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.Score import com.lagradost.cloudstream3.SearchQuality import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.TvType @@ -79,50 +80,64 @@ object DataStoreHelper { R.drawable.profile_bg_teal ) - private var searchPreferenceProvidersStrings : List by UserPreferenceDelegate( + private var searchPreferenceProvidersStrings: List by UserPreferenceDelegate( /** java moment right here, as listOf()::class.java != List(0) { "" }::class.java */ "search_pref_providers", List(0) { "" } ) - private fun serializeTv(data : List) : List = data.map { it.name } + private fun serializeTv(data: List): List = data.map { it.name } - private fun deserializeTv(data : List) : List { + private fun deserializeTv(data: List): List { return data.mapNotNull { listName -> TvType.values().firstOrNull { it.name == listName } } } - var searchPreferenceProviders : List + var searchPreferenceProviders: List get() { val ret = searchPreferenceProvidersStrings return ret.ifEmpty { context?.filterProviderByPreferredMedia()?.map { it.name } ?: emptyList() } - } set(value) { + } + set(value) { searchPreferenceProvidersStrings = value } - private var searchPreferenceTagsStrings : List by UserPreferenceDelegate("search_pref_tags", listOf(TvType.Movie, TvType.TvSeries).map { it.name }) - var searchPreferenceTags : List + private var searchPreferenceTagsStrings: List by UserPreferenceDelegate( + "search_pref_tags", + listOf(TvType.Movie, TvType.TvSeries).map { it.name }) + var searchPreferenceTags: List get() = deserializeTv(searchPreferenceTagsStrings) set(value) { searchPreferenceTagsStrings = serializeTv(value) } - private var homePreferenceStrings : List by UserPreferenceDelegate("home_pref_homepage", listOf(TvType.Movie, TvType.TvSeries).map { it.name }) - var homePreference : List + private var homePreferenceStrings: List by UserPreferenceDelegate( + "home_pref_homepage", + listOf(TvType.Movie, TvType.TvSeries).map { it.name }) + var homePreference: List get() = deserializeTv(homePreferenceStrings) set(value) { homePreferenceStrings = serializeTv(value) } - var homeBookmarkedList : IntArray by UserPreferenceDelegate("home_bookmarked_last_list", IntArray(0)) - var playBackSpeed : Float by UserPreferenceDelegate("playback_speed", 1.0f) - var resizeMode : Int by UserPreferenceDelegate("resize_mode", 0) - var librarySortingMode : Int by UserPreferenceDelegate("library_sorting_mode", ListSorting.AlphabeticalA.ordinal) - private var _resultsSortingMode : Int by UserPreferenceDelegate("results_sorting_mode", EpisodeSortType.NUMBER_ASC.ordinal) - var resultsSortingMode : EpisodeSortType + var homeBookmarkedList: IntArray by UserPreferenceDelegate( + "home_bookmarked_last_list", + IntArray(0) + ) + var playBackSpeed: Float by UserPreferenceDelegate("playback_speed", 1.0f) + var resizeMode: Int by UserPreferenceDelegate("resize_mode", 0) + var librarySortingMode: Int by UserPreferenceDelegate( + "library_sorting_mode", + ListSorting.AlphabeticalA.ordinal + ) + private var _resultsSortingMode: Int by UserPreferenceDelegate( + "results_sorting_mode", + EpisodeSortType.NUMBER_ASC.ordinal + ) + var resultsSortingMode: EpisodeSortType get() = EpisodeSortType.entries.getOrNull(_resultsSortingMode) ?: EpisodeSortType.NUMBER_ASC set(value) { _resultsSortingMode = value.ordinal @@ -140,7 +155,10 @@ object DataStoreHelper { @JsonProperty("lockPin") val lockPin: String? = null, ) { - val image get() = customImage?.let { UiImage.Image(it) } ?: profileImages.getOrNull(defaultImageIndex)?.let { UiImage.Drawable(it) } ?: UiImage.Drawable(profileImages.first()) + val image + get() = customImage?.let { UiImage.Image(it) } ?: profileImages.getOrNull( + defaultImageIndex + )?.let { UiImage.Drawable(it) } ?: UiImage.Drawable(profileImages.first()) } const val TAG = "data_store_helper" @@ -222,7 +240,8 @@ object DataStoreHelper { return this } - fun Int.toYear() : Date = GregorianCalendar.getInstance().also { it.set(Calendar.YEAR, this) }.time + fun Int.toYear(): Date = + GregorianCalendar.getInstance().also { it.set(Calendar.YEAR, this) }.time /** * Used to display notifications on new episodes and posters in library. @@ -239,10 +258,23 @@ object DataStoreHelper { @JsonProperty("syncData") open val syncData: Map?, @JsonProperty("quality") override var quality: SearchQuality?, @JsonProperty("posterHeaders") override var posterHeaders: Map?, - @JsonProperty("plot") open val plot : String? = null, - @JsonProperty("rating") open val rating : Int? = null, - @JsonProperty("tags") open val tags : List? = null, - ) : SearchResponse + @JsonProperty("plot") open val plot: String? = null, + @JsonProperty("score") open var score: Score? = null, + @JsonProperty("tags") open val tags: List? = null, + ) : SearchResponse { + @JsonProperty("rating", access = JsonProperty.Access.WRITE_ONLY) + @Deprecated( + "`rating` is the old scoring system, use score instead", + replaceWith = ReplaceWith("score"), + level = DeprecationLevel.ERROR + ) + var rating: Int? = null + set(value) { + if (value != null) { + score = Score.fromOld(value) + } + } + } data class SubscribedData( @JsonProperty("subscribedTime") val subscribedTime: Long, @@ -259,9 +291,24 @@ object DataStoreHelper { override var quality: SearchQuality? = null, override var posterHeaders: Map? = null, override val plot: String? = null, - override val rating: Int? = null, + override var score: Score? = null, override val tags: List? = null, - ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders, plot,rating,tags) { + ) : LibrarySearchResponse( + id, + latestUpdatedTime, + name, + url, + apiName, + type, + posterUrl, + year, + syncData, + quality, + posterHeaders, + plot, + score, + tags + ) { fun toLibraryItem(): SyncAPI.LibraryItem? { return SyncAPI.LibraryItem( name, @@ -271,7 +318,16 @@ object DataStoreHelper { null, null, latestUpdatedTime, - apiName, type, posterUrl, posterHeaders, quality, year?.toYear(), this.id, plot = this.plot, rating = this.rating, tags = this.tags + apiName, + type, + posterUrl, + posterHeaders, + quality, + year?.toYear(), + this.id, + plot = this.plot, + rating = this.score, + tags = this.tags ) } } @@ -290,9 +346,22 @@ object DataStoreHelper { override var quality: SearchQuality? = null, override var posterHeaders: Map? = null, override val plot: String? = null, - override val rating: Int? = null, + override var score: Score? = null, override val tags: List? = null, - ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders, plot) { + ) : LibrarySearchResponse( + id, + latestUpdatedTime, + name, + url, + apiName, + type, + posterUrl, + year, + syncData, + quality, + posterHeaders, + plot + ) { fun toLibraryItem(id: String): SyncAPI.LibraryItem { return SyncAPI.LibraryItem( name, @@ -302,7 +371,16 @@ object DataStoreHelper { null, null, latestUpdatedTime, - apiName, type, posterUrl, posterHeaders, quality, year?.toYear(), this.id, plot = this.plot, rating = this.rating, tags = this.tags + apiName, + type, + posterUrl, + posterHeaders, + quality, + year?.toYear(), + this.id, + plot = this.plot, + rating = this.score, + tags = this.tags ) } } @@ -321,9 +399,22 @@ object DataStoreHelper { override var quality: SearchQuality? = null, override var posterHeaders: Map? = null, override val plot: String? = null, - override val rating: Int? = null, + override var score: Score? = null, override val tags: List? = null, - ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders,plot) { + ) : LibrarySearchResponse( + id, + latestUpdatedTime, + name, + url, + apiName, + type, + posterUrl, + year, + syncData, + quality, + posterHeaders, + plot + ) { fun toLibraryItem(): SyncAPI.LibraryItem? { return SyncAPI.LibraryItem( name, @@ -333,7 +424,16 @@ object DataStoreHelper { null, null, latestUpdatedTime, - apiName, type, posterUrl, posterHeaders, quality, year?.toYear(), this.id, plot = this.plot, rating = this.rating, tags = this.tags + apiName, + type, + posterUrl, + posterHeaders, + quality, + year?.toYear(), + this.id, + plot = this.plot, + rating = this.score, + tags = this.tags ) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadHelper.kt index 30f66f835bb..966ccfd5662 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadHelper.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.utils import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.Score import com.lagradost.cloudstream3.TvType object VideoDownloadHelper { abstract class DownloadCached( @@ -13,11 +14,24 @@ object VideoDownloadHelper { @JsonProperty("episode") val episode: Int, @JsonProperty("season") val season: Int?, @JsonProperty("parentId") val parentId: Int, - @JsonProperty("rating") val rating: Int?, + @JsonProperty("score") var score: Score? = null, @JsonProperty("description") val description: String?, @JsonProperty("cacheTime") val cacheTime: Long, override val id: Int, - ): DownloadCached(id) + ): DownloadCached(id) { + @JsonProperty("rating", access = JsonProperty.Access.WRITE_ONLY) + @Deprecated( + "`rating` is the old scoring system, use score instead", + replaceWith = ReplaceWith("score"), + level = DeprecationLevel.ERROR + ) + var rating: Int? = null + set(value) { + if (value != null) { + score = Score.fromOld(value) + } + } + } data class DownloadHeaderCached( @JsonProperty("apiName") val apiName: String, diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index d9afb2b1bde..1c0c5b577cb 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -9,7 +9,6 @@ -%d %d %d - %.1f/10.0 %d %1$s epizoda %2$d Glumačka postava: %s diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 05bf7d5a920..3a820d2d78d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -9,7 +9,6 @@ -%d %d %d - %.1f/10.0 %d %1$s B. %2$d Cast: %s diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 6b191d33fb7..e4d7f9fa289 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -9,7 +9,6 @@ -%d %d %d - %.1f/10.0 %d %1$s 共 %2$d 集 演員:%s diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index da1d7d29014..3ed225bb423 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -9,7 +9,6 @@ -%d %d %d - %.1f/10.0 %d %1$s 共 %2$d 集 演员:%s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a973dcc2105..0f2797d84c7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -84,7 +84,7 @@ -%d %d %d - %.1f/10.0 + %s/10.0 %d %1$s Ep %2$d Cast: %s diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt index beb95403535..2e6887ab6b2 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt @@ -6,6 +6,7 @@ package com.lagradost.cloudstream3 +import com.fasterxml.jackson.annotation.JsonAutoDetect import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper @@ -28,6 +29,7 @@ import java.util.* import kotlin.io.encoding.Base64 import kotlin.io.encoding.ExperimentalEncodingApi import kotlin.math.absoluteValue +import kotlin.math.roundToInt /** Api has not yet been published to stable, and will cause `NoSuchMethodException` on stable */ @MustBeDocumented // Same as java.lang.annotation.Documented @@ -490,35 +492,35 @@ abstract class MainAPI { * * Note that this is only a hint, and may not get respected if you request something too long. * */ - open val loadLinksTimeoutMs : Long? = null + open val loadLinksTimeoutMs: Long? = null /** * The timeout on the `getMainPage` functions in milliseconds. * * Note that this is only a hint, and may not get respected if you request something too long. * */ - open val getMainPageTimeoutMs : Long? = null + open val getMainPageTimeoutMs: Long? = null /** * The timeout on the `search` functions in milliseconds. * * Note that this is only a hint, and may not get respected if you request something too long. * */ - open val searchTimeoutMs : Long? = null + open val searchTimeoutMs: Long? = null /** * The timeout on the `quickSearch` functions in milliseconds. * * Note that this is only a hint, and may not get respected if you request something too long. * */ - open val quickSearchTimeoutMs : Long? = null + open val quickSearchTimeoutMs: Long? = null /** * The timeout on the `loadSearch` functions in milliseconds. * * Note that this is only a hint, and may not get respected if you request something too long. * */ - open val loadTimeoutMs : Long? = null + open val loadTimeoutMs: Long? = null /** @@ -770,6 +772,204 @@ enum class DubStatus(val id: Int) { Subbed(0), } +/** This is the primary way to store score/rating. Use Score.from or Score.from10 to parse the score + * as it does not have a public constructor. Use toInt/toFloat to get back the score. + * + * Internally it stores it as an int up to 10^9 to represent up to 10 significant digits. So think + * of this as a decimal class specifically for ratings. + * */ +@Prerelease +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) +class Score private constructor( + /** Decimal between [0, 10^9] representing the min score and max score respectively */ + @JsonProperty("data") + private val data: Int, +) { + fun toOld(): Int = toInt(10000) + + fun toByte(maxScore: Int): Byte = toLong(maxScore).toByte() + + fun toInt(maxScore: Int = 10): Int = toLong(maxScore).toInt() + + fun toLong(maxScore: Int = 10): Long = (data.toLong() * maxScore.toLong()) / MAX.toLong() + + fun toFloat(maxScore: Int = 10): Float = + (data.toFloat() / MAX.toFloat()) * maxScore.toFloat() + + fun toDouble(maxScore: Int = 10): Double = + (data.toDouble() / MAX.toDouble()) * maxScore.toDouble() + + override fun toString(): String = this.toString(10) + + /** Formats the rating to a human readable format (with no rounding) + * + * However it may also return null if the score is less than the minimum score, + * this is to avoid 0.0/10.0 in case of default = 0 + * */ + @Throws(IllegalArgumentException::class) + fun toStringNull( + minScore: Double, + maxScore: Int, + decimals: Int = 1, + removeTrailingZeros: Boolean = true, + decimalChar: Char = '.' + ): String? { + if (toDouble() < minScore) return null + return toString(maxScore, decimals, removeTrailingZeros, decimalChar) + } + + /** Formats the rating to a human readable format (with no rounding) */ + @Throws(IllegalArgumentException::class) + fun toString( + maxScore: Int, + decimals: Int = 1, + removeTrailingZeros: Boolean = true, + decimalChar: Char = '.' + ): String { + require(maxScore in 1..1000) { + "maxScore ∈ [1,1000]" + } + require(decimals in 0..MAX_ZEROS) { + "decimals ∈ [0,$MAX_ZEROS]" + } + var number = data.toLong() * maxScore.toLong() + val chars = CharArray(MAX_ZEROS + 6) + + for (i in chars.indices) { + chars[i] = (number % 10L).toInt().digitToChar() + number /= 10L + } + + var trailingZeros = 0 + for (i in chars.indices) { + if (chars[i] != '0') { + break + } + trailingZeros += 1 + } + + var leadingZeros = 0 + for (i in chars.indices.reversed()) { + if (chars[i] != '0') { + break + } + leadingZeros += 1 + } + + val stringBuilder = StringBuilder() + for (i in maxOf(MAX_ZEROS, (chars.size - leadingZeros - 1)) downTo MAX_ZEROS) { + stringBuilder.append(chars[i]) + } + + val end = if (removeTrailingZeros) { + maxOf(MAX_ZEROS - decimals, trailingZeros) + } else { + MAX_ZEROS - decimals + } + + if (end <= MAX_ZEROS - 1) { + stringBuilder.append(decimalChar) + for (i in MAX_ZEROS - 1 downTo end) { + stringBuilder.append(chars[i]) + } + } + + return stringBuilder.toString() + } + + companion object { + const val MAX: Int = 1000_000_000 + const val MIN: Int = 0 + const val MAX_ZEROS: Int = 9 + private const val TAG: String = "Score" + + fun fromOld(value: Int?): Score? { + if (value == null) return null + if (value < 0 || value > 10000) { + com.lagradost.api.Log.w(TAG, "old: $value ∉ [0, 10000]") + return null + } + return Score(value * 100_000) + } + + /** `value ∈ [0, maxScore]` */ + fun from(value: Int?, maxScore: Int): Score? { + if (value == null) { + return null + } + if (value < 0 || value > maxScore) { + com.lagradost.api.Log.w(TAG, "fromInt: $value ∉ [0, $maxScore]") + return null + } + return Score((MAX / maxScore) * value) + } + + /** `value ∈ [0.0, maxScore]` */ + fun from(value: Double?, maxScore: Int): Score? { + if (value == null) { + return null + } + if (value < 0.0 || value > maxScore) { + com.lagradost.api.Log.w(TAG, "fromDouble: $value ∉ [0.0, $maxScore]") + return null + } + return Score(((MAX / maxScore).toDouble() * value).roundToInt()) + } + + /** `value ∈ [0.0f, maxScore]` */ + fun from(value: Float?, maxScore: Int): Score? { + if (value == null) { + return null + } + if (value < 0.0 || value > maxScore) { + com.lagradost.api.Log.w(TAG, "fromFloat: $value ∉ [0.0f, $maxScore]") + return null + } + return Score(((MAX / maxScore).toFloat() * value).roundToInt()) + } + + /** `value ∈ ["0.0", maxScore]` */ + fun from(value: String?, maxScore: Int): Score? = + from(value?.trim()?.toDoubleOrNull()?.absoluteValue, maxScore) + + /** `value ∈ [0, 5]` */ + fun from5(value: Int?): Score? = from(value, 5) + + /** `value ∈ [0, 10]` */ + fun from10(value: Int?): Score? = from(value, 10) + + /** `value ∈ [0, 100]` */ + fun from100(value: Int?): Score? = from(value, 100) + + /** `value ∈ [0.0, 5.0]` */ + fun from5(value: Double?): Score? = from(value, 5) + + /** `value ∈ [0.0, 10.0]` */ + fun from10(value: Double?): Score? = from(value, 10) + + /** `value ∈ [0.0, 100.0]` */ + fun from100(value: Double?): Score? = from(value, 100) + + /** `value ∈ [0.0f, 5.0f]` */ + fun from5(value: Float?): Score? = from(value, 5) + + /** `value ∈ [0.0f, 10.0f]` */ + fun from10(value: Float?): Score? = from(value, 10) + + /** `value ∈ [0.0f, 100.0f]` */ + fun from100(value: Float?): Score? = from(value, 100) + + /** `value ∈ ["0.0", "5.0"]` */ + fun from5(value: String?): Score? = from(value, 5) + + /** `value ∈ ["0.0", "10.0"]` */ + fun from10(value: String?): Score? = from(value, 10) + + /** `value ∈ ["0.0", "100.0"]` */ + fun from100(value: String?): Score? = from(value, 100) + } +} + @Suppress("UNUSED_PARAMETER") enum class TvType(value: Int?) { Movie(1), @@ -1287,7 +1487,8 @@ data class TrailerData( * @property posterUrl Url of the media poster, appears on Top of result page. * @property year Year of the media, appears on result page. * @property plot Plot of the media, appears on result page. - * @property rating Rating of the media, appears on result page (0-10000). + * @property score Rating of the media, appears on result page. + * Use it with addScore or by assigning a score like `Score.from(string/float/int/double, 10)` or `Score.from10(string/float/int/double)` * @property tags Tags of the media, appears on result page. * @property duration duration of the media, appears on result page. * @property trailers list of the media [TrailerData], used to load trailers. @@ -1311,7 +1512,9 @@ interface LoadResponse { var posterUrl: String? var year: Int? var plot: String? - var rating: Int? // 0-10000 + + @Prerelease + var score: Score? var tags: List? var duration: Int? // in minutes var trailers: MutableList @@ -1325,7 +1528,17 @@ interface LoadResponse { var contentRating: String? @Prerelease - var uniqueUrl : String + var uniqueUrl: String + + @Deprecated( + "`rating` is the old scoring system, use score instead", + replaceWith = ReplaceWith("score") + ) + var rating: Int? + set(value) { + this.score = Score.fromOld(value) + } + get() = score?.toOld() companion object { var malIdPrefix = "" //malApi.idPrefix @@ -1541,15 +1754,21 @@ interface LoadResponse { this.addSimklId(SimklSyncServices.Tmdb, id) } + fun LoadResponse.addScore(score: String?, maxValue: Int = 10) { + this.score = Score.from(score, maxValue) + } + + fun LoadResponse.addScore(score: Score?) { + this.score = score + } + fun LoadResponse.addRating(text: String?) { - addRating(text.toRatingInt()) + this.score = Score.from10(text) } + @Deprecated("Use addScore", replaceWith = ReplaceWith("addScore")) fun LoadResponse.addRating(value: Int?) { - if ((value ?: return) < 0 || value > 10000) { - return - } - this.rating = value + this.score = Score.fromOld(value) } fun LoadResponse.addDuration(input: String?) { @@ -1741,7 +1960,7 @@ fun EpisodeResponse.addSeasonNames(names: List) { * @see newTorrentLoadResponse */ data class TorrentLoadResponse -@Deprecated("Use newTorrentLoadResponse method", level = DeprecationLevel.WARNING) +@Deprecated("Use newTorrentLoadResponse method", level = DeprecationLevel.ERROR) constructor( override var name: String, override var url: String, @@ -1752,7 +1971,7 @@ constructor( override var type: TvType = TvType.Torrent, override var posterUrl: String? = null, override var year: Int? = null, - override var rating: Int? = null, + override var score: Score? = null, override var tags: List? = null, override var duration: Int? = null, override var trailers: MutableList = mutableListOf(), @@ -1764,16 +1983,16 @@ constructor( override var backgroundPosterUrl: String? = null, override var contentRating: String? = null, @Prerelease - override var uniqueUrl : String = url + override var uniqueUrl: String = url ) : LoadResponse { /** * Secondary constructor for backwards compatibility without contentRating. * Remove this constructor after there is a new stable release and extensions are updated to support contentRating. */ - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") @Deprecated( "Use newTorrentLoadResponse method with contentRating included", - level = DeprecationLevel.WARNING + level = DeprecationLevel.ERROR ) constructor( name: String, @@ -1805,7 +2024,7 @@ constructor( type, posterUrl, year, - rating, + Score.fromOld(rating), tags, duration, trailers, @@ -1826,7 +2045,7 @@ suspend fun MainAPI.newTorrentLoadResponse( torrent: String? = null, initializer: suspend TorrentLoadResponse.() -> Unit = { } ): TorrentLoadResponse { - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") val builder = TorrentLoadResponse( name = name, url = url, @@ -1845,7 +2064,7 @@ suspend fun MainAPI.newTorrentLoadResponse( * @see newAnimeLoadResponse * */ data class AnimeLoadResponse -@Deprecated("Use newAnimeLoadResponse method", level = DeprecationLevel.WARNING) +@Deprecated("Use newAnimeLoadResponse method", level = DeprecationLevel.ERROR) constructor( var engName: String? = null, var japName: String? = null, @@ -1864,7 +2083,7 @@ constructor( override var tags: List? = null, var synonyms: List? = null, - override var rating: Int? = null, + override var score: Score? = null, override var duration: Int? = null, override var trailers: MutableList = mutableListOf(), override var recommendations: List? = null, @@ -1877,8 +2096,9 @@ constructor( override var backgroundPosterUrl: String? = null, override var contentRating: String? = null, @Prerelease - override var uniqueUrl : String = url + override var uniqueUrl: String = url ) : LoadResponse, EpisodeResponse { + override fun getLatestEpisodes(): Map { return episodes.map { (status, episodes) -> val maxSeason = episodes.maxOfOrNull { it.season ?: Int.MIN_VALUE } @@ -1908,7 +2128,7 @@ constructor( * Secondary constructor for backwards compatibility without contentRating. * Remove this constructor after there is a new stable release and extensions are updated to support contentRating. */ - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") @Deprecated( "Use newAnimeLoadResponse method with contentRating included", level = DeprecationLevel.WARNING @@ -1952,7 +2172,7 @@ constructor( plot, tags, synonyms, - rating, + Score.fromOld(rating), duration, trailers, recommendations, @@ -2000,7 +2220,7 @@ suspend fun MainAPI.newAnimeLoadResponse( * @see newLiveStreamLoadResponse * */ data class LiveStreamLoadResponse -@Deprecated("Use newLiveStreamLoadResponse method", level = DeprecationLevel.WARNING) +@Deprecated("Use newLiveStreamLoadResponse method", level = DeprecationLevel.ERROR) constructor( override var name: String, override var url: String, @@ -2012,7 +2232,7 @@ constructor( override var plot: String? = null, override var type: TvType = TvType.Live, - override var rating: Int? = null, + override var score: Score? = null, override var tags: List? = null, override var duration: Int? = null, override var trailers: MutableList = mutableListOf(), @@ -2024,13 +2244,13 @@ constructor( override var backgroundPosterUrl: String? = null, override var contentRating: String? = null, @Prerelease - override var uniqueUrl : String = url + override var uniqueUrl: String = url ) : LoadResponse { /** * Secondary constructor for backwards compatibility without contentRating. * Remove this constructor after there is a new stable release and extensions are updated to support contentRating. */ - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") @Deprecated( "Use newLiveStreamLoadResponse method with contentRating included", level = DeprecationLevel.WARNING @@ -2055,8 +2275,25 @@ constructor( posterHeaders: Map? = null, backgroundPosterUrl: String? = null, ) : this( - name, url, apiName, dataUrl, posterUrl, year, plot, type, rating, tags, duration, trailers, - recommendations, actors, comingSoon, syncData, posterHeaders, backgroundPosterUrl, null + name, + url, + apiName, + dataUrl, + posterUrl, + year, + plot, + type, + Score.fromOld(rating), + tags, + duration, + trailers, + recommendations, + actors, + comingSoon, + syncData, + posterHeaders, + backgroundPosterUrl, + null ) } @@ -2066,7 +2303,7 @@ suspend fun MainAPI.newLiveStreamLoadResponse( dataUrl: String, initializer: suspend LiveStreamLoadResponse.() -> Unit = { } ): LiveStreamLoadResponse { - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") val builder = LiveStreamLoadResponse( name = name, url = url, @@ -2082,7 +2319,7 @@ suspend fun MainAPI.newLiveStreamLoadResponse( * @see newMovieLoadResponse * */ data class MovieLoadResponse -@Deprecated("Use newMovieLoadResponse method", level = DeprecationLevel.WARNING) +@Deprecated("Use newMovieLoadResponse method", level = DeprecationLevel.ERROR) constructor( override var name: String, override var url: String, @@ -2094,7 +2331,7 @@ constructor( override var year: Int? = null, override var plot: String? = null, - override var rating: Int? = null, + override var score: Score? = null, override var tags: List? = null, override var duration: Int? = null, override var trailers: MutableList = mutableListOf(), @@ -2106,16 +2343,16 @@ constructor( override var backgroundPosterUrl: String? = null, override var contentRating: String? = null, @Prerelease - override var uniqueUrl : String = url + override var uniqueUrl: String = url ) : LoadResponse { /** * Secondary constructor for backwards compatibility without contentRating. * Remove this constructor after there is a new stable release and extensions are updated to support contentRating. */ - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") @Deprecated( "Use newMovieLoadResponse method with contentRating included", - level = DeprecationLevel.WARNING + level = DeprecationLevel.ERROR ) constructor( name: String, @@ -2137,8 +2374,25 @@ constructor( posterHeaders: Map? = null, backgroundPosterUrl: String? = null, ) : this( - name, url, apiName, type, dataUrl, posterUrl, year, plot, rating, tags, duration, trailers, - recommendations, actors, comingSoon, syncData, posterHeaders, backgroundPosterUrl, null + name, + url, + apiName, + type, + dataUrl, + posterUrl, + year, + plot, + Score.fromOld(rating), + tags, + duration, + trailers, + recommendations, + actors, + comingSoon, + syncData, + posterHeaders, + backgroundPosterUrl, + null ) } @@ -2159,7 +2413,7 @@ suspend fun MainAPI.newMovieLoadResponse( ) val dataUrl = data?.toJson() ?: "" - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") val builder = MovieLoadResponse( name = name, url = url, @@ -2179,7 +2433,7 @@ suspend fun MainAPI.newMovieLoadResponse( dataUrl: String, initializer: suspend MovieLoadResponse.() -> Unit = { } ): MovieLoadResponse { - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") val builder = MovieLoadResponse( name = name, url = url, @@ -2198,30 +2452,40 @@ suspend fun MainAPI.newMovieLoadResponse( * @property season Season number. * @property episode Episode number. * @property posterUrl URL of Episode's poster image. - * @property rating Episode rating. + * @property score Episode rating. * @property date Episode air date, see addDate. * @property runTime Episode runtime in seconds. * @see newEpisode * */ data class Episode -@Deprecated("Use newEpisode", level = DeprecationLevel.WARNING) +@Deprecated("Use newEpisode", level = DeprecationLevel.ERROR) constructor( var data: String, var name: String? = null, var season: Int? = null, var episode: Int? = null, var posterUrl: String? = null, - var rating: Int? = null, + var score: Score? = null, var description: String? = null, var date: Long? = null, var runTime: Int? = null, ) { + @Deprecated( + "`rating` is the old scoring system, use score instead", + replaceWith = ReplaceWith("score") + ) + var rating: Int? + set(value) { + this.score = Score.from(value, 100) + } + get() = score?.toInt(100) + /** * Secondary constructor for backwards compatibility without runTime. * TODO Remove this constructor after there is a new stable release and extensions are updated to support runTime. */ - @Suppress("DEPRECATION") - @Deprecated("Use newEpisode with runTime included", level = DeprecationLevel.WARNING) + @Suppress("DEPRECATION_ERROR") + @Deprecated("Use newEpisode with runTime included", level = DeprecationLevel.ERROR) constructor( data: String, name: String? = null, @@ -2232,7 +2496,7 @@ constructor( description: String? = null, date: Long? = null, ) : this( - data, name, season, episode, posterUrl, rating, description, date, null + data, name, season, episode, posterUrl, Score.fromOld(rating), description, date, null ) } @@ -2253,7 +2517,7 @@ fun MainAPI.newEpisode( initializer: Episode.() -> Unit = { }, fix: Boolean = true, ): Episode { - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") val builder = Episode( data = if (fix) fixUrl(url) else url ) @@ -2270,7 +2534,7 @@ fun MainAPI.newEpisode( initializer = initializer ) // just in case java is wack - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") val builder = Episode( data = data?.toJson() ?: throw ErrorLoadingException("invalid newEpisode") ) @@ -2300,7 +2564,7 @@ enum class SimklSyncServices(val originalName: String) { * @see newTvSeriesLoadResponse * */ data class TvSeriesLoadResponse -@Deprecated("Use newTvSeriesLoadResponse method", level = DeprecationLevel.WARNING) +@Deprecated("Use newTvSeriesLoadResponse method", level = DeprecationLevel.ERROR) constructor( override var name: String, override var url: String, @@ -2313,7 +2577,7 @@ constructor( override var plot: String? = null, override var showStatus: ShowStatus? = null, - override var rating: Int? = null, + override var score: Score? = null, override var tags: List? = null, override var duration: Int? = null, override var trailers: MutableList = mutableListOf(), @@ -2327,7 +2591,7 @@ constructor( override var backgroundPosterUrl: String? = null, override var contentRating: String? = null, @Prerelease - override var uniqueUrl : String = url + override var uniqueUrl: String = url ) : LoadResponse, EpisodeResponse { override fun getLatestEpisodes(): Map { val maxSeason = @@ -2355,10 +2619,10 @@ constructor( * Secondary constructor for backwards compatibility without contentRating. * Remove this constructor after there is a new stable release and extensions are updated to support contentRating. */ - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") @Deprecated( "Use newTvSeriesLoadResponse method with contentRating included", - level = DeprecationLevel.WARNING + level = DeprecationLevel.ERROR ) constructor( name: String, @@ -2392,7 +2656,7 @@ constructor( year, plot, showStatus, - rating, + Score.fromOld(rating), tags, duration, trailers, @@ -2415,7 +2679,7 @@ suspend fun MainAPI.newTvSeriesLoadResponse( episodes: List, initializer: suspend TvSeriesLoadResponse.() -> Unit = { } ): TvSeriesLoadResponse { - @Suppress("DEPRECATION") + @Suppress("DEPRECATION_ERROR") val builder = TvSeriesLoadResponse( name = name, url = url, diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/MyDramaList.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/MyDramaList.kt index 1dd2ee79e38..a4ccab8c11e 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/MyDramaList.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/MyDramaList.kt @@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.MainAPI import com.lagradost.cloudstream3.MainPageRequest import com.lagradost.cloudstream3.ProviderType +import com.lagradost.cloudstream3.Score import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.ShowStatus import com.lagradost.cloudstream3.TvType @@ -173,7 +174,7 @@ abstract class MyDramaListAPI : MainAPI() { this.posterUrl = media.images.poster this.year = media.mediaYear this.plot = media.synopsis - this.rating = media.mediaRating.times(1000).toInt() + this.score = Score.from10(media.mediaRating) this.tags = media.fixGenres() this.duration = media.runtime.toInt() this.recommendations = media.fetchRecommendations().map { it.toSearchResponse() } @@ -350,7 +351,7 @@ abstract class MyDramaListAPI : MainAPI() { season = null episode = ep.episodeNumber posterUrl = null - rating = ep.rating.times(1000).toInt() + score = Score.from10(ep.rating) description = null runTime = null addDate(ep.releasedAt) diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt index 70732c5ef65..fa8a73b4968 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt @@ -180,7 +180,7 @@ open class TmdbProvider : MainAPI() { tags = genres?.mapNotNull { it.name } duration = episode_run_time?.average()?.toInt() - rating = this@toLoadResponse.rating + // score = Score.from10(this@toLoadResponse.rating) No docs on this? addTrailer(videos.toTrailers()) recommendations = (this@toLoadResponse.recommendations @@ -224,7 +224,7 @@ open class TmdbProvider : MainAPI() { addImdbId(external_ids?.imdb_id) tags = genres?.mapNotNull { it.name } duration = runtime - rating = this@toLoadResponse.rating + // score = Score.from10(this@toLoadResponse.rating) No docs on this? addTrailer(videos.toTrailers()) recommendations = (this@toLoadResponse.recommendations diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt index 1dd0df7df74..d040886fa61 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt @@ -9,12 +9,14 @@ import com.lagradost.cloudstream3.Episode import com.lagradost.cloudstream3.HomePageResponse import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId +import com.lagradost.cloudstream3.LoadResponse.Companion.addRating import com.lagradost.cloudstream3.LoadResponse.Companion.addTMDbId import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.MainAPI import com.lagradost.cloudstream3.MainPageRequest import com.lagradost.cloudstream3.NextAiring import com.lagradost.cloudstream3.ProviderType +import com.lagradost.cloudstream3.Score import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.ShowStatus import com.lagradost.cloudstream3.TvType @@ -178,7 +180,7 @@ open class TraktProvider : MainAPI() { this.posterUrl = getOriginalWidthImageUrl(posterUrl) this.year = mediaDetails.year this.plot = mediaDetails.overview - this.rating = mediaDetails.rating?.times(1000)?.roundToInt() + this.score = Score.from10(mediaDetails.rating) this.tags = mediaDetails.genres this.duration = mediaDetails.runtime this.recommendations = relatedMedia @@ -266,7 +268,7 @@ open class TraktProvider : MainAPI() { this.year = mediaDetails.year this.plot = mediaDetails.overview this.showStatus = getStatus(mediaDetails.status) - this.rating = mediaDetails.rating?.times(1000)?.roundToInt() + this.score = Score.from10(mediaDetails.rating) this.tags = mediaDetails.genres this.duration = mediaDetails.runtime this.recommendations = relatedMedia