fixed sync stuff and tv stuff

This commit is contained in:
LagradOst 2022-04-03 23:41:28 +02:00
parent 10c945f497
commit 7cbdc1fc6c
24 changed files with 209 additions and 160 deletions

View file

@ -13,6 +13,8 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.lagradost.cloudstream3.animeproviders.* import com.lagradost.cloudstream3.animeproviders.*
import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider
import com.lagradost.cloudstream3.movieproviders.* import com.lagradost.cloudstream3.movieproviders.*
import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi
import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.malApi
import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import okhttp3.Interceptor import okhttp3.Interceptor
@ -119,10 +121,10 @@ object APIHolder {
return null return null
} }
fun getApiFromUrlNull(url : String?) : MainAPI? { fun getApiFromUrlNull(url: String?): MainAPI? {
if (url == null) return null if (url == null) return null
for (api in allProviders) { for (api in allProviders) {
if(url.startsWith(api.mainUrl)) if (url.startsWith(api.mainUrl))
return api return api
} }
return null return null
@ -397,7 +399,7 @@ abstract class MainAPI {
} }
/** An okhttp interceptor for used in OkHttpDataSource */ /** An okhttp interceptor for used in OkHttpDataSource */
open fun getVideoInterceptor(extractorLink: ExtractorLink) : Interceptor? { open fun getVideoInterceptor(extractorLink: ExtractorLink): Interceptor? {
return null return null
} }
} }
@ -571,10 +573,10 @@ enum class SearchQuality {
} }
/**Add anything to here if you find a site that uses some specific naming convention*/ /**Add anything to here if you find a site that uses some specific naming convention*/
fun getQualityFromString(string: String?) : SearchQuality? { fun getQualityFromString(string: String?): SearchQuality? {
val check = (string ?: return null).trim().lowercase().replace(" ","") val check = (string ?: return null).trim().lowercase().replace(" ", "")
return when(check) { return when (check) {
"cam" -> SearchQuality.Cam "cam" -> SearchQuality.Cam
"camrip" -> SearchQuality.CamRip "camrip" -> SearchQuality.CamRip
"hdcam" -> SearchQuality.HdCam "hdcam" -> SearchQuality.HdCam
@ -589,7 +591,7 @@ fun getQualityFromString(string: String?) : SearchQuality? {
"telesync" -> SearchQuality.Telesync "telesync" -> SearchQuality.Telesync
"ts" -> SearchQuality.Telesync "ts" -> SearchQuality.Telesync
"dvd" -> SearchQuality.DVD "dvd" -> SearchQuality.DVD
"blueray" -> SearchQuality.BlueRay "blueray" -> SearchQuality.BlueRay
"bluray" -> SearchQuality.BlueRay "bluray" -> SearchQuality.BlueRay
"br" -> SearchQuality.BlueRay "br" -> SearchQuality.BlueRay
"standard" -> SearchQuality.SD "standard" -> SearchQuality.SD
@ -614,7 +616,7 @@ interface SearchResponse {
var type: TvType? var type: TvType?
var posterUrl: String? var posterUrl: String?
var id: Int? var id: Int?
var quality : SearchQuality? var quality: SearchQuality?
} }
enum class ActorRole { enum class ActorRole {
@ -703,8 +705,12 @@ interface LoadResponse {
var recommendations: List<SearchResponse>? var recommendations: List<SearchResponse>?
var actors: List<ActorData>? var actors: List<ActorData>?
var comingSoon: Boolean var comingSoon: Boolean
var syncData: MutableMap<String, String>
companion object { companion object {
val malIdPrefix = malApi.idPrefix
val aniListIdPrefix = aniListApi.idPrefix
@JvmName("addActorNames") @JvmName("addActorNames")
fun LoadResponse.addActors(actors: List<String>?) { fun LoadResponse.addActors(actors: List<String>?) {
this.actors = actors?.map { ActorData(Actor(it)) } this.actors = actors?.map { ActorData(Actor(it)) }
@ -725,6 +731,30 @@ interface LoadResponse {
this.actors = actors?.map { actor -> ActorData(actor) } this.actors = actors?.map { actor -> ActorData(actor) }
} }
fun LoadResponse.addMalId(id: Int?) {
this.syncData[malIdPrefix] = (id ?: return).toString()
}
fun LoadResponse.addAniListId(id: Int?) {
this.syncData[aniListIdPrefix] = (id ?: return).toString()
}
fun LoadResponse.addImdbUrl(url : String?) {
addImdbId(imdbUrlToIdNullable(url))
}
fun LoadResponse.addImdbId(id: String?) {
// TODO add imdb sync
}
fun LoadResponse.addTrackId(id: String?) {
// TODO add trackt sync
}
fun LoadResponse.addkitsuId(id: String?) {
// TODO add kitsu sync
}
fun LoadResponse.setDuration(input: String?) { fun LoadResponse.setDuration(input: String?) {
val cleanInput = input?.trim()?.replace(" ", "") ?: return val cleanInput = input?.trim()?.replace(" ", "") ?: return
Regex("([0-9]*)h.*?([0-9]*)m").find(cleanInput)?.groupValues?.let { values -> Regex("([0-9]*)h.*?([0-9]*)m").find(cleanInput)?.groupValues?.let { values ->
@ -789,6 +819,7 @@ data class TorrentLoadResponse(
override var recommendations: List<SearchResponse>? = null, override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null, override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false, override var comingSoon: Boolean = false,
override var syncData: MutableMap<String, String> = mutableMapOf(),
) : LoadResponse ) : LoadResponse
data class AnimeLoadResponse( data class AnimeLoadResponse(
@ -802,21 +833,20 @@ data class AnimeLoadResponse(
override var posterUrl: String? = null, override var posterUrl: String? = null,
override var year: Int? = null, override var year: Int? = null,
var episodes: HashMap<DubStatus, List<AnimeEpisode>> = hashMapOf(), var episodes: MutableMap<DubStatus, List<AnimeEpisode>> = mutableMapOf(),
var showStatus: ShowStatus? = null, var showStatus: ShowStatus? = null,
override var plot: String? = null, override var plot: String? = null,
override var tags: List<String>? = null, override var tags: List<String>? = null,
var synonyms: List<String>? = null, var synonyms: List<String>? = null,
var malId: Int? = null,
var anilistId: Int? = null,
override var rating: Int? = null, override var rating: Int? = null,
override var duration: Int? = null, override var duration: Int? = null,
override var trailerUrl: String? = null, override var trailerUrl: String? = null,
override var recommendations: List<SearchResponse>? = null, override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null, override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false, override var comingSoon: Boolean = false,
override var syncData: MutableMap<String, String> = mutableMapOf(),
) : LoadResponse ) : LoadResponse
fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List<AnimeEpisode>?) { fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List<AnimeEpisode>?) {
@ -828,15 +858,15 @@ fun MainAPI.newAnimeLoadResponse(
name: String, name: String,
url: String, url: String,
type: TvType, type: TvType,
comingSoonIfNone : Boolean, comingSoonIfNone: Boolean,
initializer: AnimeLoadResponse.() -> Unit = { }, initializer: AnimeLoadResponse.() -> Unit = { },
): AnimeLoadResponse { ): AnimeLoadResponse {
val builder = AnimeLoadResponse(name = name, url = url, apiName = this.name, type = type) val builder = AnimeLoadResponse(name = name, url = url, apiName = this.name, type = type)
builder.initializer() builder.initializer()
if(comingSoonIfNone) { if (comingSoonIfNone) {
builder.comingSoon = true builder.comingSoon = true
for (key in builder.episodes.keys) for (key in builder.episodes.keys)
if(!builder.episodes[key].isNullOrEmpty()) { if (!builder.episodes[key].isNullOrEmpty()) {
builder.comingSoon = false builder.comingSoon = false
break break
} }
@ -864,7 +894,6 @@ data class MovieLoadResponse(
override var year: Int? = null, override var year: Int? = null,
override var plot: String? = null, override var plot: String? = null,
var imdbId: String? = null,
override var rating: Int? = null, override var rating: Int? = null,
override var tags: List<String>? = null, override var tags: List<String>? = null,
override var duration: Int? = null, override var duration: Int? = null,
@ -872,6 +901,7 @@ data class MovieLoadResponse(
override var recommendations: List<SearchResponse>? = null, override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null, override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false, override var comingSoon: Boolean = false,
override var syncData: MutableMap<String, String> = mutableMapOf(),
) : LoadResponse ) : LoadResponse
fun MainAPI.newMovieLoadResponse( fun MainAPI.newMovieLoadResponse(
@ -916,7 +946,6 @@ data class TvSeriesLoadResponse(
override var plot: String? = null, override var plot: String? = null,
var showStatus: ShowStatus? = null, var showStatus: ShowStatus? = null,
var imdbId: String? = null,
override var rating: Int? = null, override var rating: Int? = null,
override var tags: List<String>? = null, override var tags: List<String>? = null,
override var duration: Int? = null, override var duration: Int? = null,
@ -924,6 +953,7 @@ data class TvSeriesLoadResponse(
override var recommendations: List<SearchResponse>? = null, override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null, override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false, override var comingSoon: Boolean = false,
override var syncData: MutableMap<String, String> = mutableMapOf(),
) : LoadResponse ) : LoadResponse
fun MainAPI.newTvSeriesLoadResponse( fun MainAPI.newTvSeriesLoadResponse(
@ -954,5 +984,5 @@ fun fetchUrls(text: String?): List<String> {
return linkRegex.findAll(text).map { it.value.trim().removeSurrounding("\"") }.toList() return linkRegex.findAll(text).map { it.value.trim().removeSurrounding("\"") }.toList()
} }
fun String?.toRatingInt() : Int? = fun String?.toRatingInt(): Int? =
this?.trim()?.toDoubleOrNull()?.absoluteValue?.times(1000f)?.toInt() this?.trim()?.toDoubleOrNull()?.absoluteValue?.times(1000f)?.toInt()

View file

@ -275,21 +275,26 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
if (str.contains("/${api.redirectUrl}")) { if (str.contains("/${api.redirectUrl}")) {
ioSafe { ioSafe {
Log.i(TAG, "handleAppIntent $str") Log.i(TAG, "handleAppIntent $str")
if (api.handleRedirect(str)) { val isSuccessful = api.handleRedirect(str)
if (isSuccessful) {
Log.i(TAG, "authenticated ${api.name}") Log.i(TAG, "authenticated ${api.name}")
this.runOnUiThread {
try {
showToast(
this,
getString(R.string.authenticated_user).format(api.name)
)
} catch (e: Exception) {
logError(e) // format might fail
}
}
} else { } else {
Log.i(TAG, "failed to authenticate ${api.name}") Log.i(TAG, "failed to authenticate ${api.name}")
} }
this.runOnUiThread {
try {
showToast(
this,
getString(if (isSuccessful) R.string.authenticated_user else R.string.authenticated_user_fail).format(
api.name
)
)
} catch (e: Exception) {
logError(e) // format might fail
}
}
} }
} }
} }

View file

@ -3,6 +3,8 @@ package com.lagradost.cloudstream3.animeproviders
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.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.network.AppResponse import com.lagradost.cloudstream3.network.AppResponse
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
@ -305,8 +307,8 @@ class AnimePaheProvider : MainAPI() {
null null
} }
this.malId = malId addMalId(malId)
this.anilistId = anilistId addAniListId(anilistId)
this.trailerUrl = trailer this.trailerUrl = trailer
} }
} }

View file

@ -1,11 +1,13 @@
package com.lagradost.cloudstream3.animeproviders package com.lagradost.cloudstream3.animeproviders
import java.util.*
import org.json.JSONObject
import org.jsoup.nodes.Element
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
import org.json.JSONObject
import org.jsoup.nodes.Element
import java.util.*
class AnimeWorldProvider : MainAPI() { class AnimeWorldProvider : MainAPI() {
override var mainUrl = "https://www.animeworld.tv" override var mainUrl = "https://www.animeworld.tv"
@ -170,8 +172,8 @@ class AnimeWorldProvider : MainAPI() {
showStatus = status showStatus = status
plot = description plot = description
tags = genres tags = genres
this.malId = malId addMalId(malId)
this.anilistId = anlId addAniListId(anlId)
this.rating = rating this.rating = rating
this.duration = duration this.duration = duration
this.trailerUrl = trailerUrl this.trailerUrl = trailerUrl

View file

@ -4,6 +4,8 @@ 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.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.extractRabbitStream import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.extractRabbitStream
import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.runSflixExtractorVerifierJob import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.runSflixExtractorVerifierJob
import com.lagradost.cloudstream3.network.Requests.Companion.await import com.lagradost.cloudstream3.network.Requests.Companion.await
@ -291,8 +293,8 @@ class ZoroProvider : MainAPI() {
this.tags = tags this.tags = tags
this.recommendations = recommendations this.recommendations = recommendations
this.actors = actors this.actors = actors
this.malId = syncData?.malId?.toIntOrNull() addMalId(syncData?.malId?.toIntOrNull())
this.anilistId = syncData?.aniListId?.toIntOrNull() addAniListId(syncData?.aniListId?.toIntOrNull())
} }
} }

View file

@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.metaproviders
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.uwetrottmann.tmdb2.Tmdb import com.uwetrottmann.tmdb2.Tmdb
import com.uwetrottmann.tmdb2.entities.* import com.uwetrottmann.tmdb2.entities.*
@ -135,7 +136,8 @@ open class TmdbProvider : MainAPI() {
}.get(Calendar.YEAR) }.get(Calendar.YEAR)
} }
plot = overview plot = overview
imdbId = external_ids?.imdb_id addImdbId(external_ids?.imdb_id)
tags = genres?.mapNotNull { it.name } tags = genres?.mapNotNull { it.name }
duration = episode_run_time?.average()?.toInt() duration = episode_run_time?.average()?.toInt()
rating = this@toLoadResponse.rating rating = this@toLoadResponse.rating
@ -163,7 +165,7 @@ open class TmdbProvider : MainAPI() {
}.get(Calendar.YEAR) }.get(Calendar.YEAR)
} }
plot = overview plot = overview
imdbId = external_ids?.imdb_id addImdbId(external_ids?.imdb_id)
tags = genres?.mapNotNull { it.name } tags = genres?.mapNotNull { it.name }
duration = runtime duration = runtime
rating = this@toLoadResponse.rating rating = this@toLoadResponse.rating
@ -251,7 +253,8 @@ open class TmdbProvider : MainAPI() {
val found = idRegex.find(url) val found = idRegex.find(url)
val isTvSeries = found?.groupValues?.getOrNull(1).equals("tv", ignoreCase = true) val isTvSeries = found?.groupValues?.getOrNull(1).equals("tv", ignoreCase = true)
val id = found?.groupValues?.getOrNull(2)?.toIntOrNull() ?: throw ErrorLoadingException("No id found") val id = found?.groupValues?.getOrNull(2)?.toIntOrNull()
?: throw ErrorLoadingException("No id found")
return if (useMetaLoadResponse) { return if (useMetaLoadResponse) {
return if (isTvSeries) { return if (isTvSeries) {
@ -263,8 +266,8 @@ open class TmdbProvider : MainAPI() {
?.let { ?.let {
it.results?.map { res -> res.toSearchResponse() } it.results?.map { res -> res.toSearchResponse() }
}?.let { list -> }?.let { list ->
response.recommendations = list response.recommendations = list
} }
if (response.actors.isNullOrEmpty()) if (response.actors.isNullOrEmpty())
tmdb.tvService().credits(id, "en-US").awaitResponse().body()?.let { tmdb.tvService().credits(id, "en-US").awaitResponse().body()?.let {

View file

@ -167,7 +167,6 @@ class AllMoviesForYouProvider : MainAPI() {
year?.toIntOrNull(), year?.toIntOrNull(),
descipt, descipt,
null, null,
null,
rating rating
) )
} else { } else {

View file

@ -107,7 +107,6 @@ class AsiaFlixProvider : MainAPI() {
synopsis, synopsis,
getStatus(tvStatus ?: ""), getStatus(tvStatus ?: ""),
null, null,
null,
genre?.split(",")?.map { it.trim() } genre?.split(",")?.map { it.trim() }
) )
} }

View file

@ -286,7 +286,6 @@ open class BflixProvider() : MainAPI() {
year?.toIntOrNull(), year?.toIntOrNull(),
description, description,
null, null,
null,
rating, rating,
tags, tags,
recommendations = recommendations, recommendations = recommendations,
@ -303,7 +302,6 @@ open class BflixProvider() : MainAPI() {
poster, poster,
year?.toIntOrNull(), year?.toIntOrNull(),
description, description,
null,
rating, rating,
tags, tags,
recommendations = recommendations, recommendations = recommendations,

View file

@ -182,7 +182,6 @@ class IHaveNoTvProvider : MainAPI() {
)?.destructured?.component1()?.toIntOrNull(), )?.destructured?.component1()?.toIntOrNull(),
description, description,
null, null,
null,
soup.selectFirst(".videoDetails").select("a[href*=\"/category/\"]") soup.selectFirst(".videoDetails").select("a[href*=\"/category/\"]")
.map { it.text().trim() } .map { it.text().trim() }
)) ))
@ -204,7 +203,6 @@ class IHaveNoTvProvider : MainAPI() {
description, description,
null, null,
null, null,
null,
categories.toList() categories.toList()
) else (episodes?.first() as MovieLoadResponse) ) else (episodes?.first() as MovieLoadResponse)
} }

View file

@ -235,7 +235,6 @@ class LookMovieProvider : MainAPI() {
poster, poster,
year, year,
descript, descript,
null,
rating rating
) )
} else { } else {
@ -292,7 +291,6 @@ class LookMovieProvider : MainAPI() {
year, year,
descript, descript,
null, null,
null,
rating rating
) )
} }

View file

@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.movieproviders
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.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbUrl
import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.parseJson
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.ExtractorLink
@ -144,17 +145,17 @@ class MeloMovieProvider : MainAPI() {
if (type == 1) { // MOVIE if (type == 1) { // MOVIE
val serialize = document.selectFirst("table.accordion__list") val serialize = document.selectFirst("table.accordion__list")
?: throw ErrorLoadingException("No links found") ?: throw ErrorLoadingException("No links found")
return MovieLoadResponse( return newMovieLoadResponse(
title, title,
url, url,
this.name,
TvType.Movie, TvType.Movie,
serializeData(serialize), serializeData(serialize)
poster, ) {
year, this.posterUrl = poster
plot, this.year = year
imdbUrlToIdNullable(imdbUrl) this.plot = plot
) addImdbUrl(imdbUrl)
}
} else if (type == 2) { } else if (type == 2) {
val episodes = ArrayList<TvSeriesEpisode>() val episodes = ArrayList<TvSeriesEpisode>()
val seasons = document.select("div.accordion__card") val seasons = document.select("div.accordion__card")
@ -175,18 +176,17 @@ class MeloMovieProvider : MainAPI() {
} }
} }
episodes.reverse() episodes.reverse()
return TvSeriesLoadResponse( return newTvSeriesLoadResponse(
title, title,
url, url,
this.name,
TvType.TvSeries, TvType.TvSeries,
episodes, episodes
poster, ) {
year, this.posterUrl = poster
plot, this.year = year
null, this.plot = plot
imdbUrlToIdNullable(imdbUrl) addImdbUrl(imdbUrl)
) }
} }
return null return null
} }

