diff --git a/app/build.gradle b/app/build.gradle index 67dabcfc..e0e26466 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,7 +36,7 @@ android { targetSdkVersion 30 versionCode 45 - versionName "2.9.18" + versionName "2.9.19" resValue "string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}" @@ -96,8 +96,8 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' - implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0-alpha03' - implementation 'androidx.navigation:navigation-ui-ktx:2.5.0-alpha03' + implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0-alpha04' + implementation 'androidx.navigation:navigation-ui-ktx:2.5.0-alpha04' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1' testImplementation 'junit:junit:4.13.2' @@ -126,6 +126,8 @@ dependencies { implementation 'com.google.android.exoplayer:exoplayer:2.16.1' implementation 'com.google.android.exoplayer:extension-cast:2.16.1' implementation "com.google.android.exoplayer:extension-mediasession:2.16.1" + implementation 'com.google.android.exoplayer:extension-okhttp:2.16.1' + //implementation "com.google.android.exoplayer:extension-leanback:2.14.0" // Bug reports @@ -154,7 +156,6 @@ dependencies { // Networking implementation "com.squareup.okhttp3:okhttp:4.9.2" implementation "com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1" - implementation 'com.google.android.exoplayer:extension-okhttp:2.16.1' // Util to skip the URI file fuckery 🙏 implementation "com.github.tachiyomiorg:unifile:17bec43" diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NineAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NineAnimeProvider.kt index 0a5367be..e80dbc87 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NineAnimeProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NineAnimeProvider.kt @@ -197,7 +197,8 @@ class NineAnimeProvider : MainAPI() { ) override suspend fun load(url: String): LoadResponse? { - val doc = app.get(url).document + val validUrl = url.replace("https://9anime.to", mainUrl) + val doc = app.get(validUrl).document val animeid = doc.selectFirst("div.player-wrapper.watchpage").attr("data-id") ?: return null val animeidencoded = encode(getVrf(animeid) ?: return null) val poster = doc.selectFirst("aside.main div.thumb div img").attr("src") @@ -233,7 +234,7 @@ class NineAnimeProvider : MainAPI() { else null val tags = doc.select("div.info .meta .col1 div:contains(Genre) a").map { it.text() } - return newAnimeLoadResponse(title, url, tvType) { + return newAnimeLoadResponse(title, validUrl, tvType) { this.posterUrl = poster this.plot = description this.recommendations = recommendations diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt new file mode 100644 index 00000000..2813aa7c --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt @@ -0,0 +1,30 @@ +package com.lagradost.cloudstream3.metaproviders + +import com.lagradost.cloudstream3.ErrorLoadingException +import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.SyncApis +import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi +import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.malApi +import com.lagradost.cloudstream3.utils.SyncUtil + +object SyncRedirector { + val syncApis = SyncApis + + suspend fun redirect(url: String, preferredUrl: String): String { + for (api in syncApis) { + if (url.contains(api.mainUrl)) { + val otherApi = when (api.name) { + aniListApi.name -> "anilist" + malApi.name -> "myanimelist" + else -> return url + } + + return SyncUtil.getUrlsFromId(api.getIdFromUrl(url), otherApi).firstOrNull { realUrl -> + realUrl.contains(preferredUrl) + } ?: run { + throw ErrorLoadingException("Page does not exist on $preferredUrl") + } + } + } + return url + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt index c1d0aa26..50f2af20 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt @@ -4,13 +4,26 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.syncproviders.OAuth2API +import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.syncproviders.providers.AniListApi +import com.lagradost.cloudstream3.syncproviders.providers.MALApi +import com.lagradost.cloudstream3.utils.SyncUtil +// wont be implemented class MultiAnimeProvider : MainAPI() { override var name = "MultiAnime" override val lang = "en" override val usesWebView = true override val supportedTypes = setOf(TvType.Anime) - private val syncApi = OAuth2API.aniListApi + private val syncApi: SyncAPI = OAuth2API.aniListApi + + private val syncUtilType by lazy { + when (syncApi) { + is AniListApi -> "anilist" + is MALApi -> "myanimelist" + else -> throw ErrorLoadingException("Invalid Api") + } + } private val validApis by lazy { APIHolder.apis.filter { @@ -32,13 +45,25 @@ class MultiAnimeProvider : MainAPI() { override suspend fun load(url: String): LoadResponse? { return syncApi.getResult(url)?.let { res -> - newAnimeLoadResponse(res.title!!, url, TvType.Anime) { + val data = SyncUtil.getUrlsFromId(res.id, syncUtilType).apmap { url -> + validApis.firstOrNull { api -> url.startsWith(api.mainUrl) }?.load(url) + }.filterNotNull() + + val type = + if (data.any { it.type == TvType.AnimeMovie }) TvType.AnimeMovie else TvType.Anime + + newAnimeLoadResponse( + res.title ?: throw ErrorLoadingException("No Title found"), + url, + type + ) { posterUrl = res.posterUrl plot = res.synopsis tags = res.genres rating = res.publicScore addTrailer(res.trailerUrl) addAniListId(res.id.toIntOrNull()) + recommendations = res.recommendations } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt index 5c72ac17..589a039f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt @@ -218,31 +218,26 @@ class NginxProvider : MainAPI() { if (isMovieType) { val movieName = nfoContent.select("title").text() - val posterUrl = mediaRootUrl + "poster.jpg" - - return@mapNotNull MovieSearchResponse( + return@mapNotNull newMovieSearchResponse( movieName, mediaRootUrl, - this.name, TvType.Movie, - posterUrl, - null, - ) + ) { + addPoster(posterUrl, authHeader) + } } else { // tv serie val serieName = nfoContent.select("title").text() val posterUrl = mediaRootUrl + "poster.jpg" - TvSeriesSearchResponse( + newTvSeriesSearchResponse( serieName, nfoPath, - this.name, TvType.TvSeries, - posterUrl, - null, - null, - ) + ) { + addPoster(posterUrl, authHeader) + } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt index 62bb9449..06a7fd0d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt @@ -605,14 +605,12 @@ open class SflixProvider : MainAPI() { M3u8Helper().m3u8Generation(M3u8Helper.M3u8Stream(this.file, null), true) .map { stream -> //println("stream: ${stream.quality} at ${stream.streamUrl}") - val qualityString = if ((stream.quality ?: 0) == 0) label - ?: "" else "${stream.quality}p" ExtractorLink( caller.name, - "${caller.name} $qualityString $name", + "${caller.name} $name", stream.streamUrl, caller.mainUrl, - getQualityFromName(stream.quality.toString()), + getQualityFromName(stream.quality?.toString()), true, extractorData = extractorData ) 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 bdfff9c1..40d91dcd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt @@ -1,7 +1,6 @@ package com.lagradost.cloudstream3.syncproviders -import com.lagradost.cloudstream3.ActorData -import com.lagradost.cloudstream3.ShowStatus +import com.lagradost.cloudstream3.* interface SyncAPI : OAuth2API { val icon: Int @@ -24,13 +23,19 @@ interface SyncAPI : OAuth2API { suspend fun search(name: String): List? + fun getIdFromUrl(url : String) : String + data class SyncSearchResult( - val name: String, - val syncApiName: String, - val id: String, - val url: String, - val posterUrl: String?, - ) + override val name: String, + override val apiName: String, + var syncId: String, + override val url: String, + override var posterUrl: String?, + override var type: TvType? = null, + override var quality: SearchQuality? = null, + override var posterHeaders: Map? = null, + override var id: Int? = null, + ) : SearchResponse data class SyncNextAiring( val episode: Int, diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt index bbdc061d..daddcc2a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt @@ -9,6 +9,7 @@ class SyncRepo(private val repo: SyncAPI) { val idPrefix = repo.idPrefix val name = repo.name val icon = repo.icon + val mainUrl = repo.mainUrl suspend fun score(id: String, status: SyncAPI.SyncStatus): Resource { return safeApiCall { repo.score(id, status) } @@ -29,4 +30,6 @@ class SyncRepo(private val repo: SyncAPI) { fun hasAccount() : Boolean { return normalSafeApiCall { repo.loginInfo() != null } ?: false } + + fun getIdFromUrl(url : String) : String = repo.getIdFromUrl(url) } \ 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 798c1c30..b7398d05 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 @@ -70,6 +70,14 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { return user != null } + override fun getIdFromUrl(url : String): String { + return url.removePrefix("$mainUrl/anime/").removeSuffix("/") + } + + private fun getUrlFromId(id: Int): String { + return "$mainUrl/anime/$id" + } + override suspend fun search(name: String): List? { val data = searchShows(name) ?: return null return data.data?.Page?.media?.map { @@ -77,7 +85,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { it.title.romaji ?: return null, this.name, it.id.toString(), - "$mainUrl/anime/${it.id}", + getUrlFromId(it.id), it.bannerImage ) } @@ -126,7 +134,16 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { ) }, publicScore = season.averageScore?.times(100), - //recommendations = season. + recommendations = season.recommendations?.edges?.mapNotNull { rec -> + val recMedia = rec.node.mediaRecommendation + SyncAPI.SyncSearchResult( + name = recMedia.title?.userPreferred ?: return@mapNotNull null, + this.name, + recMedia.id?.toString() ?: return@mapNotNull null, + getUrlFromId(recMedia.id), + recMedia.coverImage?.large ?: recMedia.coverImage?.medium + ) + } //TODO REST ) } @@ -335,10 +352,10 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { color } title { - romaji - english - native - userPreferred + romaji + english + native + userPreferred } duration episodes @@ -382,23 +399,44 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { thumbnail } relations { - edges { - id - relationType(version: 2) - node { - id - coverImage { - extraLarge - large - medium - color - } - } - } + edges { + id + relationType(version: 2) + node { + id + coverImage { + extraLarge + large + medium + color + } + } + } + } + recommendations { + edges { + node { + mediaRecommendation { + id + coverImage { + extraLarge + large + medium + color + } + title { + romaji + english + native + userPreferred + } + } + } + } } nextAiringEpisode { - timeUntilAiring - episode + timeUntilAiring + episode } format } @@ -772,6 +810,21 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("trailer") val trailer: MediaTrailer?, @JsonProperty("description") val description: String?, @JsonProperty("characters") val characters: CharacterConnection?, + @JsonProperty("recommendations") val recommendations: RecommendationConnection?, + ) + + data class RecommendationConnection( + @JsonProperty("edges") val edges: List = emptyList(), + @JsonProperty("nodes") val nodes: List = emptyList(), + //@JsonProperty("pageInfo") val pageInfo: PageInfo, + ) + + data class RecommendationEdge( + //@JsonProperty("rating") val rating: Int, + @JsonProperty("node") val node: Recommendation, + ) + data class Recommendation( + @JsonProperty("mediaRecommendation") val mediaRecommendation: SeasonMedia, ) data class CharacterName( @@ -955,13 +1008,6 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("data") val data: LikeData?, ) - data class Recommendation( - @JsonProperty("title") val title: String?, - @JsonProperty("idMal") val idMal: Int?, - @JsonProperty("poster") val poster: String?, - @JsonProperty("averageScore") val averageScore: Int? - ) - data class AniListTitleHolder( @JsonProperty("title") val title: Title?, @JsonProperty("isFavourite") val isFavourite: Boolean?, 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 5853ad94..b73aded2 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 @@ -81,6 +81,10 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } + override fun getIdFromUrl(url: String): String { + return Regex("""/anime/((.*)/|(.*))""").find(url)!!.groupValues.first() + } + override suspend fun score(id: String, status: SyncAPI.SyncStatus): Boolean { return setScoreRequest( id.toIntOrNull() ?: return false, @@ -173,8 +177,8 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { private fun toSearchResult(node: Node?): SyncAPI.SyncSearchResult? { return SyncAPI.SyncSearchResult( name = node?.title ?: return null, - syncApiName = this.name, - id = node.id.toString(), + apiName = this.name, + syncId = node.id.toString(), url = "https://myanimelist.net/anime/${node.id}", posterUrl = node.main_picture?.large ) 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 b4e9c6b8..bcc51262 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 @@ -93,6 +93,7 @@ import com.lagradost.cloudstream3.utils.VideoDownloadManager.getFileName import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename import kotlinx.android.synthetic.main.fragment_result.* import kotlinx.android.synthetic.main.fragment_result_swipe.* +import kotlinx.android.synthetic.main.result_recommendations.* import kotlinx.android.synthetic.main.result_sync.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -572,14 +573,6 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio setFormatText(result_meta_rating, R.string.rating_format, rating?.div(1000f)) } - private fun setMalSync(id: Int?): Boolean { - return syncModel.setMalId(id?.toString()) - } - - private fun setAniListSync(id: Int?): Boolean { - return syncModel.setAniListId(id?.toString()) - } - private fun setActors(actors: List?) { if (actors.isNullOrEmpty()) { result_cast_text?.isVisible = false @@ -601,23 +594,47 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio } } - private fun setRecommendations(rec: List?) { + private fun setRecommendations(rec: List?, validApiName: String?) { val isInvalid = rec.isNullOrEmpty() result_recommendations?.isGone = isInvalid result_recommendations_btt?.isGone = isInvalid result_recommendations_btt?.setOnClickListener { - if (result_overlapping_panels?.getSelectedPanel()?.ordinal == 1) { - result_recommendations_btt?.nextFocusDownId = R.id.result_recommendations + val nextFocusDown = if (result_overlapping_panels?.getSelectedPanel()?.ordinal == 1) { result_overlapping_panels?.openEndPanel() + R.id.result_recommendations } else { - result_recommendations_btt?.nextFocusDownId = R.id.result_description result_overlapping_panels?.closePanels() + R.id.result_description } + + result_recommendations_btt?.nextFocusDownId = nextFocusDown + result_search?.nextFocusDownId = nextFocusDown + result_open_in_browser?.nextFocusDownId = nextFocusDown + result_share?.nextFocusDownId = nextFocusDown } result_overlapping_panels?.setEndPanelLockState(if (isInvalid) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED) + + val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName + rec?.map { it.apiName }?.distinct()?.let { apiNames -> + // very dirty selection + result_recommendations_filter_button?.isVisible = apiNames.size > 1 + result_recommendations_filter_button?.text = matchAgainst + result_recommendations_filter_button?.setOnClickListener { _ -> + activity?.showBottomDialog( + apiNames, + apiNames.indexOf(matchAgainst), + getString(R.string.home_change_provider_img_des), false, {} + ) { + setRecommendations(rec, apiNames[it]) + } + } + } ?: run { + result_recommendations_filter_button?.isVisible = false + } + result_recommendations?.post { rec?.let { list -> - (result_recommendations?.adapter as SearchAdapter?)?.updateList(list) + (result_recommendations?.adapter as SearchAdapter?)?.updateList(list.filter { it.apiName == matchAgainst }) } } } @@ -1086,7 +1103,6 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio ACTION_PLAY_EPISODE_IN_PLAYER -> { viewModel.getGenerator(episodeClick.data) ?.let { generator -> - println("LANUCJ:::: $syncdata") activity?.navigate( R.id.global_to_navigation_player, GeneratorPlayer.newInstance( @@ -1641,7 +1657,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio setDuration(d.duration) setYear(d.year) setRating(d.rating) - setRecommendations(d.recommendations) + setRecommendations(d.recommendations, null) setActors(d.actors) if (SettingsFragment.accountEnabled) { @@ -1950,7 +1966,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio } } - result_recommendations.adapter = recAdapter + result_recommendations?.adapter = recAdapter context?.let { ctx -> result_bookmark_button?.isVisible = ctx.isTvSettings() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt index 4007df33..08a20eb3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt @@ -11,6 +11,9 @@ import com.lagradost.cloudstream3.APIHolder.getApiFromUrlNull import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider +import com.lagradost.cloudstream3.animeproviders.NineAnimeProvider +import com.lagradost.cloudstream3.metaproviders.SyncRedirector import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.syncproviders.SyncAPI @@ -127,6 +130,17 @@ class ResultViewModel : ViewModel() { addTrailer(meta.trailerUrl) posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl actors = actors ?: meta.actors + + val realRecommendations = ArrayList() + val apiNames = listOf(GogoanimeProvider().name, NineAnimeProvider().name) + meta.recommendations?.forEach { rec -> + apiNames.forEach { name -> + realRecommendations.add(rec.copy(apiName = name)) + } + } + + recommendations = recommendations?.union(realRecommendations)?.toList() + ?: realRecommendations } } @@ -296,10 +310,9 @@ class ResultViewModel : ViewModel() { } fun load(url: String, apiName: String, showFillers: Boolean) = viewModelScope.launch { - _resultResponse.postValue(Resource.Loading(url)) _publicEpisodes.postValue(Resource.Loading()) + _resultResponse.postValue(Resource.Loading(url)) - _apiName.postValue(apiName) val api = getApiFromNameNull(apiName) ?: getApiFromUrlNull(url) if (api == null) { _resultResponse.postValue( @@ -312,9 +325,31 @@ class ResultViewModel : ViewModel() { ) return@launch } + + val validUrlResource = safeApiCall { + SyncRedirector.redirect( + url, + api.mainUrl.replace(NineAnimeProvider().mainUrl, "9anime") + .replace(GogoanimeProvider().mainUrl, "gogoanime") + ) + } + + if (validUrlResource !is Resource.Success) { + if (validUrlResource is Resource.Failure) { + _resultResponse.postValue(validUrlResource) + } + + return@launch + } + val validUrl = validUrlResource.value + + _resultResponse.postValue(Resource.Loading(validUrl)) + + _apiName.postValue(apiName) + repo = APIRepository(api) - val data = repo?.load(url) ?: return@launch + val data = repo?.load(validUrl) ?: return@launch _resultResponse.postValue(data) @@ -331,7 +366,7 @@ class ResultViewModel : ViewModel() { mainId.toString(), VideoDownloadHelper.DownloadHeaderCached( apiName, - url, + validUrl, d.type, d.name, d.posterUrl, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt index 3c8177c2..a14061e5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt @@ -82,16 +82,16 @@ class SyncViewModel : ViewModel() { var isValid = false map?.forEach { (prefix, id) -> - isValid = isValid || addSync(prefix, id) + isValid = addSync(prefix, id) || isValid } return isValid } - fun setMalId(id: String?): Boolean { + private fun setMalId(id: String?): Boolean { return addSync(malApi.idPrefix, id ?: return false) } - fun setAniListId(id: String?): Boolean { + private fun setAniListId(id: String?): Boolean { return addSync(aniListApi.idPrefix, id ?: return false) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SyncSearchViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SyncSearchViewModel.kt index 9c517d4a..7f6587c1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SyncSearchViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SyncSearchViewModel.kt @@ -4,7 +4,6 @@ import com.lagradost.cloudstream3.SearchQuality import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.syncproviders.OAuth2API -import com.lagradost.cloudstream3.syncproviders.SyncAPI class SyncSearchViewModel { private val repos = OAuth2API.SyncApis @@ -20,15 +19,4 @@ class SyncSearchViewModel { override var posterHeaders: Map? = null, ) : SearchResponse - private fun SyncAPI.SyncSearchResult.toSearchResponse(): SyncSearchResultSearchResponse { - return SyncSearchResultSearchResponse( - this.name, - this.url, - this.syncApiName, - null, - this.posterUrl, - null, //this.id.hashCode() - ) - } - } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/CastHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/CastHelper.kt index e48c51e3..b56264e2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/CastHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/CastHelper.kt @@ -7,6 +7,7 @@ import com.google.android.gms.cast.framework.CastSession import com.google.android.gms.cast.framework.media.RemoteMediaClient import com.google.android.gms.common.api.PendingResult import com.google.android.gms.common.images.WebImage +import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.MetadataHolder import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.ui.result.ResultEpisode @@ -64,7 +65,10 @@ object CastHelper { return builder.build() } - fun awaitLinks(pending: PendingResult?, callback: (Boolean) -> Unit) { + fun awaitLinks( + pending: PendingResult?, + callback: (Boolean) -> Unit + ) { if (pending == null) return main { val res = withContext(Dispatchers.IO) { pending.await() } @@ -90,27 +94,15 @@ object CastHelper { startIndex: Int? = null, startTime: Long? = null, ): Boolean { - if (this == null) return false - if (episodes.isEmpty()) return false - if (currentEpisodeIndex >= episodes.size) return false + try { + if (this == null) return false + if (episodes.isEmpty()) return false + if (currentEpisodeIndex >= episodes.size) return false - val epData = episodes[currentEpisodeIndex] + val epData = episodes[currentEpisodeIndex] - val holder = - MetadataHolder(apiName, isMovie, title, poster, currentEpisodeIndex, episodes, currentLinks, subtitles) - - val index = if (startIndex == null || startIndex < 0) 0 else startIndex - - val mediaItem = - getMediaInfo(epData, holder, index, JSONObject(holder.toJson()), subtitles) - - awaitLinks( - this.remoteMediaClient?.load( - MediaLoadRequestData.Builder().setMediaInfo(mediaItem).setCurrentTime(startTime ?: 0L).build() - ) - ) { - if (currentLinks.size > index + 1) - startCast( + val holder = + MetadataHolder( apiName, isMovie, title, @@ -118,11 +110,38 @@ object CastHelper { currentEpisodeIndex, episodes, currentLinks, - subtitles, - index + 1, - startTime + subtitles ) + + val index = if (startIndex == null || startIndex < 0) 0 else startIndex + + val mediaItem = + getMediaInfo(epData, holder, index, JSONObject(holder.toJson()), subtitles) + + awaitLinks( + this.remoteMediaClient?.load( + MediaLoadRequestData.Builder().setMediaInfo(mediaItem) + .setCurrentTime(startTime ?: 0L).build() + ) + ) { + if (currentLinks.size > index + 1) + startCast( + apiName, + isMovie, + title, + poster, + currentEpisodeIndex, + episodes, + currentLinks, + subtitles, + index + 1, + startTime + ) + } + return true + } catch (e: Exception) { + logError(e) + return false } - return true } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt index 03bb82b8..4f9e294e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt @@ -53,7 +53,7 @@ object SyncUtil { * valid sites are: Gogoanime, Twistmoe and 9anime*/ private suspend fun getIdsFromSlug( slug: String, - site: String = "GogoanimeGogoanime" + site: String = "Gogoanime" ): Pair? { Log.i(TAG, "getIdsFromSlug $slug $site") try { @@ -76,6 +76,28 @@ object SyncUtil { return null } + suspend fun getUrlsFromId(id: String, type: String = "anilist") : List { + val url = + "https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/$type/anime/$id.json" + val response = app.get(url, cacheTime = 1, cacheUnit = TimeUnit.DAYS).mapped() + val pages = response.pages ?: return emptyList() + return pages.gogoanime.values.union(pages.nineanime.values).union(pages.twistmoe.values).mapNotNull { it.url } + } + + data class SyncPage( + @JsonProperty("Pages") val pages: SyncPages?, + ) + + data class SyncPages( + @JsonProperty("9anime") val nineanime: Map = emptyMap(), + @JsonProperty("Gogoanime") val gogoanime: Map = emptyMap(), + @JsonProperty("Twistmoe") val twistmoe: Map = emptyMap(), + ) + + data class ProviderPage( + @JsonProperty("url") val url: String?, + ) + data class MalSyncPage( @JsonProperty("identifier") val identifier: String?, @JsonProperty("type") val type: String?, diff --git a/app/src/main/res/layout/fragment_result_swipe.xml b/app/src/main/res/layout/fragment_result_swipe.xml index 18666a7b..875f7721 100644 --- a/app/src/main/res/layout/fragment_result_swipe.xml +++ b/app/src/main/res/layout/fragment_result_swipe.xml @@ -180,17 +180,7 @@ android:layout_height="match_parent" android:layout_gravity="end"> - + diff --git a/app/src/main/res/layout/result_recommendations.xml b/app/src/main/res/layout/result_recommendations.xml new file mode 100644 index 00000000..1585960a --- /dev/null +++ b/app/src/main/res/layout/result_recommendations.xml @@ -0,0 +1,37 @@ + + + + + + + + + + \ No newline at end of file