fixed posters, trailer and anilist/mal + added option for posters

This commit is contained in:
LagradOst 2022-06-19 01:03:25 +02:00
parent f90a5dd2cb
commit 7c7b3a3e5a
13 changed files with 108 additions and 42 deletions

View file

@ -10,6 +10,7 @@ class SyncRepo(private val repo: SyncAPI) {
val name = repo.name val name = repo.name
val icon = repo.icon val icon = repo.icon
val mainUrl = repo.mainUrl val mainUrl = repo.mainUrl
val requiresLogin = repo.requiresLogin
suspend fun score(id: String, status: SyncAPI.SyncStatus): Resource<Boolean> { suspend fun score(id: String, status: SyncAPI.SyncStatus): Resource<Boolean> {
return safeApiCall { repo.score(id, status) } return safeApiCall { repo.score(id, status) }

View file

@ -29,7 +29,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
override val idPrefix = "anilist" override val idPrefix = "anilist"
override var mainUrl = "https://anilist.co" override var mainUrl = "https://anilist.co"
override val icon = R.drawable.ic_anilist_icon override val icon = R.drawable.ic_anilist_icon
override val requiresLogin = true override val requiresLogin = false
override val createAccountUrl = "$mainUrl/signup" override val createAccountUrl = "$mainUrl/signup"
override fun loginInfo(): AuthAPI.LoginInfo? { override fun loginInfo(): AuthAPI.LoginInfo? {
@ -90,9 +90,9 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
} }
} }
override suspend fun getResult(id: String): SyncAPI.SyncResult? { override suspend fun getResult(id: String): SyncAPI.SyncResult {
val internalId = (Regex("anilist\\.co/anime/(\\d*)").find(id)?.groupValues?.getOrNull(1) val internalId = (Regex("anilist\\.co/anime/(\\d*)").find(id)?.groupValues?.getOrNull(1)
?: id).toIntOrNull() ?: return null ?: id).toIntOrNull() ?: throw ErrorLoadingException("Invalid internalId")
val season = getSeason(internalId).data.Media val season = getSeason(internalId).data.Media
return SyncAPI.SyncResult( return SyncAPI.SyncResult(

View file

@ -26,10 +26,14 @@ object Kitsu {
private val cache: MutableMap<Pair<String, String>, Map<Int, KitsuResponse.Node>> = private val cache: MutableMap<Pair<String, String>, Map<Int, KitsuResponse.Node>> =
mutableMapOf() mutableMapOf()
var isEnabled = true
suspend fun getEpisodesDetails( suspend fun getEpisodesDetails(
malId: String?, malId: String?,
anilistId: String? anilistId: String?,
isResponseRequired: Boolean = true, // overrides isEnabled
): Map<Int, KitsuResponse.Node>? { ): Map<Int, KitsuResponse.Node>? {
if (!isResponseRequired && !isEnabled) return null
if (anilistId != null) { if (anilistId != null) {
try { try {
val map = getKitsuEpisodesDetails(anilistId, "ANILIST_ANIME") val map = getKitsuEpisodesDetails(anilistId, "ANILIST_ANIME")

View file

@ -35,7 +35,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
override var mainUrl = "https://myanimelist.net" override var mainUrl = "https://myanimelist.net"
val apiUrl = "https://api.myanimelist.net" val apiUrl = "https://api.myanimelist.net"
override val icon = R.drawable.mal_logo override val icon = R.drawable.mal_logo
override val requiresLogin = true override val requiresLogin = false
override val createAccountUrl = "$mainUrl/register.php" override val createAccountUrl = "$mainUrl/register.php"
@ -189,9 +189,11 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
val internalId = id.toIntOrNull() ?: return null val internalId = id.toIntOrNull() ?: return null
val url = val url =
"$apiUrl/v2/anime/$internalId?fields=id,title,main_picture,alternative_titles,start_date,end_date,synopsis,mean,rank,popularity,num_list_users,num_scoring_users,nsfw,created_at,updated_at,media_type,status,genres,my_list_status,num_episodes,start_season,broadcast,source,average_episode_duration,rating,pictures,background,related_anime,related_manga,recommendations,studios,statistics" "$apiUrl/v2/anime/$internalId?fields=id,title,main_picture,alternative_titles,start_date,end_date,synopsis,mean,rank,popularity,num_list_users,num_scoring_users,nsfw,created_at,updated_at,media_type,status,genres,my_list_status,num_episodes,start_season,broadcast,source,average_episode_duration,rating,pictures,background,related_anime,related_manga,recommendations,studios,statistics"
val auth = getAuth()
val res = app.get( val res = app.get(
url, headers = mapOf( url, headers = if (auth == null) emptyMap() else mapOf(
"Authorization" to "Bearer " + (getAuth() ?: return null) "Authorization" to "Bearer $auth"
) )
).text ).text
return mapper.readValue<MalAnime>(res).let { malAnime -> return mapper.readValue<MalAnime>(res).let { malAnime ->

View file

@ -736,7 +736,7 @@ class CS3IPlayer : IPlayer {
super.onPlaybackStateChanged(playbackState) super.onPlaybackStateChanged(playbackState)
when (playbackState) { when (playbackState) {
Player.STATE_READY -> { Player.STATE_READY -> {
requestAutoFocus?.invoke()
} }
Player.STATE_ENDED -> { Player.STATE_ENDED -> {
handleEvent(CSPlayerEvent.NextEpisode) handleEvent(CSPlayerEvent.NextEpisode)

View file

@ -46,6 +46,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.getCastSession import com.lagradost.cloudstream3.CommonActivity.getCastSession
import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
@ -642,6 +643,8 @@ class ResultFragment : ResultTrailerPlayer() {
false false
} }
result_trailer_loading?.isVisible = isSuccess result_trailer_loading?.isVisible = isSuccess
result_smallscreen_holder?.isVisible = !isSuccess && !isFullScreenPlayer
result_fullscreen_holder?.isVisible = !isSuccess && isFullScreenPlayer
} }
private fun setTrailers(trailers: List<ExtractorLink>?) { private fun setTrailers(trailers: List<ExtractorLink>?) {
@ -2114,6 +2117,9 @@ class ResultFragment : ResultTrailerPlayer() {
val showFillers = val showFillers =
settingsManager.getBoolean(ctx.getString(R.string.show_fillers_key), false) settingsManager.getBoolean(ctx.getString(R.string.show_fillers_key), false)
Kitsu.isEnabled =
settingsManager.getBoolean(ctx.getString(R.string.show_kitsu_posters_key), true)
val tempUrl = url val tempUrl = url
if (tempUrl != null) { if (tempUrl != null) {
result_reload_connectionerror.setOnClickListener { result_reload_connectionerror.setOnClickListener {

View file

@ -54,6 +54,9 @@ open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreen
} }
result_trailer_loading?.isVisible = false result_trailer_loading?.isVisible = false
result_smallscreen_holder?.isVisible = !isFullScreenPlayer
result_fullscreen_holder?.isVisible = isFullScreenPlayer
player_background?.apply { player_background?.apply {
isVisible = true isVisible = true
layoutParams = layoutParams =

View file

@ -5,6 +5,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
@ -28,6 +29,7 @@ import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.player.IGenerator import com.lagradost.cloudstream3.ui.player.IGenerator
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
import com.lagradost.cloudstream3.utils.Coroutines.ioWork import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.DataStoreHelper
@ -160,30 +162,35 @@ class ResultViewModel : ViewModel() {
argamap({ argamap({
addTrailer(meta.trailers) addTrailer(meta.trailers)
}, { }, {
if (this !is AnimeLoadResponse) return@argamap if (this !is AnimeLoadResponse) return@argamap
val map = getEpisodesDetails(getMalId(), getAniListId()) val map = getEpisodesDetails(getMalId(), getAniListId(), isResponseRequired = false)
if (map.isNullOrEmpty()) return@argamap if (map.isNullOrEmpty()) return@argamap
updateEpisodes = DubStatus.values().map { dubStatus -> updateEpisodes = DubStatus.values().map { dubStatus ->
val current = val current =
this.episodes[dubStatus]?.sortedBy { it.episode ?: 0 }?.toMutableList() this.episodes[dubStatus]?.mapIndexed { index, episode ->
episode.apply {
this.episode = this.episode ?: (index + 1)
}
}?.sortedBy { it.episode ?: 0 }?.toMutableList()
if (current.isNullOrEmpty()) return@map false if (current.isNullOrEmpty()) return@map false
val episodes = current.mapIndexed { index, ep -> ep.episode ?: (index + 1) } val episodeNumbers = current.map { ep -> ep.episode!! }
var updateCount = 0 var updateCount = 0
map.forEach { (episode, node) -> map.forEach { (episode, node) ->
episodes.binarySearch(episode).let { index -> episodeNumbers.binarySearch(episode).let { index ->
current.getOrNull(index)?.let { currentEp -> current.getOrNull(index)?.let { currentEp ->
current[index] = currentEp.apply { current[index] = currentEp.apply {
updateCount++ updateCount++
val currentBack = this
this.description = this.description ?: node.description?.en this.description = this.description ?: node.description?.en
this.name = this.name ?: node.titles?.canonical this.name = this.name ?: node.titles?.canonical
this.episode = this.episode ?: node.num ?: episodes[index] this.episode = this.episode ?: node.num ?: episodeNumbers[index]
this.posterUrl = this.posterUrl ?: node.thumbnail?.original?.url this.posterUrl = this.posterUrl ?: node.thumbnail?.original?.url
} }
} }
} }
} }
this.episodes[dubStatus] = current this.episodes[dubStatus] = current
updateCount > 0 updateCount > 0
}.any { it } }.any { it }
}) })
@ -432,6 +439,7 @@ class ResultViewModel : ViewModel() {
res[dubStatus]?.let { episodes -> res[dubStatus]?.let { episodes ->
updateEpisodes(mainId, episodes, -1) updateEpisodes(mainId, episodes, -1)
} }
_dubStatus.postValue(dubStatus) _dubStatus.postValue(dubStatus)
_dubSubSelections.postValue(loadResponse.episodes.keys) _dubSubSelections.postValue(loadResponse.episodes.keys)
} }

View file

@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.utils.SyncUtil import com.lagradost.cloudstream3.utils.SyncUtil
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.*
data class CurrentSynced( data class CurrentSynced(
@ -178,7 +179,12 @@ class SyncViewModel : ViewModel() {
fun modifyMaxEpisode(episodeNum: Int) { fun modifyMaxEpisode(episodeNum: Int) {
Log.i(TAG, "modifyMaxEpisode = $episodeNum") Log.i(TAG, "modifyMaxEpisode = $episodeNum")
modifyData { status -> modifyData { status ->
status.copy(watchedEpisodes = maxOf(episodeNum, status.watchedEpisodes ?: return@modifyData null)) status.copy(
watchedEpisodes = maxOf(
episodeNum,
status.watchedEpisodes ?: return@modifyData null
)
)
} }
} }
@ -228,15 +234,26 @@ class SyncViewModel : ViewModel() {
_metaResponse.postValue(Resource.Loading()) _metaResponse.postValue(Resource.Loading())
var lastError: Resource<SyncAPI.SyncResult> = Resource.Failure(false, null, null, "No data") var lastError: Resource<SyncAPI.SyncResult> = Resource.Failure(false, null, null, "No data")
syncs.forEach { (prefix, id) -> val current = syncs.toList()
// shitty way to sort anilist first, as it has trailers while mal does not
if (syncs.containsKey(aniListApi.idPrefix)) {
Collections.swap(current, current.indexOfFirst { it.first == aniListApi.idPrefix }, 0)
}
current.forEach { (prefix, id) ->
repos.firstOrNull { it.idPrefix == prefix }?.let { repo -> repos.firstOrNull { it.idPrefix == prefix }?.let { repo ->
if (repo.hasAccount()) { if (!repo.requiresLogin || repo.hasAccount()) {
Log.i(TAG, "updateMetadata loading ${repo.idPrefix}")
val result = repo.getResult(id) val result = repo.getResult(id)
if (result is Resource.Success) { if (result is Resource.Success) {
_metaResponse.postValue(result) _metaResponse.postValue(result)
return@launch return@launch
} else if (result is Resource.Failure) { } else if (result is Resource.Failure) {
Log.e(TAG, "updateMetadata error ${result.errorString}") Log.e(
TAG,
"updateMetadata error $id at ${repo.idPrefix} ${result.errorString}"
)
lastError = result lastError = result
} }
} }

View file

@ -0,0 +1,15 @@
<vector xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:tint="?attr/white"
android:viewportHeight="225"
android:viewportWidth="225"
android:width="24dp">
<path
android:fillColor="@android:color/white"
android:pathData="M152.7 48.5c-6.8-2.5-14.1-4.1-21.8-4.4-12.7-0.6-24.8 2.2-35.4 7.6-0.6 0.3-1.3 0.6-2 1v36.4c0 0.5 0 2.4-0.3 4-0.7 3.7-2.9 7-6 9.1-2.6 1.8-5.6 2.6-8.8 2.5-0.6 0-1.3-0.1-1.9-0.2-1.6-0.3-3.3-0.9-3.8-1.1-1.4-0.5-21.8-8.4-31.6-12.2-1.2-0.5-2.2-0.9-3-1.2-11.7 9.9-24 21.7-35.5 35.6-0.1 0.1-0.6 0.7-0.7 0.8-1.5 2.1-1.6 5.1 0 7.4 1.2 1.7 3.1 2.7 5 2.8 1.3 0.1 2.7-0.3 3.9-1.1 0.1-0.1 0.2-0.2 0.4-0.3 12.2-8.8 25.6-15.9 39.8-21.6 1-0.5 2.2-0.8 3.3-0.7 1.6 0.1 3.1 0.7 4.3 1.9 2.3 2.3 2.4 6 0.5 8.5-0.8 1.2-1.5 2.4-2.2 3.6-7.6 12.4-13.7 25.9-18.3 40-0.1 0.4-0.2 0.7-0.3 1.1v0.1c-0.4 1.7-0.1 3.5 1 5 1.2 1.7 3.1 2.7 5.1 2.8 1.4 0.1 2.7-0.3 3.9-1.1 0.5-0.4 1-0.8 1.4-1.3 0.1-0.2 0.3-0.4 0.4-0.6 5-7.1 10.5-13.8 16.4-20 26.3-28.2 61.2-48.1 100.3-55.9 0.3-0.1 0.6-0.1 0.9-0.1 2.2 0.1 3.9 2 3.8 4.2-0.1 1.9-1.4 3.3-3.2 3.7-36.3 7.7-101.7 50.8-78.8 113.4 0.4 1 0.7 1.6 1.2 2.5 1.2 1.7 3.1 2.7 5 2.8 1.1 0 4.2-0.3 6.1-3.7 3.7-7 10.7-14.8 30.9-23.2 56.3-23.3 65.6-56.6 66.6-77.7v-1.2c0.9-31.4-18.6-58.8-46.6-69.2zm-56.5 165C91 198 91.5 183 97.6 168.7c11.7 18.9 32.1 20.5 32.1 20.5-20.9 8.7-29.1 17.3-33.5 24.3z"
tools:ignore="VectorPath" />
<path
android:fillColor="@android:color/white"
android:pathData="M1.1 50.6c0.1 0.2 0.3 0.4 0.4 0.5 5.3 7.2 11.3 13.5 17.8 19.1 0.1 0.1 0.2 0.1 0.2 0.2 4.2 3.6 12.2 8.8 18 10.9 0 0 36.1 13.9 38 14.7 0.7 0.3 1.7 0.6 2.2 0.7 1.6 0.3 3.3 0 4.8-1s2.4-2.5 2.7-4.1c0.1-0.6 0.2-1.6 0.2-2.3V48.5c0.1-6.2-1.9-15.6-3.7-20.8 0-0.1-0.1-0.2-0.1-0.3-2.8-8.1-6.6-16-11.4-23.5l-0.3-0.6-0.1-0.1c-2-2.8-6-3.5-8.9-1.5-0.5 0.3-0.8 0.7-1.2 1.1-0.3 0.4-0.5 0.7-0.8 1.1-6.4 9.3-9 20.6-7.3 31.7-3.3 1.7-6.8 4-7.2 4.3-0.4 0.3-3.9 2.7-6.6 5.2-9.7-5.5-21.3-7.2-32.2-4.6-0.4 0.1-0.9 0.2-1.3 0.3-0.5 0.2-1 0.4-1.4 0.7-2.9 2-3.7 5.9-1.8 8.9v0.2zm63.5-40.1c3.4 5.7 6.3 11.6 8.6 17.8-4.6 0.8-9.1 2-13.5 3.6-0.6-7.5 1.1-14.9 4.9-21.4zM31.5 51.3c-3.2 3.5-5.9 7.3-8.3 11.3-4.9-4.3-9.4-9.2-13.5-14.4 7.5-1.3 15-0.2 21.8 3.1z" />
</vector>

View file

@ -8,6 +8,31 @@
android:screenOrientation="landscape" android:screenOrientation="landscape"
tools:orientation="vertical"> tools:orientation="vertical">
<TextView
android:id="@+id/player_time_text"
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center"
android:shadowColor="@android:color/black"
android:shadowRadius="10.0"
android:textColor="@android:color/white"
android:textSize="30sp"
tools:text="+100" />
<FrameLayout
android:id="@+id/subtitle_holder"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/shadow_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black_overlay" />
</FrameLayout>
<FrameLayout <FrameLayout
android:id="@+id/player_intro_play" android:id="@+id/player_intro_play"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -35,29 +60,6 @@
android:layout_height="60dp" /> android:layout_height="60dp" />
</FrameLayout> </FrameLayout>
<TextView
android:id="@+id/player_time_text"
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center"
android:shadowColor="@android:color/black"
android:shadowRadius="10.0"
android:textColor="@android:color/white"
android:textSize="30sp"
tools:text="+100" />
<FrameLayout
android:id="@+id/subtitle_holder"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/shadow_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black_overlay" />
</FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/player_video_holder" android:id="@+id/player_video_holder"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -32,6 +32,7 @@
<string name="display_sub_key" translatable="false">display_sub_key</string> <string name="display_sub_key" translatable="false">display_sub_key</string>
<string name="show_fillers_key" translatable="false">show_fillers_key</string> <string name="show_fillers_key" translatable="false">show_fillers_key</string>
<string name="show_trailers_key" translatable="false">show_trailers_key</string> <string name="show_trailers_key" translatable="false">show_trailers_key</string>
<string name="show_kitsu_posters_key" translatable="false">show_kitsu_posters_key</string>
<string name="random_button_key" translatable="false">random_button_key</string> <string name="random_button_key" translatable="false">random_button_key</string>
<string name="provider_lang_key" translatable="false">provider_lang_key</string> <string name="provider_lang_key" translatable="false">provider_lang_key</string>
<string name="dns_key" translatable="false">dns_key</string> <string name="dns_key" translatable="false">dns_key</string>
@ -252,6 +253,8 @@
<string name="bug_report_settings_on">Sends no data</string> <string name="bug_report_settings_on">Sends no data</string>
<string name="show_fillers_settings">Show filler episode for anime</string> <string name="show_fillers_settings">Show filler episode for anime</string>
<string name="show_trailers_settings">Show trailers</string> <string name="show_trailers_settings">Show trailers</string>
<string name="kitsu_settings">Show posters from kitsu</string>
<string name="updates_settings">Show app updates</string> <string name="updates_settings">Show app updates</string>
<string name="updates_settings_des">Automatically search for new updates on start</string> <string name="updates_settings_des">Automatically search for new updates on start</string>
<string name="uprereleases_settings">Update to prereleases</string> <string name="uprereleases_settings">Update to prereleases</string>

View file

@ -34,6 +34,11 @@
android:icon="@drawable/baseline_theaters_24" android:icon="@drawable/baseline_theaters_24"
android:title="@string/show_trailers_settings" android:title="@string/show_trailers_settings"
android:defaultValue="true" /> android:defaultValue="true" />
<SwitchPreference
android:key="@string/show_kitsu_posters_key"
android:icon="@drawable/kitsu_icon"
android:title="@string/kitsu_settings"
android:defaultValue="true" />
<SwitchPreference <SwitchPreference
android:key="@string/show_fillers_key" android:key="@string/show_fillers_key"
android:icon="@drawable/ic_baseline_skip_next_24" android:icon="@drawable/ic_baseline_skip_next_24"