forked from recloudstream/cloudstream
backend fixes for opening from sync urls
This commit is contained in:
parent
05dc032df6
commit
67a1c447ae
11 changed files with 134 additions and 59 deletions
|
@ -10,15 +10,14 @@ import com.fasterxml.jackson.annotation.JsonProperty
|
|||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||
import com.lagradost.cloudstream3.metaproviders.SyncIdName
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
|
||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
|
||||
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
||||
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.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
import okhttp3.Interceptor
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
@ -402,6 +401,20 @@ abstract class MainAPI {
|
|||
open val hasMainPage = 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(
|
||||
TvType.Movie,
|
||||
TvType.TvSeries,
|
||||
|
@ -412,7 +425,6 @@ abstract class MainAPI {
|
|||
|
||||
open val vpnStatus = VPNStatus.None
|
||||
open val providerType = ProviderType.DirectProvider
|
||||
|
||||
open val mainPage = listOf(MainPageData("", ""))
|
||||
|
||||
@WorkerThread
|
||||
|
@ -471,6 +483,14 @@ abstract class MainAPI {
|
|||
open fun getVideoInterceptor(extractorLink: ExtractorLink): Interceptor? {
|
||||
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*/
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -602,7 +602,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
|||
// English title first
|
||||
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,
|
||||
this.progress,
|
||||
this.media.episodes,
|
||||
|
|
|
@ -387,7 +387,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
|||
fun toLibraryItem(): LibraryItem {
|
||||
return LibraryItem(
|
||||
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?.num_episodes_watched,
|
||||
this.node.num_episodes,
|
||||
|
|
|
@ -12,6 +12,8 @@ import com.google.android.material.tabs.TabLayoutMediator
|
|||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
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.UIHelper.fixPaddingStatusbar
|
||||
import kotlinx.android.synthetic.main.fragment_library.*
|
||||
|
@ -68,12 +70,17 @@ class LibraryFragment : Fragment() {
|
|||
|
||||
viewpager?.setPageTransformer(LibraryScrollTransformer())
|
||||
viewpager?.adapter =
|
||||
viewpager.adapter ?: ViewpagerAdapter(emptyList()) { isScrollingDown: Boolean ->
|
||||
viewpager.adapter ?: ViewpagerAdapter(emptyList(), { isScrollingDown: Boolean ->
|
||||
if (isScrollingDown) {
|
||||
sort_fab?.shrink()
|
||||
} else {
|
||||
sort_fab?.extend()
|
||||
}
|
||||
}) { searchClickCallback ->
|
||||
println("SEARCH CLICK $searchClickCallback")
|
||||
if (searchClickCallback.action == SEARCH_ACTION_LOAD) {
|
||||
activity?.loadSearchResult(searchClickCallback.card)
|
||||
}
|
||||
}
|
||||
viewpager?.offscreenPageLimit = 2
|
||||
|
||||
|
|
|
@ -6,12 +6,14 @@ import android.view.ViewGroup
|
|||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
|
||||
import com.lagradost.cloudstream3.utils.AppUtils
|
||||
import kotlinx.android.synthetic.main.search_result_grid_expanded.view.*
|
||||
|
||||
class PageAdapter(
|
||||
override val items: MutableList<LibraryItem>,
|
||||
val clickCallback: (SearchClickCallback) -> Unit
|
||||
) :
|
||||
AppUtils.DiffAdapter<LibraryItem>(items) {
|
||||
|
||||
|
@ -33,7 +35,7 @@ class PageAdapter(
|
|||
inner class LibraryItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
fun bind(item: LibraryItem, position: Int) {
|
||||
SearchResultBuilder.bind(
|
||||
{ println("CLICKED ${it.action}") },
|
||||
this@PageAdapter.clickCallback,
|
||||
item,
|
||||
position,
|
||||
itemView,
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.R
|
|||
import com.lagradost.cloudstream3.SearchQuality
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import com.lagradost.cloudstream3.TvType
|
||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
||||
import kotlinx.android.synthetic.main.library_viewpager_page.view.*
|
||||
import me.xdrop.fuzzywuzzy.FuzzySearch
|
||||
|
@ -55,7 +56,11 @@ data class LibraryItem(
|
|||
) : 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 {
|
||||
return PageViewHolder(
|
||||
LayoutInflater.from(parent.context)
|
||||
|
@ -75,8 +80,9 @@ class ViewpagerAdapter(var pages: List<Page>, val scrollCallback: (isScrollingDo
|
|||
RecyclerView.ViewHolder(itemViewTest) {
|
||||
fun bind(page: Page) {
|
||||
if (itemViewTest.page_recyclerview?.adapter == null) {
|
||||
itemViewTest.page_recyclerview?.adapter = PageAdapter(page.items.toMutableList())
|
||||
itemView.page_recyclerview?.spanCount = this@PageViewHolder.itemView.context.getSpanCount() ?: 3
|
||||
itemViewTest.page_recyclerview?.adapter = PageAdapter(page.items.toMutableList(), clickCallback)
|
||||
itemView.page_recyclerview?.spanCount =
|
||||
this@PageViewHolder.itemView.context.getSpanCount() ?: 3
|
||||
} else {
|
||||
(itemViewTest.page_recyclerview?.adapter as? PageAdapter)?.updateList(page.items)
|
||||
itemViewTest.page_recyclerview?.scrollToPosition(0)
|
||||
|
|
|
@ -14,6 +14,7 @@ import androidx.lifecycle.MutableLiveData
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
import com.lagradost.cloudstream3.APIHolder.getId
|
||||
import com.lagradost.cloudstream3.APIHolder.unixTime
|
||||
import com.lagradost.cloudstream3.CommonActivity.getCastSession
|
||||
|
@ -1264,12 +1265,18 @@ class ResultViewModel2 : ViewModel() {
|
|||
|
||||
val realRecommendations = ArrayList<SearchResponse>()
|
||||
// TODO: fix
|
||||
//val apiNames = listOf(GogoanimeProvider().name, NineAnimeProvider().name)
|
||||
// meta.recommendations?.forEach { rec ->
|
||||
// apiNames.forEach { name ->
|
||||
// realRecommendations.add(rec.copy(apiName = name))
|
||||
// }
|
||||
// }
|
||||
val apiNames = apis.filter {
|
||||
it.name.contains("gogoanime", true) ||
|
||||
it.name.contains("9anime", true)
|
||||
}.map {
|
||||
it.name
|
||||
}
|
||||
|
||||
meta.recommendations?.forEach { rec ->
|
||||
apiNames.forEach { name ->
|
||||
realRecommendations.add(rec.copy(apiName = name))
|
||||
}
|
||||
}
|
||||
|
||||
recommendations = recommendations?.union(realRecommendations)?.toList()
|
||||
?: realRecommendations
|
||||
|
@ -1913,7 +1920,7 @@ class ResultViewModel2 : ViewModel() {
|
|||
val validUrlResource = safeApiCall {
|
||||
SyncRedirector.redirect(
|
||||
url,
|
||||
api.mainUrl
|
||||
api
|
||||
)
|
||||
}
|
||||
// TODO: fix
|
||||
|
|
|
@ -4,6 +4,7 @@ package com.lagradost.cloudstream3.utils
|
|||
|
||||
import android.util.Log
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
//import com.lagradost.cloudstream3.animeproviders.AniflixProvider
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
|
@ -78,17 +79,21 @@ object SyncUtil {
|
|||
return null
|
||||
}
|
||||
|
||||
suspend fun getUrlsFromId(id: String, type: String = "anilist") : List<String> {
|
||||
return arrayListOf()
|
||||
// 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).parsed<SyncPage>()
|
||||
// val pages = response.pages ?: return emptyList()
|
||||
// val current = pages.gogoanime.values.union(pages.nineanime.values).union(pages.twistmoe.values).mapNotNull { it.url }.toMutableList()
|
||||
// if(type == "anilist") { // TODO MAKE BETTER
|
||||
// current.add("${AniflixProvider().mainUrl}/anime/$id")
|
||||
// }
|
||||
// return current
|
||||
suspend fun getUrlsFromId(id: String, type: String = "anilist"): List<String> {
|
||||
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).parsed<SyncPage>()
|
||||
val pages = response.pages ?: return emptyList()
|
||||
val current =
|
||||
pages.gogoanime.values.union(pages.nineanime.values).union(pages.twistmoe.values)
|
||||
.mapNotNull { it.url }.toMutableList()
|
||||
|
||||
if (type == "anilist") { // TODO MAKE BETTER
|
||||
apis.filter { it.name.contains("Aniflix", ignoreCase = true) }.forEach {
|
||||
current.add("${it.mainUrl}/anime/$id")
|
||||
}
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
data class SyncPage(
|
||||
|
|
|
@ -301,6 +301,7 @@
|
|||
|
||||
<style name="TabNoCaps" parent="TextAppearance.Design.Tab">
|
||||
<item name="textAllCaps">false</item>
|
||||
<item name="fontFamily">@font/google_sans</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTextViewStyle" parent="android:Widget.TextView">
|
||||
|
|
Loading…
Reference in a new issue