View file

@ -163,7 +163,6 @@ class PelisflixProvider : MainAPI() {
year?.toIntOrNull(), year?.toIntOrNull(),
descipt2, descipt2,
null, null,
null,
rating rating
) )
} else { } else {

View file

@ -1,9 +1,9 @@
package com.lagradost.cloudstream3.movieproviders package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.util.*
class PelisplusHDProvider:MainAPI() { class PelisplusHDProvider:MainAPI() {
override var mainUrl = "https://pelisplushd.net" override var mainUrl = "https://pelisplushd.net"
@ -137,7 +137,6 @@ class PelisplusHDProvider:MainAPI() {
description, description,
null, null,
null, null,
null,
tags, tags,
) )
} }
@ -152,7 +151,6 @@ class PelisplusHDProvider:MainAPI() {
year, year,
description, description,
null, null,
null,
tags, tags,
) )
} }

View file

@ -160,7 +160,6 @@ class SeriesflixProvider : MainAPI() {
year?.toIntOrNull(), year?.toIntOrNull(),
descipt, descipt,
null, null,
null,
rating rating
) )
} else { } else {

View file

@ -76,7 +76,6 @@ class VfFilmProvider : MainAPI() {
val title = document?.selectFirst("div.SubTitle")?.text() val title = document?.selectFirst("div.SubTitle")?.text()
?: throw ErrorLoadingException("Service might be unavailable") ?: throw ErrorLoadingException("Service might be unavailable")
val year = document.select("span.Date").text()?.toIntOrNull() val year = document.select("span.Date").text()?.toIntOrNull()
val rating = document.select("span.AAIco-star").text() val rating = document.select("span.AAIco-star").text()
@ -88,7 +87,6 @@ class VfFilmProvider : MainAPI() {
val descript = document.selectFirst("div.Description > p").text() val descript = document.selectFirst("div.Description > p").text()
val players = document.select("ul.TPlayerNv > li") val players = document.select("ul.TPlayerNv > li")
var number_player = 0 var number_player = 0
var found = false var found = false
@ -108,17 +106,17 @@ class VfFilmProvider : MainAPI() {
val data = getDirect("$mainUrl/?trembed=$i&trid=$trid&trtype=1") val data = getDirect("$mainUrl/?trembed=$i&trid=$trid&trtype=1")
return MovieLoadResponse( return newMovieLoadResponse(
title, title,
url, url,
this.name,
TvType.Movie, TvType.Movie,
data, data
poster, ) {
year, this.posterUrl = poster
descript, this.year = year
rating, this.plot = descript
duration //this.rating = rating
) this.duration = duration
}
} }
} }

