forked from recloudstream/cloudstream
sync fixes + UI
This commit is contained in:
parent
a933aa8493
commit
4447196ebc
13 changed files with 556 additions and 372 deletions
|
@ -693,13 +693,13 @@ interface LoadResponse {
|
||||||
val url: String
|
val url: String
|
||||||
val apiName: String
|
val apiName: String
|
||||||
val type: TvType
|
val type: TvType
|
||||||
val posterUrl: String?
|
var posterUrl: String?
|
||||||
val year: Int?
|
val year: Int?
|
||||||
val plot: String?
|
var plot: String?
|
||||||
val rating: Int? // 1-1000
|
var rating: Int? // 1-1000
|
||||||
val tags: List<String>?
|
var tags: List<String>?
|
||||||
var duration: Int? // in minutes
|
var duration: Int? // in minutes
|
||||||
val trailerUrl: String?
|
var trailerUrl: String?
|
||||||
var recommendations: List<SearchResponse>?
|
var recommendations: List<SearchResponse>?
|
||||||
var actors: List<ActorData>?
|
var actors: List<ActorData>?
|
||||||
var comingSoon: Boolean
|
var comingSoon: Boolean
|
||||||
|
|
|
@ -73,6 +73,8 @@ interface SyncAPI : OAuth2API {
|
||||||
var synonyms: List<String>? = null,
|
var synonyms: List<String>? = null,
|
||||||
var trailerUrl: String? = null,
|
var trailerUrl: String? = null,
|
||||||
var isAdult : Boolean? = null,
|
var isAdult : Boolean? = null,
|
||||||
|
var posterUrl: String? = null,
|
||||||
|
var backgroundPosterUrl : String? = null,
|
||||||
|
|
||||||
/** In unixtime */
|
/** In unixtime */
|
||||||
var startDate: Long? = null,
|
var startDate: Long? = null,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||||
class SyncRepo(private val repo: SyncAPI) {
|
class SyncRepo(private val repo: SyncAPI) {
|
||||||
val idPrefix = repo.idPrefix
|
val idPrefix = repo.idPrefix
|
||||||
val name = repo.name
|
val name = repo.name
|
||||||
|
val icon = repo.icon
|
||||||
|
|
||||||
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) }
|
||||||
|
|
|
@ -9,6 +9,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
|
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
|
import com.lagradost.cloudstream3.ErrorLoadingException
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.app
|
import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
@ -20,6 +21,7 @@ import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.unixTime
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
|
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
|
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
@ -86,7 +88,7 @@ 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 = id.toIntOrNull() ?: return null
|
val internalId = id.toIntOrNull() ?: return null
|
||||||
val season = getSeason(internalId)?.data?.Media ?: return null
|
val season = getSeason(internalId).data?.Media ?: throw ErrorLoadingException("No media")
|
||||||
|
|
||||||
return SyncAPI.SyncResult(
|
return SyncAPI.SyncResult(
|
||||||
season.id.toString(),
|
season.id.toString(),
|
||||||
|
@ -100,7 +102,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
synonyms = season.synonyms,
|
synonyms = season.synonyms,
|
||||||
isAdult = season.isAdult,
|
isAdult = season.isAdult,
|
||||||
totalEpisodes = season.episodes,
|
totalEpisodes = season.episodes,
|
||||||
//synopsis = season.
|
synopsis = season.description,
|
||||||
|
|
||||||
//TODO REST
|
//TODO REST
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -295,28 +298,42 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
return fromIntToAnimeStatus(aniListStatusString.indexOf(string))
|
return fromIntToAnimeStatus(aniListStatusString.indexOf(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun getSeason(id: Int): SeasonResponse {
|
||||||
private suspend fun getSeason(id: Int): SeasonResponse? {
|
|
||||||
val q = """
|
val q = """
|
||||||
query (${'$'}id: Int = $id) {
|
query (${'$'}id: Int = $id) {
|
||||||
Media (id: ${'$'}id, type: ANIME) {
|
Media (id: ${'$'}id, type: ANIME) {
|
||||||
id
|
id
|
||||||
idMal
|
idMal
|
||||||
coverImage
|
coverImage {
|
||||||
|
extraLarge
|
||||||
|
large
|
||||||
|
medium
|
||||||
|
color
|
||||||
|
}
|
||||||
duration
|
duration
|
||||||
episodes
|
episodes
|
||||||
genres
|
genres
|
||||||
synonyms
|
synonyms
|
||||||
averageScore
|
averageScore
|
||||||
isAdult
|
isAdult
|
||||||
trailer
|
description(asHtml: false)
|
||||||
|
trailer {
|
||||||
|
id
|
||||||
|
site
|
||||||
|
thumbnail
|
||||||
|
}
|
||||||
relations {
|
relations {
|
||||||
edges {
|
edges {
|
||||||
id
|
id
|
||||||
relationType(version: 2)
|
relationType(version: 2)
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
coverImage
|
coverImage {
|
||||||
|
extraLarge
|
||||||
|
large
|
||||||
|
medium
|
||||||
|
color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,19 +345,13 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
val data = app.post(
|
val data = app.post(
|
||||||
"https://graphql.anilist.co",
|
"https://graphql.anilist.co",
|
||||||
data = mapOf("query" to q),
|
data = mapOf("query" to q),
|
||||||
cacheTime = 0,
|
cacheTime = 0,
|
||||||
).text
|
).text
|
||||||
if (data == "") return null
|
|
||||||
return try {
|
return tryParseJson(data) ?: throw ErrorLoadingException("Error parsing $data")
|
||||||
mapper.readValue(data)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logError(e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -661,15 +672,13 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
val seasons = mutableListOf<SeasonResponse?>()
|
val seasons = mutableListOf<SeasonResponse?>()
|
||||||
suspend fun getSeasonRecursive(id: Int) {
|
suspend fun getSeasonRecursive(id: Int) {
|
||||||
val season = getSeason(id)
|
val season = getSeason(id)
|
||||||
if (season != null) {
|
seasons.add(season)
|
||||||
seasons.add(season)
|
if (season.data?.Media?.format?.startsWith("TV") == true) {
|
||||||
if (season.data?.Media?.format?.startsWith("TV") == true) {
|
season.data.Media.relations?.edges?.forEach {
|
||||||
season.data.Media.relations?.edges?.forEach {
|
if (it.node?.format != null) {
|
||||||
if (it.node?.format != null) {
|
if (it.relationType == "SEQUEL" && it.node.format.startsWith("TV")) {
|
||||||
if (it.relationType == "SEQUEL" && it.node.format.startsWith("TV")) {
|
getSeasonRecursive(it.node.id)
|
||||||
getSeasonRecursive(it.node.id)
|
return@forEach
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -701,8 +710,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
@JsonProperty("averageScore") val averageScore: Int?,
|
@JsonProperty("averageScore") val averageScore: Int?,
|
||||||
@JsonProperty("isAdult") val isAdult: Boolean?,
|
@JsonProperty("isAdult") val isAdult: Boolean?,
|
||||||
@JsonProperty("trailer") val trailer: MediaTrailer?,
|
@JsonProperty("trailer") val trailer: MediaTrailer?,
|
||||||
|
@JsonProperty("description") val description: String?,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class MediaTrailer(
|
data class MediaTrailer(
|
||||||
@JsonProperty("id") val id: String?,
|
@JsonProperty("id") val id: String?,
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.result
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
/*
|
||||||
|
class ImageAdapter(context: Context, val resource: Int) : ArrayAdapter<Int>(context, resource) {
|
||||||
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||||
|
val newConvertView = convertView ?: run {
|
||||||
|
val mInflater = context
|
||||||
|
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||||
|
mInflater.inflate(resource, null)
|
||||||
|
}
|
||||||
|
getItem(position)?.let { (newConvertView as? ImageView?)?.setImageResource(it) }
|
||||||
|
return newConvertView
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
class ImageAdapter(
|
||||||
|
val layout: Int,
|
||||||
|
) :
|
||||||
|
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
private val images: MutableList<Int> = mutableListOf()
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
return ImageViewHolder(
|
||||||
|
LayoutInflater.from(parent.context).inflate(layout, parent, false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
when (holder) {
|
||||||
|
is ImageViewHolder -> {
|
||||||
|
holder.bind(images[position])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return images.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemId(position: Int): Long {
|
||||||
|
return images[position].toLong()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateList(newList: List<Int>) {
|
||||||
|
val diffResult = DiffUtil.calculateDiff(
|
||||||
|
DiffCallback(this.images, newList)
|
||||||
|
)
|
||||||
|
|
||||||
|
images.clear()
|
||||||
|
images.addAll(newList)
|
||||||
|
|
||||||
|
diffResult.dispatchUpdatesTo(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImageViewHolder
|
||||||
|
constructor(itemView: View) :
|
||||||
|
RecyclerView.ViewHolder(itemView) {
|
||||||
|
fun bind(img: Int) {
|
||||||
|
(itemView as? ImageView?)?.setImageResource(img)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DiffCallback<T>(private val oldList: List<T>, private val newList: List<T>) :
|
||||||
|
DiffUtil.Callback() {
|
||||||
|
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||||
|
oldList[oldItemPosition] == newList[newItemPosition]
|
||||||
|
|
||||||
|
override fun getOldListSize() = oldList.size
|
||||||
|
|
||||||
|
override fun getNewListSize() = newList.size
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||||
|
oldList[oldItemPosition] == newList[newItemPosition]
|
||||||
|
}
|
|
@ -636,7 +636,6 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
val apiName = arguments?.getString("apiName") ?: return
|
val apiName = arguments?.getString("apiName") ?: return
|
||||||
startAction = arguments?.getInt("startAction") ?: START_ACTION_NORMAL
|
startAction = arguments?.getInt("startAction") ?: START_ACTION_NORMAL
|
||||||
startValue = arguments?.getInt("startValue") ?: START_VALUE_NORMAL
|
startValue = arguments?.getInt("startValue") ?: START_VALUE_NORMAL
|
||||||
|
|
||||||
syncModel.addFromUrl(url)
|
syncModel.addFromUrl(url)
|
||||||
|
|
||||||
val api = getApiFromName(apiName)
|
val api = getApiFromName(apiName)
|
||||||
|
@ -1198,17 +1197,26 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val imgAdapter = ImageAdapter(R.layout.result_mini_image)
|
||||||
|
result_mini_sync?.adapter = imgAdapter
|
||||||
|
|
||||||
observe(syncModel.synced) { list ->
|
observe(syncModel.synced) { list ->
|
||||||
result_sync_names?.text =
|
result_sync_names?.text =
|
||||||
list.filter { it.isSynced && it.hasAccount }.joinToString { it.name }
|
list.filter { it.isSynced && it.hasAccount }.joinToString { it.name }
|
||||||
|
|
||||||
|
val newList = list.filter { it.isSynced }
|
||||||
|
result_mini_sync?.isVisible = newList.isNotEmpty()
|
||||||
|
(result_mini_sync?.adapter as? ImageAdapter?)?.updateList(newList.map { it.icon })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var currentSyncProgress = 0
|
||||||
observe(syncModel.metadata) { meta ->
|
observe(syncModel.metadata) { meta ->
|
||||||
when (meta) {
|
when (meta) {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
val d = meta.value
|
val d = meta.value
|
||||||
result_sync_episodes?.max = (d.totalEpisodes ?: 0) * 1000
|
result_sync_episodes?.max = (d.totalEpisodes ?: 0) * 1000
|
||||||
|
result_sync_episodes?.progress = currentSyncProgress * 1000
|
||||||
|
|
||||||
normalSafeApiCall {
|
normalSafeApiCall {
|
||||||
val ctx = result_sync_max_episodes?.context
|
val ctx = result_sync_max_episodes?.context
|
||||||
result_sync_max_episodes?.text =
|
result_sync_max_episodes?.text =
|
||||||
|
@ -1218,6 +1226,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
ctx?.getString(R.string.sync_total_episodes_none)
|
ctx?.getString(R.string.sync_total_episodes_none)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
viewModel.setMeta(d)
|
||||||
}
|
}
|
||||||
is Resource.Loading -> {
|
is Resource.Loading -> {
|
||||||
result_sync_max_episodes?.text =
|
result_sync_max_episodes?.text =
|
||||||
|
@ -1249,6 +1258,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
result_sync_rating?.value = d.score?.toFloat() ?: 0.0f
|
result_sync_rating?.value = d.score?.toFloat() ?: 0.0f
|
||||||
result_sync_check?.setItemChecked(d.status + 1, true)
|
result_sync_check?.setItemChecked(d.status + 1, true)
|
||||||
val watchedEpisodes = d.watchedEpisodes ?: 0
|
val watchedEpisodes = d.watchedEpisodes ?: 0
|
||||||
|
currentSyncProgress = watchedEpisodes
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
result_sync_episodes?.setProgress(watchedEpisodes * 1000, true)
|
result_sync_episodes?.setProgress(watchedEpisodes * 1000, true)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1447,372 +1457,369 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
currentId = it
|
currentId = it
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(viewModel.resultResponse) { data ->
|
observe(viewModel.result) { data ->
|
||||||
when (data) {
|
when (data) {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
val d = data.value
|
val d = data.value
|
||||||
if (d is LoadResponse) {
|
if (d !is AnimeLoadResponse && result_episode_loading.isVisible) { // no episode loading when not anime
|
||||||
if (d !is AnimeLoadResponse && result_episode_loading.isVisible) { // no episode loading when not anime
|
result_episode_loading.isVisible = false
|
||||||
result_episode_loading.isVisible = false
|
}
|
||||||
|
|
||||||
|
updateVisStatus(2)
|
||||||
|
|
||||||
|
result_vpn?.text = when (api.vpnStatus) {
|
||||||
|
VPNStatus.MightBeNeeded -> getString(R.string.vpn_might_be_needed)
|
||||||
|
VPNStatus.Torrent -> getString(R.string.vpn_torrent)
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
result_vpn?.isGone = api.vpnStatus == VPNStatus.None
|
||||||
|
|
||||||
|
result_info?.text = when (api.providerType) {
|
||||||
|
ProviderType.MetaProvider -> getString(R.string.provider_info_meta)
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
result_info?.isVisible = api.providerType == ProviderType.MetaProvider
|
||||||
|
|
||||||
|
if (d.type.isEpisodeBased()) {
|
||||||
|
val ep = d as? TvSeriesLoadResponse
|
||||||
|
val epCount = ep?.episodes?.size ?: 1
|
||||||
|
if (epCount < 1) {
|
||||||
|
result_info?.text = getString(R.string.no_episodes_found)
|
||||||
|
result_info?.isVisible = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateVisStatus(2)
|
currentHeaderName = d.name
|
||||||
|
currentType = d.type
|
||||||
|
|
||||||
result_vpn?.text = when (api.vpnStatus) {
|
currentPoster = d.posterUrl
|
||||||
VPNStatus.MightBeNeeded -> getString(R.string.vpn_might_be_needed)
|
currentIsMovie = !d.isEpisodeBased()
|
||||||
VPNStatus.Torrent -> getString(R.string.vpn_torrent)
|
|
||||||
else -> ""
|
result_open_in_browser?.setOnClickListener {
|
||||||
|
val i = Intent(ACTION_VIEW)
|
||||||
|
i.data = Uri.parse(d.url)
|
||||||
|
try {
|
||||||
|
startActivity(i)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
}
|
}
|
||||||
result_vpn?.isGone = api.vpnStatus == VPNStatus.None
|
}
|
||||||
|
|
||||||
result_info?.text = when (api.providerType) {
|
result_search?.setOnClickListener {
|
||||||
ProviderType.MetaProvider -> getString(R.string.provider_info_meta)
|
QuickSearchFragment.pushSearch(activity, d.name)
|
||||||
else -> ""
|
}
|
||||||
|
|
||||||
|
result_share?.setOnClickListener {
|
||||||
|
try {
|
||||||
|
val i = Intent(ACTION_SEND)
|
||||||
|
i.type = "text/plain"
|
||||||
|
i.putExtra(EXTRA_SUBJECT, d.name)
|
||||||
|
i.putExtra(EXTRA_TEXT, d.url)
|
||||||
|
startActivity(createChooser(i, d.name))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
}
|
}
|
||||||
result_info?.isVisible = api.providerType == ProviderType.MetaProvider
|
}
|
||||||
|
|
||||||
if (d.type.isEpisodeBased()) {
|
val showStatus = when (d) {
|
||||||
val ep = d as? TvSeriesLoadResponse
|
is TvSeriesLoadResponse -> d.showStatus
|
||||||
val epCount = ep?.episodes?.size ?: 1
|
is AnimeLoadResponse -> d.showStatus
|
||||||
if (epCount < 1) {
|
else -> null
|
||||||
result_info?.text = getString(R.string.no_episodes_found)
|
}
|
||||||
result_info?.isVisible = true
|
|
||||||
|
setShow(showStatus)
|
||||||
|
setDuration(d.duration)
|
||||||
|
setYear(d.year)
|
||||||
|
setRating(d.rating)
|
||||||
|
setRecommendations(d.recommendations)
|
||||||
|
setActors(d.actors)
|
||||||
|
|
||||||
|
if (SettingsFragment.accountEnabled)
|
||||||
|
if (d is AnimeLoadResponse) {
|
||||||
|
if (
|
||||||
|
setMalSync(d.malId)
|
||||||
|
||
|
||||||
|
setAniListSync(d.anilistId)
|
||||||
|
) {
|
||||||
|
syncModel.updateMetaAndUser()
|
||||||
|
} else {
|
||||||
|
syncModel.addFromUrl(d.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentHeaderName = d.name
|
result_meta_site?.text = d.apiName
|
||||||
currentType = d.type
|
|
||||||
|
|
||||||
currentPoster = d.posterUrl
|
val posterImageLink = d.posterUrl
|
||||||
currentIsMovie = !d.isEpisodeBased()
|
if (!posterImageLink.isNullOrEmpty()) {
|
||||||
|
result_poster?.setImage(posterImageLink)
|
||||||
result_open_in_browser?.setOnClickListener {
|
result_poster_blur?.setImageBlur(posterImageLink, 10, 3)
|
||||||
val i = Intent(ACTION_VIEW)
|
//Full screen view of Poster image
|
||||||
i.data = Uri.parse(d.url)
|
result_poster_holder?.setOnClickListener {
|
||||||
try {
|
try {
|
||||||
startActivity(i)
|
context?.let { ctx ->
|
||||||
} catch (e: Exception) {
|
val bitmap = result_poster.drawable.toBitmap()
|
||||||
logError(e)
|
val sourceBuilder = AlertDialog.Builder(ctx)
|
||||||
}
|
sourceBuilder.setView(R.layout.result_poster)
|
||||||
}
|
|
||||||
|
|
||||||
result_search?.setOnClickListener {
|
val sourceDialog = sourceBuilder.create()
|
||||||
QuickSearchFragment.pushSearch(activity, d.name)
|
sourceDialog.show()
|
||||||
}
|
|
||||||
|
|
||||||
result_share?.setOnClickListener {
|
sourceDialog.findViewById<ImageView?>(R.id.imgPoster)
|
||||||
try {
|
?.apply {
|
||||||
val i = Intent(ACTION_SEND)
|
setImageBitmap(bitmap)
|
||||||
i.type = "text/plain"
|
setOnClickListener {
|
||||||
i.putExtra(EXTRA_SUBJECT, d.name)
|
sourceDialog.dismissSafe()
|
||||||
i.putExtra(EXTRA_TEXT, d.url)
|
|
||||||
startActivity(createChooser(i, d.name))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val showStatus = when (d) {
|
|
||||||
is TvSeriesLoadResponse -> d.showStatus
|
|
||||||
is AnimeLoadResponse -> d.showStatus
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
setShow(showStatus)
|
|
||||||
setDuration(d.duration)
|
|
||||||
setYear(d.year)
|
|
||||||
setRating(d.rating)
|
|
||||||
setRecommendations(d.recommendations)
|
|
||||||
setActors(d.actors)
|
|
||||||
|
|
||||||
if (SettingsFragment.accountEnabled)
|
|
||||||
if (d is AnimeLoadResponse) {
|
|
||||||
if (
|
|
||||||
setMalSync(d.malId)
|
|
||||||
||
|
|
||||||
setAniListSync(d.anilistId)
|
|
||||||
) {
|
|
||||||
syncModel.updateMetaAndUser()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result_meta_site?.text = d.apiName
|
|
||||||
|
|
||||||
val posterImageLink = d.posterUrl
|
|
||||||
if (!posterImageLink.isNullOrEmpty()) {
|
|
||||||
result_poster?.setImage(posterImageLink)
|
|
||||||
result_poster_blur?.setImageBlur(posterImageLink, 10, 3)
|
|
||||||
//Full screen view of Poster image
|
|
||||||
result_poster_holder?.setOnClickListener {
|
|
||||||
try {
|
|
||||||
context?.let { ctx ->
|
|
||||||
val bitmap = result_poster.drawable.toBitmap()
|
|
||||||
val sourceBuilder = AlertDialog.Builder(ctx)
|
|
||||||
sourceBuilder.setView(R.layout.result_poster)
|
|
||||||
|
|
||||||
val sourceDialog = sourceBuilder.create()
|
|
||||||
sourceDialog.show()
|
|
||||||
|
|
||||||
sourceDialog.findViewById<ImageView?>(R.id.imgPoster)
|
|
||||||
?.apply {
|
|
||||||
setImageBitmap(bitmap)
|
|
||||||
setOnClickListener {
|
|
||||||
sourceDialog.dismissSafe()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
logError(e)
|
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
result_poster?.setImageResource(R.drawable.default_cover)
|
|
||||||
result_poster_blur?.setImageResource(R.drawable.default_cover)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
result_poster?.setImageResource(R.drawable.default_cover)
|
||||||
|
result_poster_blur?.setImageResource(R.drawable.default_cover)
|
||||||
|
}
|
||||||
|
|
||||||
result_poster_holder?.visibility = VISIBLE
|
result_poster_holder?.visibility = VISIBLE
|
||||||
|
|
||||||
/*result_play_movie?.text =
|
/*result_play_movie?.text =
|
||||||
if (d.type == TvType.Torrent) getString(R.string.play_torrent_button) else getString(
|
if (d.type == TvType.Torrent) getString(R.string.play_torrent_button) else getString(
|
||||||
R.string.play_movie_button
|
R.string.play_movie_button
|
||||||
)*/
|
)*/
|
||||||
//result_plot_header?.text =
|
//result_plot_header?.text =
|
||||||
// if (d.type == TvType.Torrent) getString(R.string.torrent_plot) else getString(R.string.result_plot)
|
// if (d.type == TvType.Torrent) getString(R.string.torrent_plot) else getString(R.string.result_plot)
|
||||||
if (!d.plot.isNullOrEmpty()) {
|
if (!d.plot.isNullOrEmpty()) {
|
||||||
var syno = d.plot!!
|
var syno = d.plot!!
|
||||||
if (syno.length > MAX_SYNO_LENGH) {
|
if (syno.length > MAX_SYNO_LENGH) {
|
||||||
syno = syno.substring(0, MAX_SYNO_LENGH) + "..."
|
syno = syno.substring(0, MAX_SYNO_LENGH) + "..."
|
||||||
}
|
|
||||||
result_descript.setOnClickListener {
|
|
||||||
val builder: AlertDialog.Builder =
|
|
||||||
AlertDialog.Builder(requireContext())
|
|
||||||
builder.setMessage(d.plot)
|
|
||||||
.setTitle(if (d.type == TvType.Torrent) R.string.torrent_plot else R.string.result_plot)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
result_descript.text = syno
|
|
||||||
} else {
|
|
||||||
result_descript.text =
|
|
||||||
if (d.type == TvType.Torrent) getString(R.string.torrent_no_plot) else getString(
|
|
||||||
R.string.normal_no_plot
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
result_descript.setOnClickListener {
|
||||||
|
val builder: AlertDialog.Builder =
|
||||||
|
AlertDialog.Builder(requireContext())
|
||||||
|
builder.setMessage(d.plot)
|
||||||
|
.setTitle(if (d.type == TvType.Torrent) R.string.torrent_plot else R.string.result_plot)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
result_descript.text = syno
|
||||||
|
} else {
|
||||||
|
result_descript.text =
|
||||||
|
if (d.type == TvType.Torrent) getString(R.string.torrent_no_plot) else getString(
|
||||||
|
R.string.normal_no_plot
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
result_tag?.removeAllViews()
|
result_tag?.removeAllViews()
|
||||||
|
//result_tag_holder?.visibility = GONE
|
||||||
|
// result_status.visibility = GONE
|
||||||
|
|
||||||
|
d.comingSoon.let { soon ->
|
||||||
|
result_coming_soon?.isVisible = soon
|
||||||
|
result_data_holder?.isGone = soon
|
||||||
|
}
|
||||||
|
|
||||||
|
val tags = d.tags
|
||||||
|
if (tags.isNullOrEmpty()) {
|
||||||
//result_tag_holder?.visibility = GONE
|
//result_tag_holder?.visibility = GONE
|
||||||
// result_status.visibility = GONE
|
} else {
|
||||||
|
//result_tag_holder?.visibility = VISIBLE
|
||||||
|
|
||||||
d.comingSoon.let { soon ->
|
for ((index, tag) in tags.withIndex()) {
|
||||||
result_coming_soon?.isVisible = soon
|
val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
|
||||||
result_data_holder?.isGone = soon
|
val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
|
||||||
|
btt.text = tag
|
||||||
|
|
||||||
|
result_tag?.addView(viewBtt, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.type.isMovieType()) {
|
||||||
|
val hasDownloadSupport = api.hasDownloadSupport
|
||||||
|
lateFixDownloadButton(true)
|
||||||
|
|
||||||
|
result_play_movie?.setOnClickListener {
|
||||||
|
val card =
|
||||||
|
currentEpisodes?.firstOrNull() ?: return@setOnClickListener
|
||||||
|
handleAction(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
||||||
}
|
}
|
||||||
|
|
||||||
val tags = d.tags
|
result_play_movie?.setOnLongClickListener {
|
||||||
if (tags.isNullOrEmpty()) {
|
val card = currentEpisodes?.firstOrNull()
|
||||||
//result_tag_holder?.visibility = GONE
|
?: return@setOnLongClickListener true
|
||||||
} else {
|
handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
|
||||||
//result_tag_holder?.visibility = VISIBLE
|
return@setOnLongClickListener true
|
||||||
|
|
||||||
for ((index, tag) in tags.withIndex()) {
|
|
||||||
val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
|
|
||||||
val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
|
|
||||||
btt.text = tag
|
|
||||||
|
|
||||||
result_tag?.addView(viewBtt, index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.type.isMovieType()) {
|
result_download_movie?.setOnLongClickListener {
|
||||||
val hasDownloadSupport = api.hasDownloadSupport
|
val card = currentEpisodes?.firstOrNull()
|
||||||
lateFixDownloadButton(true)
|
?: return@setOnLongClickListener true
|
||||||
|
handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
|
||||||
result_play_movie?.setOnClickListener {
|
return@setOnLongClickListener true
|
||||||
val card =
|
}
|
||||||
currentEpisodes?.firstOrNull() ?: return@setOnClickListener
|
|
||||||
handleAction(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
|
|
||||||
}
|
|
||||||
|
|
||||||
result_play_movie?.setOnLongClickListener {
|
|
||||||
val card = currentEpisodes?.firstOrNull()
|
|
||||||
?: return@setOnLongClickListener true
|
|
||||||
handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
|
|
||||||
return@setOnLongClickListener true
|
|
||||||
}
|
|
||||||
|
|
||||||
result_download_movie?.setOnLongClickListener {
|
|
||||||
val card = currentEpisodes?.firstOrNull()
|
|
||||||
?: return@setOnLongClickListener true
|
|
||||||
handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
|
|
||||||
return@setOnLongClickListener true
|
|
||||||
}
|
|
||||||
|
|
||||||
// result_options.setOnClickListener {
|
// result_options.setOnClickListener {
|
||||||
// val card = currentEpisodes?.first() ?: return@setOnClickListener
|
// val card = currentEpisodes?.first() ?: return@setOnClickListener
|
||||||
// handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
|
// handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
|
||||||
// }
|
// }
|
||||||
|
|
||||||
result_download_movie?.visibility =
|
result_download_movie?.visibility =
|
||||||
if (hasDownloadSupport) VISIBLE else GONE
|
if (hasDownloadSupport) VISIBLE else GONE
|
||||||
if (hasDownloadSupport) {
|
if (hasDownloadSupport) {
|
||||||
val localId = d.getId()
|
val localId = d.getId()
|
||||||
val file =
|
val file =
|
||||||
VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(
|
VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
localId
|
localId
|
||||||
)
|
)
|
||||||
downloadButton?.dispose()
|
downloadButton?.dispose()
|
||||||
downloadButton = EasyDownloadButton()
|
downloadButton = EasyDownloadButton()
|
||||||
downloadButton?.setUpMoreButton(
|
downloadButton?.setUpMoreButton(
|
||||||
file?.fileLength,
|
file?.fileLength,
|
||||||
file?.totalBytes,
|
file?.totalBytes,
|
||||||
result_movie_progress_downloaded,
|
result_movie_progress_downloaded,
|
||||||
result_movie_download_icon,
|
result_movie_download_icon,
|
||||||
result_movie_download_text,
|
result_movie_download_text,
|
||||||
result_movie_download_text_precentage,
|
result_movie_download_text_precentage,
|
||||||
result_download_movie,
|
result_download_movie,
|
||||||
true,
|
true,
|
||||||
VideoDownloadHelper.DownloadEpisodeCached(
|
VideoDownloadHelper.DownloadEpisodeCached(
|
||||||
d.name,
|
d.name,
|
||||||
d.posterUrl,
|
d.posterUrl,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
localId,
|
localId,
|
||||||
localId,
|
localId,
|
||||||
d.rating,
|
d.rating,
|
||||||
d.plot,
|
d.plot,
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
)
|
)
|
||||||
) { downloadClickEvent ->
|
) { downloadClickEvent ->
|
||||||
if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
|
if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
|
||||||
currentEpisodes?.firstOrNull()?.let { episode ->
|
currentEpisodes?.firstOrNull()?.let { episode ->
|
||||||
handleAction(
|
handleAction(
|
||||||
EpisodeClickEvent(
|
EpisodeClickEvent(
|
||||||
ACTION_DOWNLOAD_EPISODE,
|
ACTION_DOWNLOAD_EPISODE,
|
||||||
ResultEpisode(
|
ResultEpisode(
|
||||||
d.name,
|
d.name,
|
||||||
d.name,
|
d.name,
|
||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
episode.data,
|
episode.data,
|
||||||
d.apiName,
|
d.apiName,
|
||||||
localId,
|
localId,
|
||||||
0,
|
0,
|
||||||
0L,
|
0L,
|
||||||
0L,
|
0L,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
d.type,
|
d.type,
|
||||||
localId,
|
localId,
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handleDownloadClick(
|
|
||||||
activity,
|
|
||||||
currentHeaderName,
|
|
||||||
downloadClickEvent
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
handleDownloadClick(
|
||||||
result_download_movie?.setOnLongClickListener {
|
activity,
|
||||||
val card =
|
currentHeaderName,
|
||||||
currentEpisodes?.firstOrNull()
|
downloadClickEvent
|
||||||
?: return@setOnLongClickListener false
|
|
||||||
handleAction(EpisodeClickEvent(ACTION_DOWNLOAD_MIRROR, card))
|
|
||||||
return@setOnLongClickListener true
|
|
||||||
}
|
|
||||||
|
|
||||||
/*downloadButton?.setUpMaterialButton(
|
|
||||||
file?.fileLength,
|
|
||||||
file?.totalBytes,
|
|
||||||
result_movie_progress_downloaded,
|
|
||||||
result_download_movie,
|
|
||||||
null, //result_movie_text_progress
|
|
||||||
VideoDownloadHelper.DownloadEpisodeCached(
|
|
||||||
d.name,
|
|
||||||
d.posterUrl,
|
|
||||||
0,
|
|
||||||
null,
|
|
||||||
localId,
|
|
||||||
localId,
|
|
||||||
d.rating,
|
|
||||||
d.plot,
|
|
||||||
System.currentTimeMillis(),
|
|
||||||
)
|
)
|
||||||
) { downloadClickEvent ->
|
}
|
||||||
if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
|
}
|
||||||
currentEpisodes?.firstOrNull()?.let { episode ->
|
|
||||||
handleAction(
|
result_download_movie?.setOnLongClickListener {
|
||||||
EpisodeClickEvent(
|
val card =
|
||||||
ACTION_DOWNLOAD_EPISODE,
|
currentEpisodes?.firstOrNull()
|
||||||
ResultEpisode(
|
?: return@setOnLongClickListener false
|
||||||
d.name,
|
handleAction(EpisodeClickEvent(ACTION_DOWNLOAD_MIRROR, card))
|
||||||
d.name,
|
return@setOnLongClickListener true
|
||||||
null,
|
}
|
||||||
0,
|
|
||||||
null,
|
/*downloadButton?.setUpMaterialButton(
|
||||||
episode.data,
|
file?.fileLength,
|
||||||
d.apiName,
|
file?.totalBytes,
|
||||||
localId,
|
result_movie_progress_downloaded,
|
||||||
0,
|
result_download_movie,
|
||||||
0L,
|
null, //result_movie_text_progress
|
||||||
0L,
|
VideoDownloadHelper.DownloadEpisodeCached(
|
||||||
null,
|
d.name,
|
||||||
null,
|
d.posterUrl,
|
||||||
null,
|
0,
|
||||||
d.type,
|
null,
|
||||||
localId,
|
localId,
|
||||||
)
|
localId,
|
||||||
|
d.rating,
|
||||||
|
d.plot,
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
)
|
||||||
|
) { downloadClickEvent ->
|
||||||
|
if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
|
||||||
|
currentEpisodes?.firstOrNull()?.let { episode ->
|
||||||
|
handleAction(
|
||||||
|
EpisodeClickEvent(
|
||||||
|
ACTION_DOWNLOAD_EPISODE,
|
||||||
|
ResultEpisode(
|
||||||
|
d.name,
|
||||||
|
d.name,
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
episode.data,
|
||||||
|
d.apiName,
|
||||||
|
localId,
|
||||||
|
0,
|
||||||
|
0L,
|
||||||
|
0L,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
d.type,
|
||||||
|
localId,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handleDownloadClick(
|
|
||||||
activity,
|
|
||||||
currentHeaderName,
|
|
||||||
downloadClickEvent
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}*/
|
} else {
|
||||||
}
|
handleDownloadClick(
|
||||||
} else {
|
activity,
|
||||||
lateFixDownloadButton(false)
|
currentHeaderName,
|
||||||
}
|
downloadClickEvent
|
||||||
|
)
|
||||||
context?.getString(
|
}
|
||||||
when (d.type) {
|
}*/
|
||||||
TvType.TvSeries -> R.string.tv_series_singular
|
|
||||||
TvType.Anime -> R.string.anime_singular
|
|
||||||
TvType.OVA -> R.string.ova_singular
|
|
||||||
TvType.AnimeMovie -> R.string.movies_singular
|
|
||||||
TvType.Cartoon -> R.string.cartoons_singular
|
|
||||||
TvType.Documentary -> R.string.documentaries_singular
|
|
||||||
TvType.Movie -> R.string.movies_singular
|
|
||||||
TvType.Torrent -> R.string.torrent_singular
|
|
||||||
TvType.AsianDrama -> R.string.asian_drama_singular
|
|
||||||
}
|
|
||||||
)?.let {
|
|
||||||
result_meta_type?.text = it
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
when (d) {
|
|
||||||
is AnimeLoadResponse -> {
|
|
||||||
|
|
||||||
// val preferEnglish = true
|
|
||||||
//val titleName = (if (preferEnglish) d.engName else d.japName) ?: d.name
|
|
||||||
val titleName = d.name
|
|
||||||
result_title.text = titleName
|
|
||||||
//result_toolbar.title = titleName
|
|
||||||
}
|
|
||||||
else -> result_title.text = d.name
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updateVisStatus(1)
|
lateFixDownloadButton(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
context?.getString(
|
||||||
|
when (d.type) {
|
||||||
|
TvType.TvSeries -> R.string.tv_series_singular
|
||||||
|
TvType.Anime -> R.string.anime_singular
|
||||||
|
TvType.OVA -> R.string.ova_singular
|
||||||
|
TvType.AnimeMovie -> R.string.movies_singular
|
||||||
|
TvType.Cartoon -> R.string.cartoons_singular
|
||||||
|
TvType.Documentary -> R.string.documentaries_singular
|
||||||
|
TvType.Movie -> R.string.movies_singular
|
||||||
|
TvType.Torrent -> R.string.torrent_singular
|
||||||
|
TvType.AsianDrama -> R.string.asian_drama_singular
|
||||||
|
}
|
||||||
|
)?.let {
|
||||||
|
result_meta_type?.text = it
|
||||||
|
}
|
||||||
|
|
||||||
|
when (d) {
|
||||||
|
is AnimeLoadResponse -> {
|
||||||
|
|
||||||
|
// val preferEnglish = true
|
||||||
|
//val titleName = (if (preferEnglish) d.engName else d.japName) ?: d.name
|
||||||
|
val titleName = d.name
|
||||||
|
result_title.text = titleName
|
||||||
|
//result_toolbar.title = titleName
|
||||||
|
}
|
||||||
|
else -> result_title.text = d.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Resource.Failure -> {
|
is Resource.Failure -> {
|
||||||
|
@ -1873,7 +1880,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
|
||||||
it.context?.openBrowser(tempUrl)
|
it.context?.openBrowser(tempUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (restart || viewModel.resultResponse.value == null) {
|
if (restart || viewModel.result.value == null) {
|
||||||
//viewModel.clear()
|
//viewModel.clear()
|
||||||
viewModel.load(tempUrl, apiName, showFillers)
|
viewModel.load(tempUrl, apiName, showFillers)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.lagradost.cloudstream3.ui.result
|
package com.lagradost.cloudstream3.ui.result
|
||||||
|
|
||||||
import android.content.Context
|
import android.util.Log
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.APIHolder.getId
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository
|
import com.lagradost.cloudstream3.ui.APIRepository
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.ui.player.IGenerator
|
import com.lagradost.cloudstream3.ui.player.IGenerator
|
||||||
|
@ -43,7 +44,7 @@ class ResultViewModel : ViewModel() {
|
||||||
private var repo: APIRepository? = null
|
private var repo: APIRepository? = null
|
||||||
private var generator: IGenerator? = null
|
private var generator: IGenerator? = null
|
||||||
|
|
||||||
private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData()
|
private val _resultResponse: MutableLiveData<Resource<LoadResponse>> = MutableLiveData()
|
||||||
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
|
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
|
||||||
private val episodeById: MutableLiveData<HashMap<Int, Int>> =
|
private val episodeById: MutableLiveData<HashMap<Int, Int>> =
|
||||||
MutableLiveData() // lookup by ID to get Index
|
MutableLiveData() // lookup by ID to get Index
|
||||||
|
@ -55,7 +56,8 @@ class ResultViewModel : ViewModel() {
|
||||||
private val selectedRangeInt: MutableLiveData<Int> = MutableLiveData()
|
private val selectedRangeInt: MutableLiveData<Int> = MutableLiveData()
|
||||||
val rangeOptions: LiveData<List<String>> = _rangeOptions
|
val rangeOptions: LiveData<List<String>> = _rangeOptions
|
||||||
|
|
||||||
val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse
|
val result: LiveData<Resource<LoadResponse>> get() = _resultResponse
|
||||||
|
|
||||||
val episodes: LiveData<List<ResultEpisode>> get() = _episodes
|
val episodes: LiveData<List<ResultEpisode>> get() = _episodes
|
||||||
val publicEpisodes: LiveData<Resource<List<ResultEpisode>>> get() = _publicEpisodes
|
val publicEpisodes: LiveData<Resource<List<ResultEpisode>>> get() = _publicEpisodes
|
||||||
val publicEpisodesCount: LiveData<Int> get() = _publicEpisodesCount
|
val publicEpisodesCount: LiveData<Int> get() = _publicEpisodesCount
|
||||||
|
@ -106,6 +108,41 @@ class ResultViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG = "RVM"
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastMeta: SyncAPI.SyncResult? = null
|
||||||
|
private fun applyMeta(resp: LoadResponse, meta: SyncAPI.SyncResult?): LoadResponse {
|
||||||
|
if (meta == null) return resp
|
||||||
|
lastMeta = meta
|
||||||
|
return resp.apply {
|
||||||
|
Log.i(TAG, "applyMeta")
|
||||||
|
|
||||||
|
duration = duration ?: meta.duration
|
||||||
|
rating = rating ?: meta.publicScore
|
||||||
|
tags = tags ?: meta.genres
|
||||||
|
plot = if (plot.isNullOrBlank()) meta.synopsis else plot
|
||||||
|
trailerUrl = trailerUrl ?: meta.trailerUrl
|
||||||
|
posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl
|
||||||
|
actors = actors ?: meta.actors?.map {
|
||||||
|
ActorData(
|
||||||
|
Actor(
|
||||||
|
name = it.name,
|
||||||
|
image = it.posterUrl
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setMeta(meta: SyncAPI.SyncResult) {
|
||||||
|
Log.i(TAG, "setMeta")
|
||||||
|
(result.value as? Resource.Success<LoadResponse>?)?.value?.let { resp ->
|
||||||
|
_resultResponse.postValue(Resource.Success(applyMeta(resp, meta)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadWatchStatus(localId: Int? = null) {
|
private fun loadWatchStatus(localId: Int? = null) {
|
||||||
val currentId = localId ?: id.value ?: return
|
val currentId = localId ?: id.value ?: return
|
||||||
val currentWatch = getResultWatchState(currentId)
|
val currentWatch = getResultWatchState(currentId)
|
||||||
|
@ -289,7 +326,7 @@ class ResultViewModel : ViewModel() {
|
||||||
|
|
||||||
when (data) {
|
when (data) {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
val d = data.value
|
val d = applyMeta(data.value, lastMeta)
|
||||||
page.postValue(d)
|
page.postValue(d)
|
||||||
val mainId = d.getId()
|
val mainId = d.getId()
|
||||||
id.postValue(mainId)
|
id.postValue(mainId)
|
||||||
|
|
|
@ -18,6 +18,7 @@ data class CurrentSynced(
|
||||||
val idPrefix: String,
|
val idPrefix: String,
|
||||||
val isSynced: Boolean,
|
val isSynced: Boolean,
|
||||||
val hasAccount: Boolean,
|
val hasAccount: Boolean,
|
||||||
|
val icon : Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
class SyncViewModel : ViewModel() {
|
class SyncViewModel : ViewModel() {
|
||||||
|
@ -48,7 +49,8 @@ class SyncViewModel : ViewModel() {
|
||||||
it.name,
|
it.name,
|
||||||
it.idPrefix,
|
it.idPrefix,
|
||||||
syncIds.containsKey(it.idPrefix),
|
syncIds.containsKey(it.idPrefix),
|
||||||
it.hasAccount()
|
it.hasAccount(),
|
||||||
|
it.icon,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,8 +69,13 @@ class SyncViewModel : ViewModel() {
|
||||||
updateSynced()
|
updateSynced()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasAddedFromUrl : HashSet<String> = hashSetOf()
|
||||||
|
|
||||||
fun addFromUrl(url: String?) = viewModelScope.launch {
|
fun addFromUrl(url: String?) = viewModelScope.launch {
|
||||||
|
if(url == null || hasAddedFromUrl.contains(url)) return@launch
|
||||||
SyncUtil.getIdsFromUrl(url)?.let { (malId, aniListId) ->
|
SyncUtil.getIdsFromUrl(url)?.let { (malId, aniListId) ->
|
||||||
|
hasAddedFromUrl.add(url)
|
||||||
|
|
||||||
setMalId(malId)
|
setMalId(malId)
|
||||||
setAniListId(aniListId)
|
setAniListId(aniListId)
|
||||||
if (malId != null || aniListId != null) {
|
if (malId != null || aniListId != null) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.lagradost.cloudstream3.utils
|
package com.lagradost.cloudstream3.utils
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import com.lagradost.cloudstream3.app
|
import com.lagradost.cloudstream3.app
|
||||||
|
@ -14,6 +15,8 @@ object SyncUtil {
|
||||||
Regex("""(twist\.moe)/a/([^/?]*)"""),
|
Regex("""(twist\.moe)/a/([^/?]*)"""),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private const val TAG = "SNC"
|
||||||
|
|
||||||
private const val GOGOANIME = "Gogoanime"
|
private const val GOGOANIME = "Gogoanime"
|
||||||
private const val NINE_ANIME = "9anime"
|
private const val NINE_ANIME = "9anime"
|
||||||
private const val TWIST_MOE = "Twistmoe"
|
private const val TWIST_MOE = "Twistmoe"
|
||||||
|
@ -28,6 +31,7 @@ object SyncUtil {
|
||||||
|
|
||||||
suspend fun getIdsFromUrl(url: String?): Pair<String?, String?>? {
|
suspend fun getIdsFromUrl(url: String?): Pair<String?, String?>? {
|
||||||
if (url == null) return null
|
if (url == null) return null
|
||||||
|
Log.i(TAG, "getIdsFromUrl $url")
|
||||||
|
|
||||||
for (regex in regexs) {
|
for (regex in regexs) {
|
||||||
regex.find(url)?.let { match ->
|
regex.find(url)?.let { match ->
|
||||||
|
@ -51,6 +55,7 @@ object SyncUtil {
|
||||||
slug: String,
|
slug: String,
|
||||||
site: String = "GogoanimeGogoanime"
|
site: String = "GogoanimeGogoanime"
|
||||||
): Pair<String?, String?>? {
|
): Pair<String?, String?>? {
|
||||||
|
Log.i(TAG, "getIdsFromSlug $slug $site")
|
||||||
try {
|
try {
|
||||||
//Gogoanime, Twistmoe and 9anime
|
//Gogoanime, Twistmoe and 9anime
|
||||||
val url =
|
val url =
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="20dp"
|
||||||
android:height="24dp"
|
android:height="20dp"
|
||||||
android:viewportWidth="172"
|
android:viewportWidth="172"
|
||||||
android:viewportHeight="172"
|
android:viewportHeight="172"
|
||||||
android:tint="?attr/white"
|
android:tint="?attr/white"
|
||||||
|
|
|
@ -375,6 +375,7 @@
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/result_bookmark_button"
|
android:id="@+id/result_bookmark_button"
|
||||||
style="@style/BlackButton"
|
style="@style/BlackButton"
|
||||||
|
|
|
@ -12,22 +12,45 @@
|
||||||
android:paddingEnd="@dimen/result_padding"
|
android:paddingEnd="@dimen/result_padding"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<ImageView
|
||||||
|
android:nextFocusDown="@id/result_bookmark_button"
|
||||||
|
android:nextFocusRight="@id/result_share"
|
||||||
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
|
||||||
<ImageView
|
android:id="@+id/result_back"
|
||||||
android:nextFocusDown="@id/result_bookmark_button"
|
android:clickable="true"
|
||||||
android:nextFocusRight="@id/result_share"
|
android:focusable="true"
|
||||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
|
||||||
|
|
||||||
android:id="@+id/result_back"
|
android:layout_width="30dp"
|
||||||
android:clickable="true"
|
android:layout_height="30dp"
|
||||||
android:focusable="true"
|
android:layout_gravity="center_vertical|start"
|
||||||
|
android:src="@drawable/ic_baseline_arrow_back_24"
|
||||||
android:layout_width="30dp"
|
android:contentDescription="@string/go_back"
|
||||||
android:layout_height="30dp"
|
app:tint="?attr/white" />
|
||||||
android:layout_gravity="center_vertical|start"
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:src="@drawable/ic_baseline_arrow_back_24"
|
android:paddingStart="10dp"
|
||||||
android:contentDescription="@string/go_back"
|
android:paddingEnd="10dp"
|
||||||
app:tint="?attr/white" />
|
android:id="@+id/result_mini_sync"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:descendantFocusability="afterDescendants"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fadingEdge="horizontal"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:itemCount="2"
|
||||||
|
tools:listitem="@layout/result_mini_image" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:gravity="end"
|
android:gravity="end"
|
||||||
|
|
10
app/src/main/res/layout/result_mini_image.xml
Normal file
10
app/src/main/res/layout/result_mini_image.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="35dp"
|
||||||
|
android:layout_height="35dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
tools:src="@drawable/ic_anilist_icon"
|
||||||
|
app:tint="?attr/white">
|
||||||
|
</ImageView>
|
Loading…
Reference in a new issue