backend fixes for opening from sync urls

This commit is contained in:
Blatzar 2022-12-05 20:49:45 +01:00
parent 05dc032df6
commit 67a1c447ae
11 changed files with 134 additions and 59 deletions

View file

@ -10,15 +10,14 @@ import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.lagradost.cloudstream3.metaproviders.SyncIdName
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
import okhttp3.Interceptor import okhttp3.Interceptor
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -402,6 +401,20 @@ abstract class MainAPI {
open val hasMainPage = false open val hasMainPage = false
open val hasQuickSearch = false open val hasQuickSearch = false
/**
* A set of which ids the provider can open with getLoadUrl()
* If the set contains SyncIdName.Imdb then getLoadUrl() can be started with
* an Imdb class which inherits from SyncId.
*
* getLoadUrl() is then used to get page url based on that ID.
*
* Example:
* "tt6723592" -> getLoadUrl(ImdbSyncId("tt6723592")) -> "mainUrl/imdb/tt6723592" -> load("mainUrl/imdb/tt6723592")
*
* This is used to launch pages from personal lists or recommendations using IDs.
**/
open val supportedSyncNames = setOf<SyncIdName>()
open val supportedTypes = setOf( open val supportedTypes = setOf(
TvType.Movie, TvType.Movie,
TvType.TvSeries, TvType.TvSeries,
@ -412,7 +425,6 @@ abstract class MainAPI {
open val vpnStatus = VPNStatus.None open val vpnStatus = VPNStatus.None
open val providerType = ProviderType.DirectProvider open val providerType = ProviderType.DirectProvider
open val mainPage = listOf(MainPageData("", "")) open val mainPage = listOf(MainPageData("", ""))
@WorkerThread @WorkerThread
@ -471,6 +483,14 @@ abstract class MainAPI {
open fun getVideoInterceptor(extractorLink: ExtractorLink): Interceptor? { open fun getVideoInterceptor(extractorLink: ExtractorLink): Interceptor? {
return null return null
} }
/**
* Get the load() url based on a sync ID like IMDb or MAL.
* Only contains SyncIds based on supportedSyncUrls.
**/
open suspend fun getLoadUrl(name: SyncIdName, id: String): String? {
return null
}
} }
/** Might need a different implementation for desktop*/ /** Might need a different implementation for desktop*/

View file

@ -1,30 +0,0 @@
package com.lagradost.cloudstream3.metaproviders
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
import com.lagradost.cloudstream3.syncproviders.AccountManager.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
}
}

View file

@ -0,0 +1,57 @@
package com.lagradost.cloudstream3.metaproviders
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
import com.lagradost.cloudstream3.utils.SyncUtil
enum class SyncIdName {
AniList,
MyAnimeList,
Trakt,
Imdb
}
object SyncRedirector {
val syncApis = SyncApis
private val syncIds =
listOf(
SyncIdName.MyAnimeList to Regex("""myanimelist\.net\/anime\/(\d+)"""),
SyncIdName.AniList to Regex("""anilist\.co\/anime\/(\d+)""")
)
suspend fun redirect(url: String, providerApi: MainAPI): String {
// Tries built in ID -> ProviderUrl
for (api in syncApis) {
if (url.contains(api.mainUrl)) {
val otherApi = when (api.name) {
aniListApi.name -> "anilist"
malApi.name -> "myanimelist"
else -> return url
}
SyncUtil.getUrlsFromId(api.getIdFromUrl(url), otherApi).firstOrNull { realUrl ->
realUrl.contains(providerApi.mainUrl)
}?.let {
return it
}
// ?: run {
// throw ErrorLoadingException("Page does not exist on $preferredUrl")
// }
}
}
// Tries provider solution
return syncIds.firstNotNullOfOrNull { (syncName, syncRegex) ->
if (providerApi.supportedSyncNames.contains(syncName)) {
syncRegex.find(url)?.value?.let {
suspendSafeApiCall {
providerApi.getLoadUrl(syncName, it)
}
}
} else null
} ?: url
}
}

View file

@ -602,7 +602,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
// English title first // English title first
this.media.title.english ?: this.media.title.romaji ?: this.media.synonyms.firstOrNull() this.media.title.english ?: this.media.title.romaji ?: this.media.synonyms.firstOrNull()
?: "", ?: "",
this.media.id.toString(), "https://anilist.co/anime/${this.media.id}/",
listName ?: return null, listName ?: return null,
this.progress, this.progress,
this.media.episodes, this.media.episodes,

View file

@ -387,7 +387,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
fun toLibraryItem(): LibraryItem { fun toLibraryItem(): LibraryItem {
return LibraryItem( return LibraryItem(
this.node.title, this.node.title,
this.node.id.toString(), "https://myanimelist.net/anime/${this.node.id}/",
this.list_status?.status?.lowercase()?.capitalize()?.replace("_", " ") ?: "NONE", this.list_status?.status?.lowercase()?.capitalize()?.replace("_", " ") ?: "NONE",
this.list_status?.num_episodes_watched, this.list_status?.num_episodes_watched,
this.node.num_episodes, this.node.num_episodes,

View file

@ -12,6 +12,8 @@ import com.google.android.material.tabs.TabLayoutMediator
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import kotlinx.android.synthetic.main.fragment_library.* import kotlinx.android.synthetic.main.fragment_library.*
@ -68,12 +70,17 @@ class LibraryFragment : Fragment() {
viewpager?.setPageTransformer(LibraryScrollTransformer()) viewpager?.setPageTransformer(LibraryScrollTransformer())
viewpager?.adapter = viewpager?.adapter =
viewpager.adapter ?: ViewpagerAdapter(emptyList()) { isScrollingDown: Boolean -> viewpager.adapter ?: ViewpagerAdapter(emptyList(), { isScrollingDown: Boolean ->
if (isScrollingDown) { if (isScrollingDown) {
sort_fab?.shrink() sort_fab?.shrink()
} else { } else {
sort_fab?.extend() sort_fab?.extend()
} }
}) { searchClickCallback ->
println("SEARCH CLICK $searchClickCallback")
if (searchClickCallback.action == SEARCH_ACTION_LOAD) {
activity?.loadSearchResult(searchClickCallback.card)
}
} }
viewpager?.offscreenPageLimit = 2 viewpager?.offscreenPageLimit = 2

View file

@ -6,12 +6,14 @@ import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
import com.lagradost.cloudstream3.utils.AppUtils import com.lagradost.cloudstream3.utils.AppUtils
import kotlinx.android.synthetic.main.search_result_grid_expanded.view.* import kotlinx.android.synthetic.main.search_result_grid_expanded.view.*
class PageAdapter( class PageAdapter(
override val items: MutableList<LibraryItem>, override val items: MutableList<LibraryItem>,
val clickCallback: (SearchClickCallback) -> Unit
) : ) :
AppUtils.DiffAdapter<LibraryItem>(items) { AppUtils.DiffAdapter<LibraryItem>(items) {
@ -33,7 +35,7 @@ class PageAdapter(
inner class LibraryItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class LibraryItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: LibraryItem, position: Int) { fun bind(item: LibraryItem, position: Int) {
SearchResultBuilder.bind( SearchResultBuilder.bind(
{ println("CLICKED ${it.action}") }, this@PageAdapter.clickCallback,
item, item,
position, position,
itemView, itemView,

View file

@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchQuality import com.lagradost.cloudstream3.SearchQuality
import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import kotlinx.android.synthetic.main.library_viewpager_page.view.* import kotlinx.android.synthetic.main.library_viewpager_page.view.*
import me.xdrop.fuzzywuzzy.FuzzySearch import me.xdrop.fuzzywuzzy.FuzzySearch
@ -55,7 +56,11 @@ data class LibraryItem(
) : SearchResponse ) : SearchResponse
class ViewpagerAdapter(var pages: List<Page>, val scrollCallback: (isScrollingDown: Boolean) -> Unit) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { class ViewpagerAdapter(
var pages: List<Page>,
val scrollCallback: (isScrollingDown: Boolean) -> Unit,
val clickCallback: (SearchClickCallback) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return PageViewHolder( return PageViewHolder(
LayoutInflater.from(parent.context) LayoutInflater.from(parent.context)
@ -75,8 +80,9 @@ class ViewpagerAdapter(var pages: List<Page>, val scrollCallback: (isScrollingDo
RecyclerView.ViewHolder(itemViewTest) { RecyclerView.ViewHolder(itemViewTest) {
fun bind(page: Page) { fun bind(page: Page) {
if (itemViewTest.page_recyclerview?.adapter == null) { if (itemViewTest.page_recyclerview?.adapter == null) {
itemViewTest.page_recyclerview?.adapter = PageAdapter(page.items.toMutableList()) itemViewTest.page_recyclerview?.adapter = PageAdapter(page.items.toMutableList(), clickCallback)
itemView.page_recyclerview?.spanCount = this@PageViewHolder.itemView.context.getSpanCount() ?: 3 itemView.page_recyclerview?.spanCount =
this@PageViewHolder.itemView.context.getSpanCount() ?: 3
} else { } else {
(itemViewTest.page_recyclerview?.adapter as? PageAdapter)?.updateList(page.items) (itemViewTest.page_recyclerview?.adapter as? PageAdapter)?.updateList(page.items)
itemViewTest.page_recyclerview?.scrollToPosition(0) itemViewTest.page_recyclerview?.scrollToPosition(0)

View file

@ -14,6 +14,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.CommonActivity.getCastSession import com.lagradost.cloudstream3.CommonActivity.getCastSession
@ -1264,12 +1265,18 @@ class ResultViewModel2 : ViewModel() {
val realRecommendations = ArrayList<SearchResponse>() val realRecommendations = ArrayList<SearchResponse>()
// TODO: fix // TODO: fix
//val apiNames = listOf(GogoanimeProvider().name, NineAnimeProvider().name) val apiNames = apis.filter {
// meta.recommendations?.forEach { rec -> it.name.contains("gogoanime", true) ||
// apiNames.forEach { name -> it.name.contains("9anime", true)
// realRecommendations.add(rec.copy(apiName = name)) }.map {
// } it.name
// } }
meta.recommendations?.forEach { rec ->
apiNames.forEach { name ->
realRecommendations.add(rec.copy(apiName = name))
}
}
recommendations = recommendations?.union(realRecommendations)?.toList() recommendations = recommendations?.union(realRecommendations)?.toList()
?: realRecommendations ?: realRecommendations
@ -1913,7 +1920,7 @@ class ResultViewModel2 : ViewModel() {
val validUrlResource = safeApiCall { val validUrlResource = safeApiCall {
SyncRedirector.redirect( SyncRedirector.redirect(
url, url,
api.mainUrl api
) )
} }
// TODO: fix // TODO: fix

View file

@ -4,6 +4,7 @@ package com.lagradost.cloudstream3.utils
import android.util.Log import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.APIHolder.apis
//import com.lagradost.cloudstream3.animeproviders.AniflixProvider //import com.lagradost.cloudstream3.animeproviders.AniflixProvider
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
@ -79,16 +80,20 @@ object SyncUtil {
} }
suspend fun getUrlsFromId(id: String, type: String = "anilist"): List<String> { suspend fun getUrlsFromId(id: String, type: String = "anilist"): List<String> {
return arrayListOf() val url =
// val url = "https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/$type/anime/$id.json"
// "https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/$type/anime/$id.json" val response = app.get(url, cacheTime = 1, cacheUnit = TimeUnit.DAYS).parsed<SyncPage>()
// val response = app.get(url, cacheTime = 1, cacheUnit = TimeUnit.DAYS).parsed<SyncPage>() val pages = response.pages ?: return emptyList()
// val pages = response.pages ?: return emptyList() val current =
// val current = pages.gogoanime.values.union(pages.nineanime.values).union(pages.twistmoe.values).mapNotNull { it.url }.toMutableList() pages.gogoanime.values.union(pages.nineanime.values).union(pages.twistmoe.values)
// if(type == "anilist") { // TODO MAKE BETTER .mapNotNull { it.url }.toMutableList()
// current.add("${AniflixProvider().mainUrl}/anime/$id")
// } if (type == "anilist") { // TODO MAKE BETTER
// return current apis.filter { it.name.contains("Aniflix", ignoreCase = true) }.forEach {
current.add("${it.mainUrl}/anime/$id")
}
}
return current
} }
data class SyncPage( data class SyncPage(

View file

@ -301,6 +301,7 @@
<style name="TabNoCaps" parent="TextAppearance.Design.Tab"> <style name="TabNoCaps" parent="TextAppearance.Design.Tab">
<item name="textAllCaps">false</item> <item name="textAllCaps">false</item>
<item name="fontFamily">@font/google_sans</item>
</style> </style>
<style name="AppTextViewStyle" parent="android:Widget.TextView"> <style name="AppTextViewStyle" parent="android:Widget.TextView">