View file

@ -160,7 +160,6 @@ class VfSerieProvider : MainAPI() {
year, year,
descript, descript,
null, null,
null,
rating rating
) )
} }

View file

@ -78,7 +78,6 @@ class FrenchStreamProvider : MainAPI() {
poster, poster,
date, date,
description, description,
null,
ratingAverage, ratingAverage,
tagsList, tagsList,
null, null,

View file

@ -606,13 +606,15 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
result_recommendations_btt?.isGone = isInvalid result_recommendations_btt?.isGone = isInvalid
result_recommendations_btt?.setOnClickListener { result_recommendations_btt?.setOnClickListener {
if (result_overlapping_panels?.getSelectedPanel()?.ordinal == 1) { if (result_overlapping_panels?.getSelectedPanel()?.ordinal == 1) {
result_recommendations_btt?.nextFocusDownId = R.id.result_recommendations
result_overlapping_panels?.openEndPanel() result_overlapping_panels?.openEndPanel()
} else { } else {
result_recommendations_btt?.nextFocusDownId = R.id.result_description
result_overlapping_panels?.closePanels() result_overlapping_panels?.closePanels()
} }
} }
result_overlapping_panels?.setEndPanelLockState(if (isInvalid) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED) result_overlapping_panels?.setEndPanelLockState(if (isInvalid) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED)
result_recommendations.post { result_recommendations?.post {
rec?.let { list -> rec?.let { list ->
(result_recommendations?.adapter as SearchAdapter?)?.updateList(list) (result_recommendations?.adapter as SearchAdapter?)?.updateList(list)
} }
@ -1337,6 +1339,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
result_sync_loading_shimmer?.stopShimmer() result_sync_loading_shimmer?.stopShimmer()
result_sync_loading_shimmer?.isVisible = false result_sync_loading_shimmer?.isVisible = false
result_sync_holder?.isVisible = false result_sync_holder?.isVisible = false
closed = true
} }
is Resource.Loading -> { is Resource.Loading -> {
result_sync_loading_shimmer?.startShimmer() result_sync_loading_shimmer?.startShimmer()
@ -1420,6 +1423,10 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
} }
result_series_parent?.isVisible = isSeriesVisible result_series_parent?.isVisible = isSeriesVisible
if (isSeriesVisible && activity?.currentFocus?.id == R.id.result_back && context?.isTrueTvSettings() == true) {
result_resume_series_button?.requestFocus()
}
if (isSeriesVisible) { if (isSeriesVisible) {
val down = when { val down = when {
result_season_button?.isVisible == true -> result_season_button result_season_button?.isVisible == true -> result_season_button
@ -1634,23 +1641,20 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
setRecommendations(d.recommendations) setRecommendations(d.recommendations)
setActors(d.actors) setActors(d.actors)
if (SettingsFragment.accountEnabled) if (SettingsFragment.accountEnabled) {
if (d is AnimeLoadResponse) { var isValid = false
// don't inline these variables as it will cause them to not be called for ((prefix, id) in d.syncData) {
val addedMal = setMalSync(d.malId) isValid = isValid || syncModel.addSync(prefix, id)
val addedAniList = setAniListSync(d.anilistId)
if (
addedMal
||
addedAniList
) {
syncModel.updateMetaAndUser()
syncModel.updateSynced()
} else {
syncModel.addFromUrl(d.url)
}
} }
if (isValid) {
syncModel.updateMetaAndUser()
syncModel.updateSynced()
} else {
syncModel.addFromUrl(d.url)
}
}
result_meta_site?.text = d.apiName result_meta_site?.text = d.apiName
val posterImageLink = d.posterUrl val posterImageLink = d.posterUrl
@ -1658,28 +1662,30 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
result_poster?.setImage(posterImageLink) result_poster?.setImage(posterImageLink)
result_poster_blur?.setImageBlur(posterImageLink, 10, 3) result_poster_blur?.setImageBlur(posterImageLink, 10, 3)
//Full screen view of Poster image //Full screen view of Poster image
result_poster_holder?.setOnClickListener { if (context?.isTrueTvSettings() == false) // Poster not clickable on tv
try { result_poster_holder?.setOnClickListener {
context?.let { ctx -> try {
val bitmap = result_poster.drawable.toBitmap() context?.let { ctx ->
val sourceBuilder = AlertDialog.Builder(ctx) val bitmap = result_poster.drawable.toBitmap()
sourceBuilder.setView(R.layout.result_poster) val sourceBuilder = AlertDialog.Builder(ctx)
sourceBuilder.setView(R.layout.result_poster)
val sourceDialog = sourceBuilder.create() val sourceDialog = sourceBuilder.create()
sourceDialog.show() sourceDialog.show()
sourceDialog.findViewById<ImageView?>(R.id.imgPoster) sourceDialog.findViewById<ImageView?>(R.id.imgPoster)
?.apply { ?.apply {
setImageBitmap(bitmap) setImageBitmap(bitmap)
setOnClickListener { setOnClickListener {
sourceDialog.dismissSafe() sourceDialog.dismissSafe()
}
} }
} }
} catch (e: Exception) {
logError(e)
} }
} catch (e: Exception) {
logError(e)
} }
}
} else { } else {
result_poster?.setImageResource(R.drawable.default_cover) result_poster?.setImageResource(R.drawable.default_cover)
result_poster_blur?.setImageResource(R.drawable.default_cover) result_poster_blur?.setImageResource(R.drawable.default_cover)
@ -1698,16 +1704,16 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
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 { result_description.setOnClickListener {
val builder: AlertDialog.Builder = val builder: AlertDialog.Builder =
AlertDialog.Builder(requireContext()) AlertDialog.Builder(requireContext())
builder.setMessage(d.plot) builder.setMessage(d.plot)
.setTitle(if (d.type == TvType.Torrent) R.string.torrent_plot else R.string.result_plot) .setTitle(if (d.type == TvType.Torrent) R.string.torrent_plot else R.string.result_plot)
.show() .show()
} }
result_descript.text = syno result_description.text = syno
} else { } else {
result_descript.text = result_description.text =
if (d.type == TvType.Torrent) getString(R.string.torrent_no_plot) else getString( if (d.type == TvType.Torrent) getString(R.string.torrent_no_plot) else getString(
R.string.normal_no_plot R.string.normal_no_plot
) )
@ -1727,12 +1733,13 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
//result_tag_holder?.visibility = GONE //result_tag_holder?.visibility = GONE
} else { } else {
//result_tag_holder?.visibility = VISIBLE //result_tag_holder?.visibility = VISIBLE
val isOnTv = context?.isTrueTvSettings() == true
for ((index, tag) in tags.withIndex()) { for ((index, tag) in tags.withIndex()) {
val viewBtt = layoutInflater.inflate(R.layout.result_tag, null) val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card) val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
btt.text = tag btt.text = tag
btt.isFocusable = !isOnTv
btt.isClickable = !isOnTv
result_tag?.addView(viewBtt, index) result_tag?.addView(viewBtt, index)
} }
} }
@ -1980,8 +1987,14 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio
} }
} }
result_meta_site?.setOnClickListener { // bloats the navigation on tv
it.context?.openBrowser(tempUrl) if (context?.isTrueTvSettings() == false) {
result_meta_site?.setOnClickListener {
it.context?.openBrowser(tempUrl)
}
result_meta_site?.isFocusable = true
} else {
result_meta_site?.isFocusable = false
} }
if (restart || viewModel.result.value == null) { if (restart || viewModel.result.value == null) {

View file

@ -65,18 +65,19 @@ class SyncViewModel : ViewModel() {
_currentSynced.postValue(getMissing()) _currentSynced.postValue(getMissing())
} }
fun setMalId(id: String?) : Boolean { fun addSync(idPrefix: String, id : String) : Boolean {
if(syncIds[malApi.idPrefix] == id ?: return false) return false if(syncIds[idPrefix] == id) return false
syncIds[malApi.idPrefix] = id Log.i(TAG, "addSync $idPrefix = $id")
Log.i(TAG, "setMalId = $id") syncIds[idPrefix] = id
return true return true
} }
fun setMalId(id: String?) : Boolean {
return addSync(malApi.idPrefix,id ?: return false)
}
fun setAniListId(id: String?) : Boolean { fun setAniListId(id: String?) : Boolean {
if(syncIds[aniListApi.idPrefix] == id ?: return false) return false return addSync(aniListApi.idPrefix,id ?: return false)
syncIds[aniListApi.idPrefix] = id
Log.i(TAG, "setAniListId = $id")
return true
} }
var hasAddedFromUrl: HashSet<String> = hashSetOf() var hasAddedFromUrl: HashSet<String> = hashSetOf()
@ -164,13 +165,15 @@ class SyncViewModel : ViewModel() {
_userDataResponse.postValue(Resource.Loading()) _userDataResponse.postValue(Resource.Loading())
var lastError: Resource<SyncAPI.SyncStatus> = Resource.Failure(false, null, null, "No data") var lastError: Resource<SyncAPI.SyncStatus> = Resource.Failure(false, null, null, "No data")
for ((prefix, id) in syncIds) { for ((prefix, id) in syncIds) {
repos.firstOrNull { it.idPrefix == prefix }?.let { repos.firstOrNull { it.idPrefix == prefix }?.let { repo ->
val result = it.getStatus(id) if(repo.hasAccount()) {
if (result is Resource.Success) { val result = repo.getStatus(id)
_userDataResponse.postValue(result) if (result is Resource.Success) {
return@launch _userDataResponse.postValue(result)
} else if (result is Resource.Failure) { return@launch
lastError = result } else if (result is Resource.Failure) {
lastError = result
}
} }
} }
} }
@ -183,13 +186,15 @@ class SyncViewModel : ViewModel() {
_metaResponse.postValue(Resource.Loading()) _metaResponse.postValue(Resource.Loading())
var lastError: Resource<SyncAPI.SyncResult> = Resource.Failure(false, null, null, "No data") var lastError: Resource<SyncAPI.SyncResult> = Resource.Failure(false, null, null, "No data")
for ((prefix, id) in syncIds) { for ((prefix, id) in syncIds) {
repos.firstOrNull { it.idPrefix == prefix }?.let { repos.firstOrNull { it.idPrefix == prefix }?.let { repo ->
val result = it.getResult(id) if(repo.hasAccount()) {
if (result is Resource.Success) { val result = repo.getResult(id)
_metaResponse.postValue(result) if (result is Resource.Success) {
return@launch _metaResponse.postValue(result)
} else if (result is Resource.Failure) { return@launch
lastError = result } else if (result is Resource.Failure) {
lastError = result
}
} }
} }
} }

View file

@ -352,7 +352,7 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<TextView <TextView
android:id="@+id/result_descript" android:id="@+id/result_description"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:foreground="@drawable/outline_drawable" android:foreground="@drawable/outline_drawable"
@ -390,7 +390,7 @@
android:minWidth="100dp" android:minWidth="100dp"
android:nextFocusLeft="@id/result_back" android:nextFocusLeft="@id/result_back"
android:nextFocusRight="@id/result_search" android:nextFocusRight="@id/result_search"
android:nextFocusUp="@id/result_descript" android:nextFocusUp="@id/result_description"
android:nextFocusDown="@id/result_play_movie" android:nextFocusDown="@id/result_play_movie"
android:paddingTop="0dp" android:paddingTop="0dp"
@ -751,7 +751,7 @@
android:layout_marginStart="0dp" android:layout_marginStart="0dp"
android:nextFocusLeft="@id/result_episode_select" android:nextFocusLeft="@id/result_episode_select"
android:nextFocusRight="@id/result_episode_select" android:nextFocusRight="@id/result_episode_select"
android:nextFocusUp="@id/result_descript" android:nextFocusUp="@id/result_description"
android:nextFocusDown="@id/result_episodes" android:nextFocusDown="@id/result_episodes"
android:visibility="gone" android:visibility="gone"
tools:text="Season 1" tools:text="Season 1"
@ -766,7 +766,7 @@
android:nextFocusLeft="@id/result_season_button" android:nextFocusLeft="@id/result_season_button"
android:nextFocusRight="@id/result_season_button" android:nextFocusRight="@id/result_season_button"
android:nextFocusUp="@id/result_descript" android:nextFocusUp="@id/result_description"
android:nextFocusDown="@id/result_episodes" android:nextFocusDown="@id/result_episodes"
android:visibility="gone" android:visibility="gone"
tools:text="50-100" tools:text="50-100"
@ -781,7 +781,7 @@
android:nextFocusLeft="@id/result_season_button" android:nextFocusLeft="@id/result_season_button"
android:nextFocusRight="@id/result_season_button" android:nextFocusRight="@id/result_season_button"
android:nextFocusUp="@id/result_descript" android:nextFocusUp="@id/result_description"
android:nextFocusDown="@id/result_episodes" android:nextFocusDown="@id/result_episodes"
android:visibility="gone" android:visibility="gone"
tools:text="Dubbed" tools:text="Dubbed"

View file

@ -70,7 +70,7 @@
<ImageView <ImageView
android:nextFocusUp="@id/result_back" android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_descript" android:nextFocusDown="@id/result_description"
android:nextFocusLeft="@id/result_add_sync" android:nextFocusLeft="@id/result_add_sync"
android:nextFocusRight="@id/result_open_in_browser" android:nextFocusRight="@id/result_open_in_browser"
@ -88,7 +88,7 @@
<ImageView <ImageView
android:nextFocusUp="@id/result_back" android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_descript" android:nextFocusDown="@id/result_description"
android:nextFocusLeft="@id/result_share" android:nextFocusLeft="@id/result_share"
android:nextFocusRight="@id/result_search" android:nextFocusRight="@id/result_search"
@ -106,7 +106,7 @@
<ImageView <ImageView
android:nextFocusUp="@id/result_back" android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_descript" android:nextFocusDown="@id/result_description"
android:nextFocusLeft="@id/result_open_in_browser" android:nextFocusLeft="@id/result_open_in_browser"
android:nextFocusRight="@id/result_recommendations_btt" android:nextFocusRight="@id/result_recommendations_btt"
@ -122,9 +122,10 @@
android:contentDescription="@string/result_open_in_browser" android:contentDescription="@string/result_open_in_browser"
app:tint="?attr/textColor" /> app:tint="?attr/textColor" />
<ImageView <ImageView
tools:visibility="visible"
android:visibility="gone" android:visibility="gone"
android:nextFocusUp="@id/result_back" android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_descript" android:nextFocusDown="@id/result_description"
android:nextFocusLeft="@id/result_search" android:nextFocusLeft="@id/result_search"
android:nextFocusRight="@id/result_bookmark_button" android:nextFocusRight="@id/result_bookmark_button"

View file

@ -35,14 +35,14 @@
<item>None</item> <item>None</item>
<item>Google</item> <item>Google</item>
<item>Cloudflare</item> <item>Cloudflare</item>
<!-- <item>OpenDns</item>--> <!-- <item>OpenDns</item>-->
<item>AdGuard</item> <item>AdGuard</item>
</array> </array>
<array name="dns_pref_values"> <array name="dns_pref_values">
<item>0</item> <item>0</item>
<item>1</item> <item>1</item>
<item>2</item> <item>2</item>
<!-- <item>3</item>--> <!-- <item>3</item>-->
<item>4</item> <item>4</item>
</array> </array>
@ -196,7 +196,7 @@
<item>Normal</item> <item>Normal</item>
<item>Blue</item> <item>Blue</item>
<item>Red</item> <item>Red</item>
<item>Purple</item> <item>Purple</item>
<item>Green</item> <item>Green</item>
<item>GreenApple</item> <item>GreenApple</item>
<item>Banana</item> <item>Banana</item>

View file

@ -401,6 +401,8 @@
<string name="sync_total_episodes_none">/??</string> <string name="sync_total_episodes_none">/??</string>
<string name="sync_total_episodes_some" formatted="true">/%d</string> <string name="sync_total_episodes_some" formatted="true">/%d</string>
<string name="authenticated_user" formatted="true">Authenticated %s</string> <string name="authenticated_user" formatted="true">Authenticated %s</string>
<string name="authenticated_user_fail" formatted="true">Failed to authenticate to %s</string>
<!-- ============ --> <!-- ============ -->
<string name="none">None</string> <string name="none">None</string>
<string name="normal">Normal</string> <string name="normal">Normal</string>