diff --git a/app/build.gradle b/app/build.gradle
index 1cd978ee..10fdff88 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -36,7 +36,7 @@ android {
targetSdkVersion 30
versionCode 48
- versionName "2.10.28"
+ versionName "2.10.29"
resValue "string", "app_version",
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
@@ -74,6 +74,8 @@ android {
}
}
compileOptions {
+ coreLibraryDesugaringEnabled true
+
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
@@ -143,7 +145,7 @@ dependencies {
implementation 'com.jaredrummler:colorpicker:1.1.0'
//run JS
- implementation 'org.mozilla:rhino:1.7R4'
+ implementation 'org.mozilla:rhino:1.7.14'
// TorrentStream
//implementation 'com.github.TorrentStream:TorrentStream-Android:2.7.0'
@@ -175,6 +177,11 @@ dependencies {
// used for subtitle decoding https://github.com/albfernandez/juniversalchardet
implementation 'com.github.albfernandez:juniversalchardet:2.4.0'
- // play yt
- implementation 'com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT'
+ // slow af yt
+ //implementation 'com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT'
+
+ // newpipe yt
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:master-SNAPSHOT'
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
+
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6dcd4e1b..b80154b2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -155,11 +155,11 @@
+ android:exported="false" />
+ android:name=".ui.ControllerActivity" />
> = request.headers()
+ val dataToSend: ByteArray? = request.dataToSend()
+ var requestBody: RequestBody? = null
+ if (dataToSend != null) {
+ requestBody = RequestBody.create(null, dataToSend)
+ }
+ val requestBuilder: okhttp3.Request.Builder = okhttp3.Request.Builder()
+ .method(httpMethod, requestBody).url(url)
+ .addHeader("User-Agent", USER_AGENT)
+
+ for ((headerName, headerValueList) in headers) {
+ if (headerValueList.size > 1) {
+ requestBuilder.removeHeader(headerName)
+ for (headerValue in headerValueList) {
+ requestBuilder.addHeader(headerName, headerValue)
+ }
+ } else if (headerValueList.size == 1) {
+ requestBuilder.header(headerName, headerValueList[0])
+ }
+ }
+ val response = client.newCall(requestBuilder.build()).execute()
+ if (response.code == 429) {
+ response.close()
+ throw ReCaptchaException("reCaptcha Challenge requested", url)
+ }
+ val body = response.body
+ var responseBodyToReturn: String? = null
+ if (body != null) {
+ responseBodyToReturn = body.string()
+ }
+ val latestUrl = response.request.url.toString()
+ return Response(
+ response.code, response.message, response.headers.toMultimap(),
+ responseBodyToReturn, latestUrl
+ )
+ }
+
+ companion object {
+ private const val USER_AGENT =
+ "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0"
+ private var instance: DownloaderTestImpl? = null
+
+ /**
+ * It's recommended to call exactly once in the entire lifetime of the application.
+ *
+ * @param builder if null, default builder will be used
+ * @return a new instance of [DownloaderTestImpl]
+ */
+ fun init(builder: OkHttpClient.Builder?): DownloaderTestImpl? {
+ instance = DownloaderTestImpl(
+ builder ?: OkHttpClient.Builder()
+ )
+ return instance
+ }
+
+ fun getInstance(): DownloaderTestImpl? {
+ if (instance == null) {
+ init(null)
+ }
+ return instance
+ }
+ }
+
+ init {
+ client = builder.readTimeout(30, TimeUnit.SECONDS).build()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
index 19e309fd..106ddea0 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt
@@ -10,6 +10,7 @@ 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.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.animeproviders.*
import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider
import com.lagradost.cloudstream3.movieproviders.*
@@ -17,8 +18,10 @@ 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.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.loadExtractor
import okhttp3.Interceptor
import java.text.SimpleDateFormat
import java.util.*
@@ -39,7 +42,7 @@ object APIHolder {
private const val defProvider = 0
- val allProviders by lazy {
+ val allProviders =
arrayListOf(
// Movie providers
ElifilmsProvider(),
@@ -132,7 +135,7 @@ object APIHolder {
NginxProvider(),
OlgplyProvider(),
)
- }
+
fun initAll() {
for (api in allProviders) {
@@ -142,7 +145,7 @@ object APIHolder {
}
var apis: List = arrayListOf()
- private var apiMap: Map? = null
+ var apiMap: Map? = null
private fun initMap() {
if (apiMap == null)
@@ -297,6 +300,16 @@ object APIHolder {
return realSet
}
+ fun Context.updateHasTrailers() {
+ LoadResponse.isTrailersEnabled = getHasTrailers()
+ }
+
+ private fun Context.getHasTrailers(): Boolean {
+ if (this.isTvSettings()) return false
+ val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
+ return settingsManager.getBoolean(this.getString(R.string.show_trailers_key), true)
+ }
+
fun Context.filterProviderByPreferredMedia(hasHomePageIsRequired: Boolean = true): List {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val currentPrefMedia =
@@ -357,6 +370,7 @@ abstract class MainAPI {
}
fun overrideWithNewData(data: ProvidersInfoJson) {
+ if (!canBeOverridden) return
this.name = data.name
if (data.url.isNotBlank() && data.url != "NONE")
this.mainUrl = data.url
@@ -366,10 +380,11 @@ abstract class MainAPI {
open var name = "NONE"
open var mainUrl = "NONE"
open var storedCredentials: String? = null
+ open var canBeOverridden: Boolean = true
//open val uniqueId : Int by lazy { this.name.hashCode() } // in case of duplicate providers you can have a shared id
- open val lang = "en" // ISO_639_1 check SubtitleHelper
+ open var lang = "en" // ISO_639_1 check SubtitleHelper
/**If link is stored in the "data" string, so links can be instantly loaded*/
open val instantLinkLoading = false
@@ -852,7 +867,7 @@ interface LoadResponse {
var rating: Int? // 0-10000
var tags: List?
var duration: Int? // in minutes
- var trailers: List?
+ var trailers: List?
var recommendations: List?
var actors: List?
var comingSoon: Boolean
@@ -862,6 +877,7 @@ interface LoadResponse {
companion object {
private val malIdPrefix = malApi.idPrefix
private val aniListIdPrefix = aniListApi.idPrefix
+ var isTrailersEnabled = true
@JvmName("addActorNames")
fun LoadResponse.addActors(actors: List?) {
@@ -883,6 +899,14 @@ interface LoadResponse {
this.actors = actors?.map { actor -> ActorData(actor) }
}
+ fun LoadResponse.getMalId() : String? {
+ return this.syncData[malIdPrefix]
+ }
+
+ fun LoadResponse.getAniListId() : String? {
+ return this.syncData[aniListIdPrefix]
+ }
+
fun LoadResponse.addMalId(id: Int?) {
this.syncData[malIdPrefix] = (id ?: return).toString()
}
@@ -895,27 +919,38 @@ interface LoadResponse {
addImdbId(imdbUrlToIdNullable(url))
}
- /**better to set trailers directly instead of calling this multiple times*/
- fun LoadResponse.addTrailer(trailerUrl: String?) {
- if (trailerUrl == null) return
+ /**better to call addTrailer with mutible trailers directly instead of calling this multiple times*/
+ suspend fun LoadResponse.addTrailer(trailerUrl: String?, referer: String? = null) {
+ if (!isTrailersEnabled || trailerUrl == null) return
+ try {
+ val newTrailers = loadExtractor(trailerUrl, referer)
+ addTrailer(newTrailers)
+ } catch (e: Exception) {
+ logError(e)
+ }
+ }
+
+ fun LoadResponse.addTrailer(newTrailers: List) {
if (this.trailers == null) {
- this.trailers = listOf(trailerUrl)
+ this.trailers = newTrailers
} else {
- val update = this.trailers?.toMutableList()
- update?.add(trailerUrl)
+ val update = this.trailers?.toMutableList() ?: mutableListOf()
+ update.addAll(newTrailers)
this.trailers = update
}
}
- fun LoadResponse.addTrailer(trailerUrls: List?) {
- if(trailerUrls == null) return
- if (this.trailers == null) {
- this.trailers = trailerUrls
- } else {
- val update = this.trailers?.toMutableList()
- update?.addAll(trailerUrls)
- this.trailers = update
- }
+ suspend fun LoadResponse.addTrailer(trailerUrls: List?, referer: String? = null) {
+ if (!isTrailersEnabled || trailerUrls == null) return
+ val newTrailers = trailerUrls.apmap { trailerUrl ->
+ try {
+ loadExtractor(trailerUrl, referer)
+ } catch (e: Exception) {
+ logError(e)
+ emptyList()
+ }
+ }.flatten().distinct()
+ addTrailer(newTrailers)
}
fun LoadResponse.addImdbId(id: String?) {
@@ -995,7 +1030,7 @@ data class TorrentLoadResponse(
override var rating: Int? = null,
override var tags: List? = null,
override var duration: Int? = null,
- override var trailers: List? = null,
+ override var trailers: List? = null,
override var recommendations: List? = null,
override var actors: List? = null,
override var comingSoon: Boolean = false,
@@ -1023,7 +1058,7 @@ data class AnimeLoadResponse(
override var rating: Int? = null,
override var duration: Int? = null,
- override var trailers: List? = null,
+ override var trailers: List? = null,
override var recommendations: List? = null,
override var actors: List? = null,
override var comingSoon: Boolean = false,
@@ -1036,12 +1071,12 @@ fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List?) {
this.episodes[status] = episodes
}
-fun MainAPI.newAnimeLoadResponse(
+suspend fun MainAPI.newAnimeLoadResponse(
name: String,
url: String,
type: TvType,
comingSoonIfNone: Boolean = true,
- initializer: AnimeLoadResponse.() -> Unit = { },
+ initializer: suspend AnimeLoadResponse.() -> Unit = { },
): AnimeLoadResponse {
val builder = AnimeLoadResponse(name = name, url = url, apiName = this.name, type = type)
builder.initializer()
@@ -1070,7 +1105,7 @@ data class MovieLoadResponse(
override var rating: Int? = null,
override var tags: List? = null,
override var duration: Int? = null,
- override var trailers: List? = null,
+ override var trailers: List? = null,
override var recommendations: List? = null,
override var actors: List? = null,
override var comingSoon: Boolean = false,
@@ -1078,12 +1113,12 @@ data class MovieLoadResponse(
override var posterHeaders: Map? = null,
) : LoadResponse
-fun MainAPI.newMovieLoadResponse(
+suspend fun MainAPI.newMovieLoadResponse(
name: String,
url: String,
type: TvType,
data: T?,
- initializer: MovieLoadResponse.() -> Unit = { }
+ initializer: suspend MovieLoadResponse.() -> Unit = { }
): MovieLoadResponse {
// just in case
if (data is String) return newMovieLoadResponse(
@@ -1106,12 +1141,12 @@ fun MainAPI.newMovieLoadResponse(
return builder
}
-fun MainAPI.newMovieLoadResponse(
+suspend fun MainAPI.newMovieLoadResponse(
name: String,
url: String,
type: TvType,
dataUrl: String,
- initializer: MovieLoadResponse.() -> Unit = { }
+ initializer: suspend MovieLoadResponse.() -> Unit = { }
): MovieLoadResponse {
val builder = MovieLoadResponse(
name = name,
@@ -1191,7 +1226,7 @@ data class TvSeriesLoadResponse(
override var rating: Int? = null,
override var tags: List? = null,
override var duration: Int? = null,
- override var trailers: List? = null,
+ override var trailers: List? = null,
override var recommendations: List? = null,
override var actors: List? = null,
override var comingSoon: Boolean = false,
@@ -1199,12 +1234,12 @@ data class TvSeriesLoadResponse(
override var posterHeaders: Map? = null,
) : LoadResponse
-fun MainAPI.newTvSeriesLoadResponse(
+suspend fun MainAPI.newTvSeriesLoadResponse(
name: String,
url: String,
type: TvType,
episodes: List,
- initializer: TvSeriesLoadResponse.() -> Unit = { }
+ initializer: suspend TvSeriesLoadResponse.() -> Unit = { }
): TvSeriesLoadResponse {
val builder = TvSeriesLoadResponse(
name = name,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index aadf00a7..51ebb41b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -28,6 +28,7 @@ import com.lagradost.cloudstream3.APIHolder.allProviders
import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.initAll
+import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
import com.lagradost.cloudstream3.CommonActivity.loadThemes
import com.lagradost.cloudstream3.CommonActivity.onColorSelectedEvent
import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
@@ -47,6 +48,7 @@ import com.lagradost.cloudstream3.ui.result.ResultFragment
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
+import com.lagradost.cloudstream3.ui.settings.SettingsGeneral
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
@@ -68,12 +70,14 @@ import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
+import com.lagradost.cloudstream3.utils.USER_PROVIDER_API
import com.lagradost.nicehttp.Requests
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_result_swipe.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
+import org.schabi.newpipe.extractor.NewPipe
import java.io.File
import kotlin.concurrent.thread
@@ -364,9 +368,23 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
}
fun test() {
- //val youtubeLink = "https://www.youtube.com/watch?v=TxB48MEAmZw"
+ /*thread {
+ val youtubeLink = "https://www.youtube.com/watch?v=Zxem9rqJ5S0"
+ val url = YoutubeStreamLinkHandlerFactory.getInstance().fromUrl(youtubeLink)
+ println("ID:::: ${url.id}")
+ NewPipe.init(DownloaderTestImpl.getInstance())
+ val service = ServiceList.YouTube
+ val s = object : YoutubeStreamExtractor(
+ service,
+ url
+ ) {
+ }
+ s.fetchPage()
+ val streams = s.videoStreams
+ println("STREAMS: ${streams.map { "url = "+ it.url + " extra= " + it.height + "|" + it.isVideoOnly + "\n" }}")
+ }*/
/*
runBlocking {
@@ -515,6 +533,26 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
apis = allProviders
}
+ try {
+ getKey>(USER_PROVIDER_API)?.let { list ->
+ list.forEach { custom ->
+ allProviders.firstOrNull { it.javaClass.simpleName == custom.parentJavaClass }
+ ?.let {
+ allProviders.add(it.javaClass.newInstance().apply {
+ name = custom.name
+ lang = custom.lang
+ mainUrl = custom.url.trimEnd('/')
+ canBeOverridden = false
+ })
+ }
+ }
+ }
+ apis = allProviders
+ APIHolder.apiMap = null
+ } catch (e: Exception) {
+ logError(e)
+ }
+
loadThemes(this)
updateLocale()
app.initClient(this)
@@ -576,6 +614,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
loadCache()
test()
+ NewPipe.init(DownloaderTestImpl.getInstance())
+ updateHasTrailers()
/*nav_view.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeWorldProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeWorldProvider.kt
index e41a63b5..4a965206 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeWorldProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeWorldProvider.kt
@@ -16,7 +16,7 @@ import org.jsoup.nodes.Element
class AnimeWorldProvider : MainAPI() {
override var mainUrl = "https://www.animeworld.tv"
override var name = "AnimeWorld"
- override val lang = "it"
+ override var lang = "it"
override val hasMainPage = true
override val supportedTypes = setOf(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimefenixProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimefenixProvider.kt
index 11eb01c2..d00543d4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimefenixProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimefenixProvider.kt
@@ -13,7 +13,7 @@ class AnimefenixProvider:MainAPI() {
override var mainUrl = "https://animefenix.com"
override var name = "Animefenix"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvIOProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvIOProvider.kt
index 481c2b25..aadfdcad 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvIOProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvIOProvider.kt
@@ -11,7 +11,7 @@ import kotlin.collections.ArrayList
class AnimeflvIOProvider:MainAPI() {
override var mainUrl = "https://animeflv.io" //Also scrapes from animeid.to
override var name = "Animeflv.io"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvProvider.kt
index 7e88ce75..df40481b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeflvProvider.kt
@@ -24,7 +24,7 @@ class AnimeflvnetProvider : MainAPI() {
override var mainUrl = "https://www3.animeflv.net"
override var name = "Animeflv.net"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DreamSubProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DreamSubProvider.kt
index 725e1054..e3ca3e28 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DreamSubProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DreamSubProvider.kt
@@ -11,7 +11,7 @@ import org.jsoup.nodes.Element
class DreamSubProvider : MainAPI() {
override var mainUrl = "https://dreamsub.me"
override var name = "DreamSub"
- override val lang = "it"
+ override var lang = "it"
override val hasMainPage = true
override val supportedTypes = setOf(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GomunimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GomunimeProvider.kt
index 1287ae6f..f32d2ddd 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GomunimeProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GomunimeProvider.kt
@@ -14,7 +14,7 @@ class GomunimeProvider : MainAPI() {
override var name = "Gomunime"
override val hasQuickSearch = false
override val hasMainPage = true
- override val lang = "id"
+ override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/JKAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/JKAnimeProvider.kt
index d6e215a9..c84c1fba 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/JKAnimeProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/JKAnimeProvider.kt
@@ -22,7 +22,7 @@ class JKAnimeProvider : MainAPI() {
override var mainUrl = "https://jkanime.net"
override var name = "JKAnime"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuramanimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuramanimeProvider.kt
index e41e9119..ce560949 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuramanimeProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuramanimeProvider.kt
@@ -12,7 +12,7 @@ class KuramanimeProvider : MainAPI() {
override var name = "Kuramanime"
override val hasQuickSearch = false
override val hasMainPage = true
- override val lang = "id"
+ override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuronimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuronimeProvider.kt
index 14e841fc..5eec3a07 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuronimeProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KuronimeProvider.kt
@@ -3,17 +3,18 @@ package com.lagradost.cloudstream3.animeproviders
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.safeApiCall
-import com.lagradost.cloudstream3.utils.*
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.getQualityFromName
+import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
-import java.util.ArrayList
class KuronimeProvider : MainAPI() {
override var mainUrl = "https://185.231.223.254"
override var name = "Kuronime"
override val hasQuickSearch = false
override val hasMainPage = true
- override val lang = "id"
+ override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
@@ -139,7 +140,6 @@ class KuronimeProvider : MainAPI() {
plot = description
addTrailer(trailer)
this.tags = tags
- trailers = listOf(trailer)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MonoschinosProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MonoschinosProvider.kt
index 3681e6f0..e06d2e66 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MonoschinosProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MonoschinosProvider.kt
@@ -24,7 +24,7 @@ class MonoschinosProvider : MainAPI() {
override var mainUrl = "https://monoschinos2.com"
override var name = "Monoschinos"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MundoDonghuaProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MundoDonghuaProvider.kt
index 19520ae6..54fdfe45 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MundoDonghuaProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/MundoDonghuaProvider.kt
@@ -15,7 +15,7 @@ class MundoDonghuaProvider : MainAPI() {
override var mainUrl = "https://www.mundodonghua.com"
override var name = "MundoDonghua"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NeonimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NeonimeProvider.kt
index d844e043..0d9574e5 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NeonimeProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NeonimeProvider.kt
@@ -11,7 +11,7 @@ class NeonimeProvider : MainAPI() {
override var name = "Neonime"
override val hasQuickSearch = false
override val hasMainPage = true
- override val lang = "id"
+ override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NineAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NineAnimeProvider.kt
index f2851d0f..52d9cfd4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NineAnimeProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NineAnimeProvider.kt
@@ -34,7 +34,7 @@ class NineAnimeProvider : MainAPI() {
Pair("$mainUrl/ajax/home/widget?name=updated_sub&page=1", "Recently Updated (SUB)"),
Pair(
"$mainUrl/ajax/home/widget?name=updated_dub&page=1",
- "Recently Updated (DUB)(DUB)"
+ "Recently Updated (DUB)"
),
Pair(
"$mainUrl/ajax/home/widget?name=updated_chinese&page=1",
@@ -64,7 +64,8 @@ class NineAnimeProvider : MainAPI() {
}
//Credits to https://github.com/jmir1
- private val key = "c/aUAorINHBLxWTy3uRiPt8J+vjsOheFG1E0q2X9CYwDZlnmd4Kb5M6gSVzfk7pQ" //key credits to @Modder4869
+ private val key =
+ "c/aUAorINHBLxWTy3uRiPt8J+vjsOheFG1E0q2X9CYwDZlnmd4Kb5M6gSVzfk7pQ" //key credits to @Modder4869
private fun getVrf(id: String): String? {
val reversed = ue(encode(id) + "0000000").slice(0..5).reversed()
@@ -175,7 +176,10 @@ class NineAnimeProvider : MainAPI() {
return app.get(url).document.select("ul.anime-list li").mapNotNull {
val title = it.selectFirst("a.name")!!.text()
val href =
- fixUrlNull(it.selectFirst("a")!!.attr("href"))?.replace(Regex("(\\?ep=(\\d+)\$)"), "")
+ fixUrlNull(it.selectFirst("a")!!.attr("href"))?.replace(
+ Regex("(\\?ep=(\\d+)\$)"),
+ ""
+ )
?: return@mapNotNull null
val image = it.selectFirst("a.poster img")!!.attr("src")
AnimeSearchResponse(
@@ -199,7 +203,8 @@ class NineAnimeProvider : MainAPI() {
override suspend fun load(url: String): LoadResponse? {
val validUrl = url.replace("https://9anime.to", mainUrl)
val doc = app.get(validUrl).document
- val animeid = doc.selectFirst("div.player-wrapper.watchpage")!!.attr("data-id") ?: return null
+ val animeid =
+ doc.selectFirst("div.player-wrapper.watchpage")!!.attr("data-id") ?: return null
val animeidencoded = encode(getVrf(animeid) ?: return null)
val poster = doc.selectFirst("aside.main div.thumb div img")!!.attr("src")
val title = doc.selectFirst(".info .title")!!.text()
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NontonAnimeIDProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NontonAnimeIDProvider.kt
index d3cf7772..5998f2a7 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NontonAnimeIDProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/NontonAnimeIDProvider.kt
@@ -15,7 +15,7 @@ class NontonAnimeIDProvider : MainAPI() {
override var name = "NontonAnimeID"
override val hasQuickSearch = false
override val hasMainPage = true
- override val lang = "id"
+ override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/OploverzProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/OploverzProvider.kt
index 906c20ff..9c01934d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/OploverzProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/OploverzProvider.kt
@@ -13,7 +13,7 @@ class OploverzProvider : MainAPI() {
override var name = "Oploverz"
override val hasQuickSearch = false
override val hasMainPage = true
- override val lang = "id"
+ override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/YoutubeExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/YoutubeExtractor.kt
new file mode 100644
index 00000000..9a059468
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/YoutubeExtractor.kt
@@ -0,0 +1,76 @@
+package com.lagradost.cloudstream3.extractors
+
+import com.lagradost.cloudstream3.ErrorLoadingException
+import com.lagradost.cloudstream3.mvvm.Resource
+import com.lagradost.cloudstream3.mvvm.safeApiCall
+import com.lagradost.cloudstream3.utils.ExtractorApi
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import org.schabi.newpipe.extractor.ServiceList
+import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor
+import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory
+import org.schabi.newpipe.extractor.stream.VideoStream
+
+class YoutubeShortLinkExtractor : YoutubeExtractor() {
+ override val mainUrl = "https://youtu.be"
+
+ override fun getExtractorUrl(id: String): String {
+ return "$mainUrl/$id"
+ }
+}
+
+open class YoutubeExtractor : ExtractorApi() {
+ override val mainUrl = "https://www.youtube.com"
+ override val requiresReferer = false
+ override val name = "YouTube"
+
+ companion object {
+ private var ytVideos: MutableMap> = mutableMapOf()
+ }
+
+ override fun getExtractorUrl(id: String): String {
+ return "$mainUrl/watch?v=$id"
+ }
+
+ override suspend fun getUrl(url: String, referer: String?): List? {
+ val streams = safeApiCall {
+ val streams = ytVideos[url] ?: let {
+ val link =
+ YoutubeStreamLinkHandlerFactory.getInstance().fromUrl(url)
+
+ val s = object : YoutubeStreamExtractor(
+ ServiceList.YouTube,
+ link
+ ) {
+
+ }
+ s.fetchPage()
+ val streams = s.videoStreams ?: return@let emptyList()
+ ytVideos[url] = streams
+ streams
+ }
+ if (streams.isEmpty()) {
+ throw ErrorLoadingException("No Youtube streams")
+ }
+
+ streams
+ //streams.sortedBy { it.height }
+ // .firstOrNull { !it.isVideoOnly && it.height > 0 }
+ // ?: throw ErrorLoadingException("No valid Youtube stream")
+ }
+ if (streams is Resource.Success) {
+ return streams.value.mapNotNull {
+ if (it.isVideoOnly || it.height <= 0) return@mapNotNull null
+
+ ExtractorLink(
+ this.name,
+ this.name,
+ it.url ?: return@mapNotNull null,
+ "",
+ it.height
+ )
+ }
+ } else {
+ return null
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/CrossTmdbProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/CrossTmdbProvider.kt
index a9c6a463..b01d188c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/CrossTmdbProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/CrossTmdbProvider.kt
@@ -12,7 +12,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
class CrossTmdbProvider : TmdbProvider() {
override var name = "MultiMovie"
override val apiName = "MultiMovie"
- override val lang = "en"
+ override var lang = "en"
override val useMetaLoadResponse = true
override val usesWebView = true
override val supportedTypes = setOf(TvType.Movie)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt
index 38c2abea..0ab44b68 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt
@@ -12,7 +12,7 @@ import com.lagradost.cloudstream3.utils.SyncUtil
// wont be implemented
class MultiAnimeProvider : MainAPI() {
override var name = "MultiAnime"
- override val lang = "en"
+ override var lang = "en"
override val usesWebView = true
override val supportedTypes = setOf(TvType.Anime)
private val syncApi: SyncAPI = aniListApi
@@ -61,7 +61,7 @@ class MultiAnimeProvider : MainAPI() {
plot = res.synopsis
tags = res.genres
rating = res.publicScore
- addTrailer(res.trailerUrl)
+ addTrailer(res.trailers)
addAniListId(res.id.toIntOrNull())
recommendations = res.recommendations
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt
index fdec2fc1..13cbd335 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt
@@ -95,7 +95,7 @@ open class TmdbProvider : MainAPI() {
}
}
- private fun TvShow.toLoadResponse(): TvSeriesLoadResponse {
+ private suspend fun TvShow.toLoadResponse(): TvSeriesLoadResponse {
val episodes = this.seasons?.filter { !disableSeasonZero || (it.season_number ?: 0) != 0 }
?.mapNotNull { season ->
season.episodes?.map { episode ->
@@ -167,7 +167,7 @@ open class TmdbProvider : MainAPI() {
}
}
- private fun Movie.toLoadResponse(): MovieLoadResponse {
+ private suspend fun Movie.toLoadResponse(): MovieLoadResponse {
return newMovieLoadResponse(
this.title ?: this.original_title, getUrl(id, false), TvType.Movie, TmdbLink(
this.imdb_id,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AkwamProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AkwamProvider.kt
index 3a8435a2..f370a274 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AkwamProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AkwamProvider.kt
@@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.nodes.Element
class AkwamProvider : MainAPI() {
- override val lang = "ar"
+ override var lang = "ar"
override var mainUrl = "https://akwam.to"
override var name = "Akwam"
override val usesWebView = false
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AltadefinizioneProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AltadefinizioneProvider.kt
index a0a7324b..d8ac1985 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AltadefinizioneProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AltadefinizioneProvider.kt
@@ -5,9 +5,11 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
+import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
+
class AltadefinizioneProvider : MainAPI() {
- override val lang = "it"
+ override var lang = "it"
override var mainUrl = "https://altadefinizione.hair"
override var name = "Altadefinizione"
override val hasMainPage = true
@@ -111,7 +113,10 @@ class AltadefinizioneProvider : MainAPI() {
}
val tags: List = document.select("#details > li:nth-child(1) > a").map { it.text() }
- return newMovieLoadResponse(
+
+ val trailerurl = document.selectFirst("#showtrailer > div > div > iframe")!!.attr("src")
+
+ return newMovieLoadResponse(
title,
url,
TvType.Movie,
@@ -125,6 +130,7 @@ class AltadefinizioneProvider : MainAPI() {
this.duration = null
this.actors = actors
this.tags = tags
+ addTrailer(trailerurl)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CineblogProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CineblogProvider.kt
index 81d6c835..50bc4244 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CineblogProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CineblogProvider.kt
@@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
class CineblogProvider : MainAPI() {
- override val lang = "it"
+ override var lang = "it"
override var mainUrl = "https://cb01.rip"
override var name = "CineBlog"
override val hasMainPage = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CinecalidadProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CinecalidadProvider.kt
index c650ba49..91fa8941 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CinecalidadProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CinecalidadProvider.kt
@@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
class CinecalidadProvider:MainAPI() {
override var mainUrl = "https://cinecalidad.lol"
override var name = "Cinecalidad"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CuevanaProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CuevanaProvider.kt
index 0703a124..771cdf3e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CuevanaProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CuevanaProvider.kt
@@ -10,7 +10,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
class CuevanaProvider : MainAPI() {
override var mainUrl = "https://cuevana3.me"
override var name = "Cuevana"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DoramasYTProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DoramasYTProvider.kt
index ab907aba..b216ec6f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DoramasYTProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DoramasYTProvider.kt
@@ -23,7 +23,7 @@ class DoramasYTProvider : MainAPI() {
override var mainUrl = "https://doramasyt.com"
override var name = "DoramasYT"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DramaidProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DramaidProvider.kt
index 5b601e28..77945fa7 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DramaidProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/DramaidProvider.kt
@@ -15,7 +15,7 @@ class DramaidProvider : MainAPI() {
override var name = "DramaId"
override val hasQuickSearch = false
override val hasMainPage = true
- override val lang = "id"
+ override var lang = "id"
override val hasDownloadSupport = true
override val hasChromecastSupport = false
override val supportedTypes = setOf(TvType.AsianDrama)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EgyBestProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EgyBestProvider.kt
index 7fa54ad6..aae321a9 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EgyBestProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EgyBestProvider.kt
@@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
import org.jsoup.nodes.Element
class EgyBestProvider : MainAPI() {
- override val lang = "ar"
+ override var lang = "ar"
override var mainUrl = "https://www.egy.best"
override var name = "EgyBest"
override val usesWebView = false
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/ElifilmsProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/ElifilmsProvider.kt
index 58167214..dd3e1d15 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/ElifilmsProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/ElifilmsProvider.kt
@@ -1,19 +1,20 @@
package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.*
-import com.lagradost.cloudstream3.utils.*
-import kotlin.collections.ArrayList
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.loadExtractor
-class ElifilmsProvider:MainAPI() {
+class ElifilmsProvider : MainAPI() {
override var mainUrl: String = "https://elifilms.net"
override var name: String = "Elifilms"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Movie,
)
+
override suspend fun getMainPage(): HomePageResponse {
val items = ArrayList()
val newest = app.get(mainUrl).document.selectFirst("a.fav_link.premiera")?.attr("href")
@@ -42,6 +43,7 @@ class ElifilmsProvider:MainAPI() {
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
+
override suspend fun search(query: String): List {
val url = "$mainUrl/?s=$query"
val doc = app.get(url).document
@@ -52,6 +54,7 @@ class ElifilmsProvider:MainAPI() {
(MovieSearchResponse(name, href, this.name, TvType.Movie, poster, null))
}
}
+
override suspend fun load(url: String): LoadResponse {
val document = app.get(url, timeout = 120).document
val title = document.selectFirst(".post_title h1")?.text() ?: ""
@@ -73,6 +76,7 @@ class ElifilmsProvider:MainAPI() {
tags
)
}
+
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EntrepeliculasyseriesProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EntrepeliculasyseriesProvider.kt
index 8ad342b8..68103f8c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EntrepeliculasyseriesProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EntrepeliculasyseriesProvider.kt
@@ -8,7 +8,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
class EntrepeliculasyseriesProvider:MainAPI() {
override var mainUrl = "https://entrepeliculasyseries.nu"
override var name = "EntrePeliculasySeries"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EstrenosDoramasProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EstrenosDoramasProvider.kt
index b02f88fa..bddd9de0 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EstrenosDoramasProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EstrenosDoramasProvider.kt
@@ -20,7 +20,7 @@ class EstrenosDoramasProvider : MainAPI() {
override var mainUrl = "https://www23.estrenosdoramas.net"
override var name = "EstrenosDoramas"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FaselHDProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FaselHDProvider.kt
index fc2258e0..9a21a42c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FaselHDProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FaselHDProvider.kt
@@ -5,7 +5,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
import org.jsoup.nodes.Element
class FaselHDProvider : MainAPI() {
- override val lang = "ar"
+ override var lang = "ar"
override var mainUrl = "https://faselhd.io"
override var name = "FaselHD"
override val usesWebView = false
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FilmanProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FilmanProvider.kt
index 14fa5d6f..8cf5f753 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FilmanProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FilmanProvider.kt
@@ -11,7 +11,7 @@ import org.jsoup.select.Elements
class FilmanProvider : MainAPI() {
override var mainUrl = "https://filman.cc"
override var name = "filman.cc"
- override val lang = "pl"
+ override var lang = "pl"
override val hasMainPage = true
override val supportedTypes = setOf(
TvType.Movie,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FrenchStreamProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FrenchStreamProvider.kt
index e3146a96..9923adca 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FrenchStreamProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FrenchStreamProvider.kt
@@ -12,7 +12,7 @@ class FrenchStreamProvider : MainAPI() {
override var name = "French Stream"
override val hasQuickSearch = false
override val hasMainPage = true
- override val lang = "fr"
+ override var lang = "fr"
override val supportedTypes = setOf(TvType.AnimeMovie, TvType.TvSeries, TvType.Movie)
override suspend fun search(query: String): List {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/HDMovie5.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/HDMovie5.kt
index 42ed9dc6..fcaee9fb 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/HDMovie5.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/HDMovie5.kt
@@ -8,9 +8,9 @@ import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
class HDMovie5 : MainAPI() {
- override var mainUrl = "https://hdmovie5.mba"
+ override var mainUrl = "https://hdmovie2.tv"
override var name = "HDMovie"
- override val lang = "hi"
+ override var lang = "hi"
override val hasQuickSearch = true
override val hasMainPage = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/LayarKaca21Provider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/LayarKaca21Provider.kt
index e8f78b28..393bae12 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/LayarKaca21Provider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/LayarKaca21Provider.kt
@@ -11,7 +11,7 @@ class LayarKacaProvider : MainAPI() {
override var mainUrl = "https://149.56.24.226"
override var name = "LayarKaca"
override val hasMainPage = true
- override val lang = "id"
+ override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Movie,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/MyCimaProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/MyCimaProvider.kt
index bcc39647..b74a281c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/MyCimaProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/MyCimaProvider.kt
@@ -8,7 +8,7 @@ import org.jsoup.Jsoup
import org.jsoup.nodes.Element
class MyCimaProvider : MainAPI() {
- override val lang = "ar"
+ override var lang = "ar"
override var mainUrl = "https://mycima.tv"
override var name = "MyCima"
override val usesWebView = false
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt
index b9472f60..4711d9cb 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt
@@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
@@ -95,7 +96,7 @@ class NginxProvider : MainAPI() {
this.plot = description
this.rating = ratingAverage
this.tags = tagsList
- this.trailers = trailer
+ addTrailer(trailer)
addPoster(poster, authHeader)
}
} else // a tv serie
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PeliSmartProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PeliSmartProvider.kt
index d29c4b58..0976c8b8 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PeliSmartProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PeliSmartProvider.kt
@@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
class PeliSmartProvider: MainAPI() {
override var mainUrl = "https://pelismart.com"
override var name = "PeliSmart"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisflixProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisflixProvider.kt
index f635573c..005cbb7f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisflixProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisflixProvider.kt
@@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
class PelisflixProvider : MainAPI() {
override var mainUrl = "https://pelisflix.li"
override var name = "Pelisflix"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisplusHDProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisplusHDProvider.kt
index 3aef6b4f..ae8d2718 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisplusHDProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisplusHDProvider.kt
@@ -8,7 +8,7 @@ import org.jsoup.nodes.Element
class PelisplusHDProvider:MainAPI() {
override var mainUrl = "https://pelisplushd.net"
override var name = "PelisplusHD"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisplusProviderTemplate.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisplusProviderTemplate.kt
index a9fd90a0..b198b4e4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisplusProviderTemplate.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PelisplusProviderTemplate.kt
@@ -12,7 +12,7 @@ import org.jsoup.Jsoup
*/
open class PelisplusProviderTemplate : MainAPI() {
- override val lang = "es"
+ override var lang = "es"
open val homePageUrlList = listOf()
// // mainUrl is good to have as a holder for the url to make future changes easier.
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyHDXyzProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyHDXyzProvider.kt
index 1a5500ef..1e6b71b6 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyHDXyzProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyHDXyzProvider.kt
@@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
class PinoyHDXyzProvider : MainAPI() {
override var name = "Pinoy-HD"
override var mainUrl = "https://www.pinoy-hd.xyz"
- override val lang = "tl"
+ override var lang = "tl"
override val supportedTypes = setOf(TvType.AsianDrama)
override val hasDownloadSupport = true
override val hasMainPage = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyMoviePediaProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyMoviePediaProvider.kt
index acc22786..7ed1be88 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyMoviePediaProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyMoviePediaProvider.kt
@@ -10,7 +10,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
class PinoyMoviePediaProvider : MainAPI() {
override var name = "Pinoy Moviepedia"
override var mainUrl = "https://pinoymoviepedia.ru"
- override val lang = "tl"
+ override var lang = "tl"
override val supportedTypes = setOf(TvType.AsianDrama)
override val hasDownloadSupport = true
override val hasMainPage = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyMoviesEsProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyMoviesEsProvider.kt
index b975255b..cee6107f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyMoviesEsProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/PinoyMoviesEsProvider.kt
@@ -15,7 +15,7 @@ import org.jsoup.select.Elements
class PinoyMoviesEsProvider : MainAPI() {
override var name = "Pinoy Movies"
override var mainUrl = "https://pinoymovies.es"
- override val lang = "tl"
+ override var lang = "tl"
override val supportedTypes = setOf(TvType.AsianDrama)
override val hasDownloadSupport = false
override val hasMainPage = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/RebahinProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/RebahinProvider.kt
index 789d6684..3a004b6b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/RebahinProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/RebahinProvider.kt
@@ -15,7 +15,7 @@ class RebahinProvider : MainAPI() {
override var mainUrl = "http://167.88.14.149"
override var name = "Rebahin"
override val hasMainPage = true
- override val lang = "id"
+ override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Movie,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SeriesflixProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SeriesflixProvider.kt
index 94683604..7fca3179 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SeriesflixProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SeriesflixProvider.kt
@@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
class SeriesflixProvider : MainAPI() {
override var mainUrl = "https://seriesflix.video"
override var name = "Seriesflix"
- override val lang = "es"
+ override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/StreamingcommunityProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/StreamingcommunityProvider.kt
index 67be5257..188c4d9f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/StreamingcommunityProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/StreamingcommunityProvider.kt
@@ -127,7 +127,7 @@ data class TrailerElement(
class StreamingcommunityProvider : MainAPI() {
- override val lang = "it"
+ override var lang = "it"
override var mainUrl = "https://streamingcommunity.press"
override var name = "Streamingcommunity"
override val hasMainPage = true
@@ -284,7 +284,7 @@ class StreamingcommunityProvider : MainAPI() {
val trailerinfojs = document.select("slider-trailer").attr("videos")
val trailerinfo = parseJson>(trailerinfojs)
val trailerurl: String? = if (trailerinfo.isNotEmpty()) {
- "https://www.youtube.com/watch?v=${trailerinfo[0].id}"
+ "https://www.youtube.com/watch?v=${trailerinfo[0].url}"
} else {
null
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TantiFilmProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TantiFilmProvider.kt
index a598d159..8227bcd2 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TantiFilmProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TantiFilmProvider.kt
@@ -4,9 +4,11 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
+import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
+
class TantifilmProvider : MainAPI() {
- override val lang = "it"
+ override var lang = "it"
override var mainUrl = "https://www.tantifilm.rodeo"
override var name = "Tantifilm"
override val hasMainPage = true
@@ -69,7 +71,6 @@ class TantifilmProvider : MainAPI() {
}
}
-
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val type = if (document.selectFirst("div.category-film")!!.text().contains("Serie")
@@ -107,7 +108,7 @@ class TantifilmProvider : MainAPI() {
}
-
+ val trailerurl = document.selectFirst("#trailer_mob > iframe")!!.attr("src")
if (type == TvType.TvSeries) {
val list = ArrayList>()
@@ -142,22 +143,18 @@ class TantifilmProvider : MainAPI() {
}
}
}
- return TvSeriesLoadResponse(
+ return newTvSeriesLoadResponse(
title,
url,
- this.name,
type,
- episodeList,
- fixUrlNull(poster),
- year.toIntOrNull(),
- descipt[0],
- null,
- rating,
- null,
- null,
- null,
- recomm
- )
+ episodeList) {
+ this.posterUrl= fixUrlNull(poster)
+ this.year = year.toIntOrNull()
+ this.plot= descipt[0]
+ this.rating= rating
+ this.recommendations = recomm
+ addTrailer(trailerurl)
+ }
} else {
val url2 = document.selectFirst("iframe")!!.attr("src")
val actorpagelink =
@@ -217,6 +214,7 @@ class TantifilmProvider : MainAPI() {
this.tags = tags
this.duration = duratio
this.actors = actors
+ addTrailer(trailerurl)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfFilmProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfFilmProvider.kt
index 51335f6b..a0b3420a 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfFilmProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfFilmProvider.kt
@@ -9,7 +9,7 @@ import org.jsoup.Jsoup
class VfFilmProvider : MainAPI() {
override var mainUrl = "https://vf-film.me"
override var name = "vf-film.me"
- override val lang = "fr"
+ override var lang = "fr"
override val hasQuickSearch = false
override val hasMainPage = false
override val hasChromecastSupport = false
diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfSerieProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfSerieProvider.kt
index 654406d8..552b0be0 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfSerieProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfSerieProvider.kt
@@ -9,7 +9,7 @@ import org.jsoup.Jsoup
class VfSerieProvider : MainAPI() {
override var mainUrl = "https://vf-serie.org"
override var name = "vf-serie.org"
- override val lang = "fr"
+ override var lang = "fr"
override val hasQuickSearch = false
override val hasMainPage = false
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt
index 3b16cb7f..87da6219 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt
@@ -67,7 +67,7 @@ interface SyncAPI : OAuth2API {
var studio: List? = null,
var genres: List? = null,
var synonyms: List? = null,
- var trailerUrl: String? = null,
+ var trailers: List? = null,
var isAdult : Boolean? = null,
var posterUrl: String? = null,
var backgroundPosterUrl : String? = null,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt
index 81c14979..9bab9381 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt
@@ -142,6 +142,10 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
getUrlFromId(recMedia.id),
recMedia.coverImage?.large ?: recMedia.coverImage?.medium
)
+ },
+ trailers = when (season.trailer?.site?.lowercase()?.trim()) {
+ "youtube" -> listOf("https://www.youtube.com/watch?v=${season.trailer.id}")
+ else -> null
}
//TODO REST
)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/KitsuApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/KitsuApi.kt
new file mode 100644
index 00000000..84c540b3
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/KitsuApi.kt
@@ -0,0 +1,141 @@
+package com.lagradost.cloudstream3.syncproviders.providers
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.app
+import com.lagradost.cloudstream3.mvvm.logError
+
+// modified code from from https://github.com/saikou-app/saikou/blob/main/app/src/main/java/ani/saikou/others/Kitsu.kt
+// GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md
+object Kitsu {
+ private suspend fun getKitsuData(query: String): KitsuResponse {
+ val headers = mapOf(
+ "Content-Type" to "application/json",
+ "Accept" to "application/json",
+ "Connection" to "keep-alive",
+ "DNT" to "1",
+ "Origin" to "https://kitsu.io"
+ )
+
+ return app.post(
+ "https://kitsu.io/api/graphql",
+ headers = headers,
+ data = mapOf("query" to query)
+ ).parsed()
+ }
+
+ private val cache: MutableMap, Map> =
+ mutableMapOf()
+
+ suspend fun getEpisodesDetails(
+ malId: String?,
+ anilistId: String?
+ ): Map? {
+ if (anilistId != null) {
+ try {
+ val map = getKitsuEpisodesDetails(anilistId, "ANILIST_ANIME")
+ if (!map.isNullOrEmpty()) return map
+ } catch (e: Exception) {
+ logError(e)
+ }
+ }
+ if (malId != null) {
+ try {
+ val map = getKitsuEpisodesDetails(malId, "MYANIMELIST_ANIME")
+ if (!map.isNullOrEmpty()) return map
+ } catch (e: Exception) {
+ logError(e)
+ }
+ }
+ return null
+ }
+
+ @Throws
+ suspend fun getKitsuEpisodesDetails(id: String, site: String): Map? {
+ require(id.isNotBlank()) {
+ "Black id"
+ }
+
+ require(site.isNotBlank()) {
+ "invalid site"
+ }
+
+ if (cache.containsKey(id to site)) {
+ return cache[id to site]
+ }
+
+ val query =
+ """
+query {
+ lookupMapping(externalId: $id, externalSite: $site) {
+ __typename
+ ... on Anime {
+ id
+ episodes(first: 2000) {
+ nodes {
+ number
+ titles {
+ canonical
+ }
+ description
+ thumbnail {
+ original {
+ url
+ }
+ }
+ }
+ }
+ }
+ }
+}"""
+ val result = getKitsuData(query)
+ val map = (result.data?.lookupMapping?.episodes?.nodes ?: return null).mapNotNull { ep ->
+ val num = ep?.num ?: return@mapNotNull null
+ num to ep
+ }.toMap()
+ if (map.isNotEmpty()) {
+ cache[id to site] = map
+ }
+ return map
+ }
+
+ data class KitsuResponse(
+ val data: Data? = null
+ ) {
+ data class Data(
+ val lookupMapping: LookupMapping? = null
+ )
+
+ data class LookupMapping(
+ val id: String? = null,
+ val episodes: Episodes? = null
+ )
+
+ data class Episodes(
+ val nodes: List? = null
+ )
+
+ data class Node(
+ @JsonProperty("number")
+ val num: Int? = null,
+ val titles: Titles? = null,
+ val description: Description? = null,
+ val thumbnail: Thumbnail? = null
+ )
+
+ data class Description(
+ val en: String? = null
+ )
+
+ data class Thumbnail(
+ val original: Original? = null
+ )
+
+ data class Original(
+ val url: String? = null
+ )
+
+ data class Titles(
+ val canonical: String? = null
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt
index 9dd5b214..28a23731 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt
@@ -33,6 +33,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
override val redirectUrl = "mallogin"
override val idPrefix = "mal"
override var mainUrl = "https://myanimelist.net"
+ val apiUrl = "https://api.myanimelist.net"
override val icon = R.drawable.mal_logo
override val requiresLogin = true
@@ -62,7 +63,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
}
override suspend fun search(name: String): List {
- val url = "https://api.myanimelist.net/v2/anime?q=$name&limit=$MAL_MAX_SEARCH_LIMIT"
+ val url = "$apiUrl/v2/anime?q=$name&limit=$MAL_MAX_SEARCH_LIMIT"
val auth = getAuth() ?: return emptyList()
val res = app.get(
url, headers = mapOf(
@@ -179,7 +180,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
name = node?.title ?: return null,
apiName = this.name,
syncId = node.id.toString(),
- url = "https://myanimelist.net/anime/${node.id}",
+ url = "$mainUrl/anime/${node.id}",
posterUrl = node.main_picture?.large
)
}
@@ -187,7 +188,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
override suspend fun getResult(id: String): SyncAPI.SyncResult? {
val internalId = id.toIntOrNull() ?: return null
val url =
- "https://api.myanimelist.net/v2/anime/$internalId?fields=id,title,main_picture,alternative_titles,start_date,end_date,synopsis,mean,rank,popularity,num_list_users,num_scoring_users,nsfw,created_at,updated_at,media_type,status,genres,my_list_status,num_episodes,start_season,broadcast,source,average_episode_duration,rating,pictures,background,related_anime,related_manga,recommendations,studios,statistics"
+ "$apiUrl/v2/anime/$internalId?fields=id,title,main_picture,alternative_titles,start_date,end_date,synopsis,mean,rank,popularity,num_list_users,num_scoring_users,nsfw,created_at,updated_at,media_type,status,genres,my_list_status,num_episodes,start_season,broadcast,source,average_episode_duration,rating,pictures,background,related_anime,related_manga,recommendations,studios,statistics"
val res = app.get(
url, headers = mapOf(
"Authorization" to "Bearer " + (getAuth() ?: return null)
@@ -195,7 +196,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
).text
return mapper.readValue(res).let { malAnime ->
SyncAPI.SyncResult(
- id = malAnime.id?.toString()!!,
+ id = internalId.toString(),
totalEpisodes = malAnime.numEpisodes,
title = malAnime.title,
publicScore = malAnime.mean?.toFloat()?.times(1000)?.toInt(),
@@ -203,13 +204,14 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
synopsis = malAnime.synopsis,
airStatus = when (malAnime.status) {
"finished_airing" -> ShowStatus.Completed
- "airing" -> ShowStatus.Ongoing
+ "currently_airing" -> ShowStatus.Ongoing
+ //"not_yet_aired"
else -> null
},
nextAiring = null,
studio = malAnime.studios?.mapNotNull { it.name },
genres = malAnime.genres?.map { it.name },
- trailerUrl = null,
+ trailers = null,
startDate = parseDate(malAnime.startDate),
endDate = parseDate(malAnime.endDate),
recommendations = malAnime.recommendations?.mapNotNull { rec ->
@@ -260,7 +262,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
val currentCode = sanitizer["code"]!!
val res = app.post(
- "https://myanimelist.net/v1/oauth2/token",
+ "$mainUrl/v1/oauth2/token",
data = mapOf(
"client_id" to key,
"code" to currentCode,
@@ -292,7 +294,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
.replace("/", "_").replace("\n", "")
val codeChallenge = codeVerifier
val request =
- "https://myanimelist.net/v1/oauth2/authorize?response_type=code&client_id=$key&code_challenge=$codeChallenge&state=RequestID$requestId"
+ "$mainUrl/v1/oauth2/authorize?response_type=code&client_id=$key&code_challenge=$codeChallenge&state=RequestID$requestId"
openBrowser(request)
}
@@ -318,7 +320,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
private suspend fun refreshToken() {
try {
val res = app.post(
- "https://myanimelist.net/v1/oauth2/token",
+ "$mainUrl/v1/oauth2/token",
data = mapOf(
"client_id" to key,
"grant_type" to "refresh_token",
@@ -451,7 +453,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
// Very lackluster docs
// https://myanimelist.net/apiconfig/references/api/v2#operation/users_user_id_animelist_get
val url =
- "https://api.myanimelist.net/v2/users/$user/animelist?fields=list_status,num_episodes,media_type,status,start_date,end_date,synopsis,alternative_titles,mean,genres,rank,num_list_users,nsfw,average_episode_duration,num_favorites,popularity,num_scoring_users,start_season,favorites_info,broadcast,created_at,updated_at&nsfw=1&limit=100&offset=$offset"
+ "$apiUrl/v2/users/$user/animelist?fields=list_status,num_episodes,media_type,status,start_date,end_date,synopsis,alternative_titles,mean,genres,rank,num_list_users,nsfw,average_episode_duration,num_favorites,popularity,num_scoring_users,start_season,favorites_info,broadcast,created_at,updated_at&nsfw=1&limit=100&offset=$offset"
val res = app.get(
url, headers = mapOf(
"Authorization" to "Bearer $auth",
@@ -463,7 +465,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
private suspend fun getDataAboutMalId(id: Int): SmallMalAnime? {
// https://myanimelist.net/apiconfig/references/api/v2#operation/anime_anime_id_get
val url =
- "https://api.myanimelist.net/v2/anime/$id?fields=id,title,num_episodes,my_list_status"
+ "$apiUrl/v2/anime/$id?fields=id,title,num_episodes,my_list_status"
val res = app.get(
url, headers = mapOf(
"Authorization" to "Bearer " + (getAuth() ?: return null)
@@ -481,7 +483,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
checkMalToken()
while (!isDone) {
val res = app.get(
- "https://api.myanimelist.net/v2/users/$user/animelist?fields=list_status&limit=1000&offset=${index * 1000}",
+ "$apiUrl/v2/users/$user/animelist?fields=list_status&limit=1000&offset=${index * 1000}",
headers = mapOf(
"Authorization" to "Bearer " + (getAuth() ?: return)
), cacheTime = 0
@@ -532,10 +534,10 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
}
private suspend fun checkMalToken() {
- if (unixTime > getKey(
+ if (unixTime > (getKey(
accountId,
MAL_UNIXTIME_KEY
- ) ?: 0L
+ ) ?: 0L)
) {
refreshToken()
}
@@ -544,7 +546,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
private suspend fun getMalUser(setSettings: Boolean = true): MalUser? {
checkMalToken()
val res = app.get(
- "https://api.myanimelist.net/v2/users/@me",
+ "$apiUrl/v2/users/@me",
headers = mapOf(
"Authorization" to "Bearer " + (getAuth() ?: return null)
), cacheTime = 0
@@ -620,7 +622,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
).filter { it.value != null } as Map
return app.put(
- "https://api.myanimelist.net/v2/anime/$id/my_list_status",
+ "$apiUrl/v2/anime/$id/my_list_status",
headers = mapOf(
"Authorization" to "Bearer " + (getAuth() ?: return null)
),
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
index d1ba5f12..b2219d1c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
@@ -13,12 +13,12 @@ class APIRepository(val api: MainAPI) {
val noneApi = object : MainAPI() {
override var name = "None"
override val supportedTypes = emptySet()
- override val lang = ""
+ override var lang = ""
}
val randomApi = object : MainAPI() {
override var name = "Random"
override val supportedTypes = emptySet()
- override val lang = ""
+ override var lang = ""
}
fun isInvalidData(data: String): Boolean {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
index a5260e73..d667006d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
@@ -1,17 +1,11 @@
package com.lagradost.cloudstream3.ui.player
-import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.util.Log
-import android.util.SparseArray
import android.widget.FrameLayout
-import androidx.core.util.forEach
-import at.huber.youtubeExtractor.VideoMeta
-import at.huber.youtubeExtractor.YouTubeExtractor
-import at.huber.youtubeExtractor.YtFile
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
@@ -38,7 +32,6 @@ import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorUri
-import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
import java.io.File
import javax.net.ssl.HttpsURLConnection
@@ -334,7 +327,6 @@ class CS3IPlayer : IPlayer {
}
companion object {
- private var ytVideos: MutableMap = mutableMapOf()
private var simpleCache: SimpleCache? = null
var requestSubtitleUpdate: (() -> Unit)? = null
@@ -534,7 +526,6 @@ class CS3IPlayer : IPlayer {
)
setHandleAudioBecomingNoisy(true)
setPlaybackSpeed(playBackSpeed)
-
}
}
}
@@ -711,7 +702,7 @@ class CS3IPlayer : IPlayer {
if (playWhenReady) {
when (playbackState) {
Player.STATE_READY -> {
- requestAutoFocus?.invoke()
+
}
Player.STATE_ENDED -> {
handleEvent(CSPlayerEvent.NextEpisode)
@@ -740,6 +731,7 @@ class CS3IPlayer : IPlayer {
override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying)
if (isPlaying) {
+ requestAutoFocus?.invoke()
onRenderFirst()
}
}
@@ -886,55 +878,9 @@ class CS3IPlayer : IPlayer {
return Pair(subSources, activeSubtitles)
}
-
- fun loadYtFile(context: Context, yt: YtFile) {
- loadOnlinePlayer(
- context,
- ExtractorLink(
- "YouTube",
- "",
- yt.url,
- "",
- yt.format?.height ?: Qualities.Unknown.value
- )
- )
- }
-
private fun loadOnlinePlayer(context: Context, link: ExtractorLink) {
Log.i(TAG, "loadOnlinePlayer $link")
try {
- if (link.url.contains("youtube.com")) {
- val ytLink = link.url.replace("/embed/", "/watch?v=")
- ytVideos[ytLink]?.let {
- loadYtFile(context, it)
- return
- }
- val ytExtractor =
- @SuppressLint("StaticFieldLeak")
- object : YouTubeExtractor(context) {
- override fun onExtractionComplete(
- ytFiles: SparseArray?,
- videoMeta: VideoMeta?
- ) {
- var yt: YtFile? = null
- ytFiles?.forEach { _, value ->
- if ((yt?.format?.height ?: 0) < (value.format?.height
- ?: -1) && (value.format?.audioBitrate ?: -1) > 0
- ) {
- yt = value
- }
- }
- yt?.let { ytf ->
- ytVideos[ytLink] = ytf
- loadYtFile(context, ytf)
- }
- }
- }
- Log.i(TAG, "YouTube extraction on $ytLink")
- ytExtractor.extract(ytLink)
- return
- }
-
currentLink = link
if (ignoreSSL) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt
index 7e9174b7..bd94560a 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt
@@ -229,6 +229,15 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
}
}
+ val playerSourceMove = if (isShowing) 0f else -50.toPx.toFloat()
+ player_open_source?.let {
+ ObjectAnimator.ofFloat(it, "translationY", playerSourceMove).apply {
+ duration = 200
+ start()
+ }
+ }
+
+
if (!isLocked) {
player_ffwd_holder?.alpha = 1f
player_rew_holder?.alpha = 1f
@@ -251,6 +260,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
}
bottom_player_bar?.startAnimation(fadeAnimation)
+ player_open_source?.startAnimation(fadeAnimation)
player_top_holder?.startAnimation(fadeAnimation)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
index b7b1c57a..99924f22 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
@@ -7,6 +7,7 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.LayoutRes
+import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar
import androidx.recyclerview.widget.RecyclerView
@@ -21,7 +22,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager
import kotlinx.android.synthetic.main.result_episode.view.*
-import kotlinx.android.synthetic.main.result_episode.view.episode_holder
import kotlinx.android.synthetic.main.result_episode.view.episode_text
import kotlinx.android.synthetic.main.result_episode_large.view.*
import kotlinx.android.synthetic.main.result_episode_large.view.episode_filler
@@ -47,6 +47,7 @@ const val ACTION_SHOW_OPTIONS = 10
const val ACTION_CLICK_DEFAULT = 11
const val ACTION_SHOW_TOAST = 12
+const val ACTION_SHOW_DESCRIPTION = 15
const val ACTION_DOWNLOAD_EPISODE_SUBTITLE = 13
const val ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR = 14
@@ -93,10 +94,10 @@ class EpisodeAdapter(
@LayoutRes
private var layout: Int = 0
fun updateLayout() {
- layout =
- if (cardList.filter { it.poster != null }.size >= cardList.size / 2f) // If over half has posters then use the large layout
- R.layout.result_episode_large
- else R.layout.result_episode
+ // layout =
+ // if (cardList.filter { it.poster != null }.size >= cardList.size / 2f) // If over half has posters then use the large layout
+ // R.layout.result_episode_large
+ // else R.layout.result_episode
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
@@ -105,7 +106,7 @@ class EpisodeAdapter(
else R.layout.result_episode*/
return EpisodeCardViewHolder(
- LayoutInflater.from(parent.context).inflate(layout, parent, false),
+ LayoutInflater.from(parent.context).inflate(R.layout.result_episode_both, parent, false),
hasDownloadSupport,
clickCallback,
downloadClickCallback
@@ -134,27 +135,39 @@ class EpisodeAdapter(
) : RecyclerView.ViewHolder(itemView), DownloadButtonViewHolder {
override var downloadButton = EasyDownloadButton()
- private val episodeText: TextView = itemView.episode_text
- private val episodeFiller: MaterialButton? = itemView.episode_filler
- private val episodeRating: TextView? = itemView.episode_rating
- private val episodeDescript: TextView? = itemView.episode_descript
- private val episodeProgress: ContentLoadingProgressBar? = itemView.episode_progress
- private val episodePoster: ImageView? = itemView.episode_poster
-
- private val episodeDownloadBar: ContentLoadingProgressBar = itemView.result_episode_progress_downloaded
- private val episodeDownloadImage: ImageView = itemView.result_episode_download
-
- private val episodeHolder = itemView.episode_holder
+ var episodeDownloadBar: ContentLoadingProgressBar? = null
+ var episodeDownloadImage: ImageView? = null
var localCard: ResultEpisode? = null
@SuppressLint("SetTextI18n")
fun bind(card: ResultEpisode) {
localCard = card
- val name = if (card.name == null) "${episodeText.context.getString(R.string.episode)} ${card.episode}" else "${card.episode}. ${card.name}"
+ val (parentView,otherView) = if(card.poster == null) {
+ itemView.episode_holder to itemView.episode_holder_large
+ } else {
+ itemView.episode_holder_large to itemView.episode_holder
+ }
+ parentView.isVisible = true
+ otherView.isVisible = false
+
+ val episodeText: TextView = parentView.episode_text
+ val episodeFiller: MaterialButton? = parentView.episode_filler
+ val episodeRating: TextView? = parentView.episode_rating
+ val episodeDescript: TextView? = parentView.episode_descript
+ val episodeProgress: ContentLoadingProgressBar? = parentView.episode_progress
+ val episodePoster: ImageView? = parentView.episode_poster
+
+ episodeDownloadBar =
+ parentView.result_episode_progress_downloaded
+ episodeDownloadImage = parentView.result_episode_download
+
+ val name =
+ if (card.name == null) "${episodeText.context.getString(R.string.episode)} ${card.episode}" else "${card.episode}. ${card.name}"
episodeFiller?.isVisible = card.isFiller == true
- episodeText.text = name//if(card.isFiller == true) episodeText.context.getString(R.string.filler).format(name) else name
+ episodeText.text =
+ name//if(card.isFiller == true) episodeText.context.getString(R.string.filler).format(name) else name
episodeText.isSelected = true // is needed for text repeating
val displayPos = card.getDisplayPosition()
@@ -171,16 +184,20 @@ class EpisodeAdapter(
}
if (card.rating != null) {
- episodeRating?.text = episodeRating?.context?.getString(R.string.rated_format)?.format(card.rating.toFloat() / 10f)
+ episodeRating?.text = episodeRating?.context?.getString(R.string.rated_format)
+ ?.format(card.rating.toFloat() / 10f)
} else {
episodeRating?.text = ""
}
- if (card.description != null) {
- episodeDescript?.visibility = View.VISIBLE
- episodeDescript?.text = card.description
- } else {
- episodeDescript?.visibility = View.GONE
+ episodeRating?.isGone = episodeRating?.text.isNullOrBlank()
+
+ episodeDescript?.apply {
+ text = card.description ?: ""
+ isGone = text.isNullOrBlank()
+ setOnClickListener {
+ clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_DESCRIPTION, card))
+ }
}
episodePoster?.setOnClickListener {
@@ -192,34 +209,42 @@ class EpisodeAdapter(
return@setOnLongClickListener true
}
- episodeHolder.setOnClickListener {
+ parentView.setOnClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card))
}
- if (episodeHolder.context.isTrueTvSettings()) {
- episodeHolder.isFocusable = true
- episodeHolder.isFocusableInTouchMode = true
- episodeHolder.touchscreenBlocksFocus = false
+ if (parentView.context.isTrueTvSettings()) {
+ parentView.isFocusable = true
+ parentView.isFocusableInTouchMode = true
+ parentView.touchscreenBlocksFocus = false
}
- episodeHolder.setOnLongClickListener {
+ parentView.setOnLongClickListener {
clickCallback.invoke(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
return@setOnLongClickListener true
}
- episodeDownloadImage.isVisible = hasDownloadSupport
- episodeDownloadBar.isVisible = hasDownloadSupport
+ episodeDownloadImage?.isVisible = hasDownloadSupport
+ episodeDownloadBar?.isVisible = hasDownloadSupport
+ reattachDownloadButton()
}
override fun reattachDownloadButton() {
downloadButton.dispose()
val card = localCard
if (hasDownloadSupport && card != null) {
- val downloadInfo = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(itemView.context, card.id)
+ val downloadInfo = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(
+ itemView.context,
+ card.id
+ )
downloadButton.setUpButton(
- downloadInfo?.fileLength, downloadInfo?.totalBytes, episodeDownloadBar, episodeDownloadImage, null,
+ downloadInfo?.fileLength,
+ downloadInfo?.totalBytes,
+ episodeDownloadBar ?: return,
+ episodeDownloadImage ?: return,
+ null,
VideoDownloadHelper.DownloadEpisodeCached(
card.name,
card.poster,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
index a1ddebe6..a787bcf0 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
@@ -41,6 +41,7 @@ import com.google.android.material.button.MaterialButton
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiFromName
import com.lagradost.cloudstream3.APIHolder.getId
+import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.getCastSession
import com.lagradost.cloudstream3.CommonActivity.showToast
@@ -50,6 +51,7 @@ import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton
+import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
import com.lagradost.cloudstream3.ui.player.SubtitleData
@@ -88,8 +90,10 @@ import com.lagradost.cloudstream3.utils.VideoDownloadManager.getFileName
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
import kotlinx.android.synthetic.main.fragment_result.*
import kotlinx.android.synthetic.main.fragment_result_swipe.*
+import kotlinx.android.synthetic.main.fragment_trailer.*
import kotlinx.android.synthetic.main.result_recommendations.*
import kotlinx.android.synthetic.main.result_sync.*
+import kotlinx.android.synthetic.main.trailer_custom_layout.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.runBlocking
@@ -599,7 +603,7 @@ class ResultFragment : ResultTrailerPlayer() {
setFormatText(result_meta_rating, R.string.rating_format, rating?.div(1000f))
}
- var currentTrailers: List = emptyList()
+ var currentTrailers: List = emptyList()
var currentTrailerIndex = 0
override fun nextMirror() {
@@ -608,48 +612,43 @@ class ResultFragment : ResultTrailerPlayer() {
}
override fun playerError(exception: Exception) {
- if (player.getIsPlaying()) // because we dont want random toasts in player
+ if (player.getIsPlaying()) { // because we dont want random toasts in player
super.playerError(exception)
+ } else {
+ nextMirror()
+ }
}
private fun loadTrailer(index: Int? = null) {
- currentTrailers.getOrNull(index ?: currentTrailerIndex)?.let { trailer ->
- //if(trailer.contains("youtube.com")) { // wont load in exo
- // nextMirror()
- // return
- //}
- context?.let { ctx ->
- player.onPause()
- player.loadPlayer(
- ctx,
- false,
- ExtractorLink(
- "",
- "Trailer",
+ val isSuccess =
+ currentTrailers.getOrNull(index ?: currentTrailerIndex)?.let { trailer ->
+ context?.let { ctx ->
+ player.onPause()
+ player.loadPlayer(
+ ctx,
+ false,
trailer,
- "",
- Qualities.Unknown.value
- ),
- null,
- startPosition = 0L,
- subtitles = emptySet(),
- subtitle = null,
- autoPlay = false
- )
+ null,
+ startPosition = 0L,
+ subtitles = emptySet(),
+ subtitle = null,
+ autoPlay = false
+ )
+ true
+ } ?: run {
+ false
+ }
+ } ?: run {
+ false
}
- }
+ result_trailer_loading?.isVisible = isSuccess
}
- private fun setTrailers(trailers: List?) {
- context?.let { ctx ->
- if (ctx.isTvSettings()) return
- val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
- val showTrailers =
- settingsManager.getBoolean(ctx.getString(R.string.show_trailers_key), true)
- if (!showTrailers) return
- currentTrailers = trailers ?: emptyList()
- loadTrailer()
- }
+ private fun setTrailers(trailers: List?) {
+ context?.updateHasTrailers()
+ if (!LoadResponse.isTrailersEnabled) return
+ currentTrailers = trailers?.sortedBy { -it.quality } ?: emptyList()
+ loadTrailer()
}
private fun setActors(actors: List?) {
@@ -758,6 +757,12 @@ class ResultFragment : ResultTrailerPlayer() {
result_overlapping_panels?.setStartPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
result_overlapping_panels?.setEndPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
+ player_open_source?.setOnClickListener {
+ currentTrailers.getOrNull(currentTrailerIndex)?.let {
+ context?.openBrowser(it.url)
+ }
+ }
+
updateUIListener = ::updateUI
val restart = arguments?.getBoolean(RESTART_BUNDLE) ?: false
@@ -767,6 +772,7 @@ class ResultFragment : ResultTrailerPlayer() {
activity?.window?.decorView?.clearFocus()
hideKeyboard()
+ context?.updateHasTrailers()
activity?.loadCache()
activity?.fixPaddingStatusbar(result_top_bar)
@@ -828,6 +834,12 @@ class ResultFragment : ResultTrailerPlayer() {
} else if (dy < -5) {
result_bookmark_fab?.extend()
}
+ if (!isFullScreenPlayer && player.getIsPlaying()) {
+ if (scrollY > (player_background?.height ?: scrollY)) {
+ player.handleEvent(CSPlayerEvent.Pause)
+ }
+ }
+
//result_poster_blur_holder?.translationY = -scrollY.toFloat()
})
@@ -975,6 +987,7 @@ class ResultFragment : ResultTrailerPlayer() {
}
ACTION_CHROME_CAST_EPISODE -> requireLinks(true)
ACTION_CHROME_CAST_MIRROR -> requireLinks(true)
+ ACTION_SHOW_DESCRIPTION -> true
else -> requireLinks(false)
}
if (!isLoaded) return@main // CANT LOAD
@@ -984,6 +997,14 @@ class ResultFragment : ResultTrailerPlayer() {
showToast(activity, R.string.play_episode_toast, Toast.LENGTH_SHORT)
}
+ ACTION_SHOW_DESCRIPTION -> {
+ val builder: AlertDialog.Builder =
+ AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom)
+ builder.setMessage(episodeClick.data.description ?: return@main)
+ .setTitle(R.string.torrent_plot)
+ .show()
+ }
+
ACTION_CLICK_DEFAULT -> {
context?.let { ctx ->
if (ctx.isConnectedToChromecast()) {
@@ -1431,7 +1452,7 @@ class ResultFragment : ResultTrailerPlayer() {
val d = meta.value
result_sync_episodes?.progress = currentSyncProgress * 1000
setSyncMaxEpisodes(d.totalEpisodes)
- viewModel.setMeta(d)
+ viewModel.setMeta(d, syncdata)
}
is Resource.Loading -> {
result_sync_max_episodes?.text =
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt
index ebc2c5be..b513a4c1 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt
@@ -53,6 +53,7 @@ open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreen
screenHeight
}
+ result_trailer_loading?.isVisible = false
player_background?.apply {
isVisible = true
layoutParams =
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt
index 633e32a4..cdeda5b2 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt
@@ -13,17 +13,22 @@ import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.AcraApplication.Companion.context
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
+import com.lagradost.cloudstream3.LoadResponse.Companion.getAniListId
+import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId
import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider
import com.lagradost.cloudstream3.animeproviders.NineAnimeProvider
import com.lagradost.cloudstream3.metaproviders.SyncRedirector
import com.lagradost.cloudstream3.mvvm.Resource
+import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.syncproviders.SyncAPI
+import com.lagradost.cloudstream3.syncproviders.providers.Kitsu.getEpisodesDetails
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.player.IGenerator
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
import com.lagradost.cloudstream3.ui.player.SubtitleData
+import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
@@ -70,7 +75,6 @@ class ResultViewModel : ViewModel() {
val dubStatus: LiveData get() = _dubStatus
private val _dubStatus: MutableLiveData = MutableLiveData()
- private val page: MutableLiveData = MutableLiveData()
val id: MutableLiveData = MutableLiveData()
val selectedSeason: MutableLiveData = MutableLiveData(-2)
val seasonSelections: MutableLiveData> = MutableLiveData()
@@ -88,11 +92,12 @@ class ResultViewModel : ViewModel() {
fun updateWatchStatus(status: WatchType) = viewModelScope.launch {
val currentId = id.value ?: return@launch
_watchStatus.postValue(status)
- val resultPage = page.value
+ val resultPage = _resultResponse.value
withContext(Dispatchers.IO) {
setResultWatchState(currentId, status.internalId)
- if (resultPage != null) {
+ if (resultPage != null && resultPage is Resource.Success) {
+ val resultPageData = resultPage.value
val current = getBookmarkedData(currentId)
val currentTime = System.currentTimeMillis()
setBookmarkedData(
@@ -101,12 +106,12 @@ class ResultViewModel : ViewModel() {
currentId,
current?.bookmarkedTime ?: currentTime,
currentTime,
- resultPage.name,
- resultPage.url,
- resultPage.apiName,
- resultPage.type,
- resultPage.posterUrl,
- resultPage.year
+ resultPageData.name,
+ resultPageData.url,
+ resultPageData.apiName,
+ resultPageData.type,
+ resultPageData.posterUrl,
+ resultPageData.year
)
)
}
@@ -118,20 +123,29 @@ class ResultViewModel : ViewModel() {
}
var lastMeta: SyncAPI.SyncResult? = null
- private fun applyMeta(resp: LoadResponse, meta: SyncAPI.SyncResult?): LoadResponse {
- if (meta == null) return resp
- lastMeta = meta
- return resp.apply {
+ var lastSync: Map? = null
+
+ private suspend fun applyMeta(
+ resp: LoadResponse,
+ meta: SyncAPI.SyncResult?,
+ syncs: Map? = null
+ ): Pair {
+ if (meta == null) return resp to false
+ var updateEpisodes = false
+ val out = 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
- addTrailer(meta.trailerUrl)
posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl
actors = actors ?: meta.actors
+ for ((k, v) in syncs ?: emptyMap()) {
+ syncData[k] = v
+ }
+
val realRecommendations = ArrayList()
val apiNames = listOf(GogoanimeProvider().name, NineAnimeProvider().name)
meta.recommendations?.forEach { rec ->
@@ -142,15 +156,55 @@ class ResultViewModel : ViewModel() {
recommendations = recommendations?.union(realRecommendations)?.toList()
?: realRecommendations
+
+ argamap({
+ addTrailer(meta.trailers)
+ }, {
+ if (this !is AnimeLoadResponse) return@argamap
+ val map = getEpisodesDetails(getMalId(), getAniListId())
+ if (map.isNullOrEmpty()) return@argamap
+ updateEpisodes = DubStatus.values().map { dubStatus ->
+ val current =
+ this.episodes[dubStatus]?.sortedBy { it.episode ?: 0 }?.toMutableList()
+ if (current.isNullOrEmpty()) return@map false
+ val episodes = current.mapIndexed { index, ep -> ep.episode ?: (index + 1) }
+ var updateCount = 0
+ map.forEach { (episode, node) ->
+ episodes.binarySearch(episode).let { index ->
+ current.getOrNull(index)?.let { currentEp ->
+ current[index] = currentEp.apply {
+ updateCount++
+ this.description = this.description ?: node.description?.en
+ this.name = this.name ?: node.titles?.canonical
+ this.episode = this.episode ?: node.num ?: episodes[index]
+ this.posterUrl = this.posterUrl ?: node.thumbnail?.original?.url
+ }
+ }
+ }
+ }
+ this.episodes[dubStatus] = current
+
+ updateCount > 0
+ }.any { it }
+ })
}
+ return out to updateEpisodes
}
- fun setMeta(meta: SyncAPI.SyncResult) {
- Log.i(TAG, "setMeta")
- (result.value as? Resource.Success?)?.value?.let { resp ->
- _resultResponse.postValue(Resource.Success(applyMeta(resp, meta)))
+ fun setMeta(meta: SyncAPI.SyncResult, syncs: Map?) =
+ viewModelScope.launch {
+ Log.i(TAG, "setMeta")
+ lastMeta = meta
+ lastSync = syncs
+ val (value, updateEpisodes) = ioWork {
+ (result.value as? Resource.Success?)?.value?.let { resp ->
+ return@ioWork applyMeta(resp, meta, syncs)
+ }
+ return@ioWork null to null
+ }
+ _resultResponse.postValue(Resource.Success(value ?: return@launch))
+ if (updateEpisodes ?: return@launch) updateEpisodes(value, lastShowFillers)
}
- }
private fun loadWatchStatus(localId: Int? = null) {
val currentId = localId ?: id.value ?: return
@@ -310,6 +364,159 @@ class ResultViewModel : ViewModel() {
return name
}
+ var lastShowFillers = false
+ private suspend fun updateEpisodes(loadResponse: LoadResponse, showFillers: Boolean) {
+ Log.i(TAG, "updateEpisodes")
+ try {
+ lastShowFillers = showFillers
+ val mainId = loadResponse.getId()
+
+ when (loadResponse) {
+ is AnimeLoadResponse -> {
+ if (loadResponse.episodes.isEmpty()) {
+ _dubSubEpisodes.postValue(emptyMap())
+ return
+ }
+
+// val status = getDub(mainId)
+ val statuses = loadResponse.episodes.map { it.key }
+
+ // Extremely bruh to have to take in context here, but I'm not sure how to do this in a better way :(
+ val preferDub = context?.getApiDubstatusSettings()
+ ?.contains(DubStatus.Dubbed) == true
+
+ // 3 statements because there can be only dub even if you do not prefer it.
+ val dubStatus =
+ if (preferDub && statuses.contains(DubStatus.Dubbed)) DubStatus.Dubbed
+ else if (!preferDub && statuses.contains(DubStatus.Subbed)) DubStatus.Subbed
+ else statuses.first()
+
+ val fillerEpisodes =
+ if (showFillers) safeApiCall { getFillerEpisodes(loadResponse.name) } else null
+
+ val existingEpisodes = HashSet()
+ val res = loadResponse.episodes.map { ep ->
+ val episodes = ArrayList()
+ val idIndex = ep.key.id
+ for ((index, i) in ep.value.withIndex()) {
+ val episode = i.episode ?: (index + 1)
+ val id = mainId + episode + idIndex * 1000000
+ if (!existingEpisodes.contains(episode)) {
+ existingEpisodes.add(id)
+ episodes.add(buildResultEpisode(
+ loadResponse.name,
+ filterName(i.name),
+ i.posterUrl,
+ episode,
+ i.season,
+ i.data,
+ loadResponse.apiName,
+ id,
+ index,
+ i.rating,
+ i.description,
+ if (fillerEpisodes is Resource.Success) fillerEpisodes.value?.let {
+ it.contains(episode) && it[episode] == true
+ } ?: false else false,
+ loadResponse.type,
+ mainId
+ ))
+ }
+ }
+
+ Pair(ep.key, episodes)
+ }.toMap()
+
+ // These posts needs to be in this order as to make the preferDub in ResultFragment work
+ _dubSubEpisodes.postValue(res)
+ res[dubStatus]?.let { episodes ->
+ updateEpisodes(mainId, episodes, -1)
+ }
+ _dubStatus.postValue(dubStatus)
+ _dubSubSelections.postValue(loadResponse.episodes.keys)
+ }
+
+ is TvSeriesLoadResponse -> {
+ val episodes = ArrayList()
+ val existingEpisodes = HashSet()
+ for ((index, episode) in loadResponse.episodes.sortedBy {
+ (it.season?.times(10000) ?: 0) + (it.episode ?: 0)
+ }.withIndex()) {
+ val episodeIndex = episode.episode ?: (index + 1)
+ val id =
+ mainId + (episode.season?.times(100000) ?: 0) + episodeIndex + 1
+ if (!existingEpisodes.contains(id)) {
+ existingEpisodes.add(id)
+ episodes.add(
+ buildResultEpisode(
+ loadResponse.name,
+ filterName(episode.name),
+ episode.posterUrl,
+ episodeIndex,
+ episode.season,
+ episode.data,
+ loadResponse.apiName,
+ id,
+ index,
+ episode.rating,
+ episode.description,
+ null,
+ loadResponse.type,
+ mainId
+ )
+ )
+ }
+ }
+ updateEpisodes(mainId, episodes, -1)
+ }
+ is MovieLoadResponse -> {
+ buildResultEpisode(
+ loadResponse.name,
+ loadResponse.name,
+ null,
+ 0,
+ null,
+ loadResponse.dataUrl,
+ loadResponse.apiName,
+ (mainId), // HAS SAME ID
+ 0,
+ null,
+ null,
+ null,
+ loadResponse.type,
+ mainId
+ ).let {
+ updateEpisodes(mainId, listOf(it), -1)
+ }
+ }
+ is TorrentLoadResponse -> {
+ updateEpisodes(
+ mainId, listOf(
+ buildResultEpisode(
+ loadResponse.name,
+ loadResponse.name,
+ null,
+ 0,
+ null,
+ loadResponse.torrent ?: loadResponse.magnet ?: "",
+ loadResponse.apiName,
+ (mainId), // HAS SAME ID
+ 0,
+ null,
+ null,
+ null,
+ loadResponse.type,
+ mainId
+ )
+ ), -1
+ )
+ }
+ }
+ } catch (e: Exception) {
+ logError(e)
+ }
+ }
+
fun load(url: String, apiName: String, showFillers: Boolean) = viewModelScope.launch {
_publicEpisodes.postValue(Resource.Loading())
_resultResponse.postValue(Resource.Loading(url))
@@ -356,8 +563,10 @@ class ResultViewModel : ViewModel() {
when (data) {
is Resource.Success -> {
- val loadResponse = applyMeta(data.value, lastMeta)
- page.postValue(loadResponse)
+ val loadResponse = if (lastMeta != null || lastSync != null) ioWork {
+ applyMeta(data.value, lastMeta, lastSync).first
+ } else data.value
+ _resultResponse.postValue(Resource.Success(loadResponse))
val mainId = loadResponse.getId()
id.postValue(mainId)
loadWatchStatus(mainId)
@@ -375,148 +584,7 @@ class ResultViewModel : ViewModel() {
System.currentTimeMillis(),
)
)
-
- when (loadResponse) {
- is AnimeLoadResponse -> {
- if (loadResponse.episodes.isEmpty()) {
- _dubSubEpisodes.postValue(emptyMap())
- return@launch
- }
-
-// val status = getDub(mainId)
- val statuses = loadResponse.episodes.map { it.key }
-
- // Extremely bruh to have to take in context here, but I'm not sure how to do this in a better way :(
- val preferDub = context?.getApiDubstatusSettings()
- ?.contains(DubStatus.Dubbed) == true
-
- // 3 statements because there can be only dub even if you do not prefer it.
- val dubStatus =
- if (preferDub && statuses.contains(DubStatus.Dubbed)) DubStatus.Dubbed
- else if (!preferDub && statuses.contains(DubStatus.Subbed)) DubStatus.Subbed
- else statuses.first()
-
- val fillerEpisodes =
- if (showFillers) safeApiCall { getFillerEpisodes(loadResponse.name) } else null
-
- val existingEpisodes = HashSet()
- val res = loadResponse.episodes.map { ep ->
- val episodes = ArrayList()
- val idIndex = ep.key.id
- for ((index, i) in ep.value.withIndex()) {
- val episode = i.episode ?: (index + 1)
- val id = mainId + episode + idIndex * 1000000
- if (!existingEpisodes.contains(episode)) {
- existingEpisodes.add(id)
- episodes.add(buildResultEpisode(
- loadResponse.name,
- filterName(i.name),
- i.posterUrl,
- episode,
- i.season,
- i.data,
- apiName,
- id,
- index,
- i.rating,
- i.description,
- if (fillerEpisodes is Resource.Success) fillerEpisodes.value?.let {
- it.contains(episode) && it[episode] == true
- } ?: false else false,
- loadResponse.type,
- mainId
- ))
- }
- }
-
- Pair(ep.key, episodes)
- }.toMap()
-
- // These posts needs to be in this order as to make the preferDub in ResultFragment work
- _dubSubEpisodes.postValue(res)
- res[dubStatus]?.let { episodes ->
- updateEpisodes(mainId, episodes, -1)
- }
- _dubStatus.postValue(dubStatus)
- _dubSubSelections.postValue(loadResponse.episodes.keys)
- }
-
- is TvSeriesLoadResponse -> {
- val episodes = ArrayList()
- val existingEpisodes = HashSet()
- for ((index, episode) in loadResponse.episodes.sortedBy {
- (it.season?.times(10000) ?: 0) + (it.episode ?: 0)
- }.withIndex()) {
- val episodeIndex = episode.episode ?: (index + 1)
- val id =
- mainId + (episode.season?.times(100000) ?: 0) + episodeIndex + 1
- if (!existingEpisodes.contains(id)) {
- existingEpisodes.add(id)
- episodes.add(
- buildResultEpisode(
- loadResponse.name,
- filterName(episode.name),
- episode.posterUrl,
- episodeIndex,
- episode.season,
- episode.data,
- apiName,
- id,
- index,
- episode.rating,
- episode.description,
- null,
- loadResponse.type,
- mainId
- )
- )
- }
- }
- updateEpisodes(mainId, episodes, -1)
- }
- is MovieLoadResponse -> {
- buildResultEpisode(
- loadResponse.name,
- loadResponse.name,
- null,
- 0,
- null,
- loadResponse.dataUrl,
- loadResponse.apiName,
- (mainId), // HAS SAME ID
- 0,
- null,
- null,
- null,
- loadResponse.type,
- mainId
- ).let {
- updateEpisodes(mainId, listOf(it), -1)
- }
- }
- is TorrentLoadResponse -> {
- updateEpisodes(
- mainId, listOf(
- buildResultEpisode(
- loadResponse.name,
- loadResponse.name,
- null,
- 0,
- null,
- loadResponse.torrent ?: loadResponse.magnet ?: "",
- loadResponse.apiName,
- (mainId), // HAS SAME ID
- 0,
- null,
- null,
- null,
- loadResponse.type,
- mainId
- )
- ), -1
- )
- }
- }
+ updateEpisodes(loadResponse, showFillers)
}
else -> Unit
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
index 22ca83a3..781736a1 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
@@ -6,12 +6,18 @@ import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.view.View
+import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
+import com.fasterxml.jackson.annotation.JsonProperty
import com.hippo.unifile.UniFile
+import com.lagradost.cloudstream3.APIHolder.allProviders
import com.lagradost.cloudstream3.AcraApplication
+import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
+import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
+import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError
@@ -20,9 +26,15 @@ import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
+import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
+import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
+import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
+import com.lagradost.cloudstream3.utils.USER_PROVIDER_API
import com.lagradost.cloudstream3.utils.VideoDownloadManager
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath
+import kotlinx.android.synthetic.main.add_remove_sites.*
+import kotlinx.android.synthetic.main.add_site_input.*
import java.io.File
class SettingsGeneral : PreferenceFragmentCompat() {
@@ -31,6 +43,17 @@ class SettingsGeneral : PreferenceFragmentCompat() {
setUpToolbar(R.string.category_general)
}
+ data class CustomSite(
+ @JsonProperty("parentJavaClass") // javaClass.simpleName
+ val parentJavaClass: String,
+ @JsonProperty("name")
+ val name: String,
+ @JsonProperty("url")
+ val url: String,
+ @JsonProperty("lang")
+ val lang: String,
+ )
+
// Open file picker
private val pathPicker =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
@@ -64,6 +87,94 @@ class SettingsGeneral : PreferenceFragmentCompat() {
setPreferencesFromResource(R.xml.settins_general, rootKey)
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
+ fun getCurrent(): MutableList {
+ return getKey>(USER_PROVIDER_API)?.toMutableList()
+ ?: mutableListOf()
+ }
+
+ fun showAdd() {
+ val providers = allProviders.distinctBy { it.javaClass }.sortedBy { it.name }
+ activity?.showDialog(
+ providers.map { "${it.name} (${it.mainUrl})" },
+ -1,
+ context?.getString(R.string.add_site_pref) ?: return,
+ true,
+ {}) { selection ->
+ val provider = providers.getOrNull(selection) ?: return@showDialog
+
+ val builder =
+ AlertDialog.Builder(context ?: return@showDialog, R.style.AlertDialogCustom)
+ .setView(R.layout.add_site_input)
+
+ val dialog = builder.create()
+ dialog.show()
+
+ dialog.text2?.text = provider.name
+ dialog.apply_btt?.setOnClickListener {
+ val name = dialog.site_name_input?.text?.toString()
+ val url = dialog.site_url_input?.text?.toString()
+ val lang = dialog.site_lang_input?.text?.toString()
+ val realLang = if (lang.isNullOrBlank()) provider.lang else lang
+ if (url.isNullOrBlank() || name.isNullOrBlank() || realLang.length != 2) {
+ showToast(activity, R.string.error_invalid_data, Toast.LENGTH_SHORT)
+ return@setOnClickListener
+ }
+
+ val current = getCurrent()
+ val newSite = CustomSite(provider.javaClass.simpleName, name, url, realLang)
+ current.add(newSite)
+ setKey(USER_PROVIDER_API, current.toTypedArray())
+
+ dialog.dismissSafe(activity)
+ }
+ dialog.cancel_btt?.setOnClickListener {
+ dialog.dismissSafe(activity)
+ }
+ }
+ }
+
+ fun showDelete() {
+ val current = getCurrent()
+
+ activity?.showMultiDialog(
+ current.map { it.name },
+ listOf(),
+ context?.getString(R.string.remove_site_pref) ?: return,
+ {}) { indexes ->
+ current.removeAll(indexes.map { current[it] })
+ setKey(USER_PROVIDER_API, current.toTypedArray())
+ }
+ }
+
+ fun showAddOrDelete() {
+ val builder =
+ AlertDialog.Builder(context ?: return, R.style.AlertDialogCustom)
+ .setView(R.layout.add_remove_sites)
+
+ val dialog = builder.create()
+ dialog.show()
+
+ dialog.add_site?.setOnClickListener {
+ showAdd()
+ dialog.dismissSafe(activity)
+ }
+ dialog.remove_site?.setOnClickListener {
+ showDelete()
+ dialog.dismissSafe(activity)
+ }
+ }
+
+ getPref(R.string.override_site_key)?.setOnPreferenceClickListener { _ ->
+
+ if (getCurrent().isEmpty()) {
+ showAdd()
+ } else {
+ showAddOrDelete()
+ }
+
+ return@setOnPreferenceClickListener true
+ }
+
getPref(R.string.legal_notice_key)?.setOnPreferenceClickListener {
val builder: AlertDialog.Builder =
AlertDialog.Builder(it.context, R.style.AlertDialogCustom)
@@ -72,7 +183,7 @@ class SettingsGeneral : PreferenceFragmentCompat() {
builder.show()
return@setOnPreferenceClickListener true
}
-
+
getPref(R.string.dns_key)?.setOnPreferenceClickListener {
val prefNames = resources.getStringArray(R.array.dns_pref)
val prefValues = resources.getIntArray(R.array.dns_pref_values)
@@ -177,6 +288,5 @@ class SettingsGeneral : PreferenceFragmentCompat() {
} catch (e: Exception) {
e.printStackTrace()
}
-
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/Coroutines.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/Coroutines.kt
index da32abaa..d5cb06f7 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/Coroutines.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/Coroutines.kt
@@ -3,28 +3,31 @@ package com.lagradost.cloudstream3.utils
import android.os.Handler
import android.os.Looper
import com.lagradost.cloudstream3.mvvm.logError
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.*
object Coroutines {
- fun main(work: suspend (() -> Unit)) : Job {
+ fun main(work: suspend (() -> Unit)): Job {
return CoroutineScope(Dispatchers.Main).launch {
work()
}
}
- fun ioSafe(work: suspend (() -> Unit)) : Job {
+ fun ioSafe(work: suspend (() -> Unit)): Job {
return CoroutineScope(Dispatchers.IO).launch {
try {
work()
- } catch (e : Exception) {
+ } catch (e: Exception) {
logError(e)
}
}
}
+ suspend fun ioWork(work: suspend (() -> T)): T {
+ return withContext(Dispatchers.IO) {
+ work()
+ }
+ }
+
fun runOnMainThread(work: (() -> Unit)) {
val mainHandler = Handler(Looper.getMainLooper())
mainHandler.post {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt
index 3ad95730..ade1fc38 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt
@@ -14,6 +14,7 @@ const val DOWNLOAD_HEADER_CACHE = "download_header_cache"
const val DOWNLOAD_EPISODE_CACHE = "download_episode_cache"
const val VIDEO_PLAYER_BRIGHTNESS = "video_player_alpha_key"
const val HOMEPAGE_API = "home_api_used"
+const val USER_PROVIDER_API = "user_custom_sites"
const val PREFERENCES_NAME = "rebuild_preference"
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
index 65c3c2c7..48622b22 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
@@ -106,6 +106,19 @@ suspend fun loadExtractor(
return false
}
+suspend fun loadExtractor(
+ url: String,
+ referer: String? = null,
+): List {
+ for (extractor in extractorApis) {
+ if (url.startsWith(extractor.mainUrl)) {
+ return extractor.getSafeUrl(url, referer) ?: emptyList()
+
+ }
+ }
+ return emptyList()
+}
+
val extractorApis: Array = arrayOf(
//AllProvider(),
WcoStream(),
@@ -205,6 +218,9 @@ val extractorApis: Array = arrayOf(
KotakAnimeid(),
Neonime8n(),
Neonime7n(),
+
+ YoutubeExtractor(),
+ YoutubeShortLinkExtractor(),
)
fun getExtractorApiFromName(name: String): ExtractorApi {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/FillerEpisodeCheck.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/FillerEpisodeCheck.kt
index 911d58e7..14d1b055 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/FillerEpisodeCheck.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/FillerEpisodeCheck.kt
@@ -11,9 +11,11 @@ object FillerEpisodeCheck {
private const val MAIN_URL = "https://www.animefillerlist.com"
var list: HashMap? = null
+ var cache: HashMap> = hashMapOf()
private fun fixName(name: String): String {
- return name.lowercase(Locale.ROOT)/*.replace(" ", "")*/.replace("-", " ").replace("[^a-zA-Z0-9 ]".toRegex(), "")
+ return name.lowercase(Locale.ROOT)/*.replace(" ", "")*/.replace("-", " ")
+ .replace("[^a-zA-Z0-9 ]".toRegex(), "")
}
private suspend fun getFillerList(): Boolean {
@@ -61,6 +63,9 @@ object FillerEpisodeCheck {
suspend fun getFillerEpisodes(query: String): HashMap? {
try {
+ cache[query]?.let {
+ return it
+ }
if (!getFillerList()) return null
val localList = list ?: return null
@@ -75,9 +80,15 @@ object FillerEpisodeCheck {
"(\\d+)" // year
)
val blackListRegex =
- Regex(""" (${blackList.joinToString(separator = "|").replace("(", "\\(").replace(")", "\\)")})""")
+ Regex(
+ """ (${
+ blackList.joinToString(separator = "|").replace("(", "\\(")
+ .replace(")", "\\)")
+ })"""
+ )
- val realQuery = fixName(query.replace(blackListRegex, "")).replace("shippuuden", "shippuden")
+ val realQuery =
+ fixName(query.replace(blackListRegex, "")).replace("shippuuden", "shippuden")
if (!localList.containsKey(realQuery)) return null
val href = localList[realQuery]?.replace(MAIN_URL, "") ?: return null // JUST IN CASE
val result = app.get("$MAIN_URL$href").text
@@ -90,6 +101,7 @@ object FillerEpisodeCheck {
hashMap[episodeNumber] = type
}
}
+ cache[query] = hashMap
return hashMap
} catch (e: Exception) {
e.printStackTrace()
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
index 0a069cec..814cf95b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
@@ -14,23 +14,33 @@ import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
import com.lagradost.cloudstream3.utils.UIHelper.setImage
object SingleSelectionHelper {
- fun Activity.showOptionSelectStringRes(
+ fun Activity?.showOptionSelectStringRes(
view: View?,
poster: String?,
options: List,
tvOptions: List = listOf(),
callback: (Pair) -> Unit
) {
- this.showOptionSelect(view, poster, options.map { this.getString(it) },tvOptions.map { this.getString(it) }, callback)
+ if(this == null) return
+
+ this.showOptionSelect(
+ view,
+ poster,
+ options.map { this.getString(it) },
+ tvOptions.map { this.getString(it) },
+ callback
+ )
}
- private fun Activity.showOptionSelect(
+ private fun Activity?.showOptionSelect(
view: View?,
poster: String?,
options: List,
tvOptions: List,
callback: (Pair) -> Unit
) {
+ if(this == null) return
+
if (this.isTvSettings()) {
val builder =
AlertDialog.Builder(this, R.style.AlertDialogCustom)
@@ -41,12 +51,13 @@ object SingleSelectionHelper {
dialog.findViewById(R.id.listview1)?.let { listView ->
listView.choiceMode = AbsListView.CHOICE_MODE_SINGLE
- listView.adapter = ArrayAdapter(this, R.layout.sort_bottom_single_choice_color).apply {
- addAll(tvOptions)
- }
+ listView.adapter =
+ ArrayAdapter(this, R.layout.sort_bottom_single_choice_color).apply {
+ addAll(tvOptions)
+ }
listView.setOnItemClickListener { _, _, i, _ ->
- callback.invoke(Pair(true,i))
+ callback.invoke(Pair(true, i))
dialog.dismissSafe(this)
}
}
@@ -62,12 +73,12 @@ object SingleSelectionHelper {
s
)
}) {
- callback(Pair(false,this.itemId))
+ callback(Pair(false, this.itemId))
}
}
}
- fun Activity.showDialog(
+ fun Activity?.showDialog(
dialog: Dialog,
items: List,
selectedIndex: List,
@@ -77,6 +88,8 @@ object SingleSelectionHelper {
callback: (List) -> Unit,
dismissCallback: () -> Unit
) {
+ if(this == null) return
+
val realShowApply = showApply || isMultiSelect
val listView = dialog.findViewById(R.id.listview1)!!
val textView = dialog.findViewById(R.id.text1)!!
@@ -145,8 +158,7 @@ object SingleSelectionHelper {
}
-
- private fun Activity.showInputDialog(
+ private fun Activity?.showInputDialog(
dialog: Dialog,
value: String,
name: String,
@@ -154,6 +166,8 @@ object SingleSelectionHelper {
callback: (String) -> Unit,
dismissCallback: () -> Unit
) {
+ if(this == null) return
+
val inputView = dialog.findViewById(R.id.nginx_text_input)!!
val textView = dialog.findViewById(R.id.text1)!!
val applyButton = dialog.findViewById(R.id.apply_btt)!!
@@ -184,13 +198,15 @@ object SingleSelectionHelper {
}
- fun Activity.showMultiDialog(
+ fun Activity?.showMultiDialog(
items: List,
selectedIndex: List,
name: String,
dismissCallback: () -> Unit,
callback: (List) -> Unit,
) {
+ if(this == null) return
+
val builder =
AlertDialog.Builder(this, R.style.AlertDialogCustom)
.setView(R.layout.bottom_selection_dialog)
@@ -200,7 +216,7 @@ object SingleSelectionHelper {
showDialog(dialog, items, selectedIndex, name, true, true, callback, dismissCallback)
}
- fun Activity.showDialog(
+ fun Activity?.showDialog(
items: List,
selectedIndex: Int,
name: String,
@@ -208,6 +224,8 @@ object SingleSelectionHelper {
dismissCallback: () -> Unit,
callback: (Int) -> Unit,
) {
+ if(this == null) return
+
val builder =
AlertDialog.Builder(this, R.style.AlertDialogCustom)
.setView(R.layout.bottom_selection_dialog)
@@ -227,14 +245,15 @@ object SingleSelectionHelper {
}
/** Only for a low amount of items */
- fun Activity.showBottomDialog(
+ fun Activity?.showBottomDialog(
items: List,
selectedIndex: Int,
name: String,
showApply: Boolean,
- dismissCallback: () -> Unit,
+ dismissCallback: () -> Unit,
callback: (Int) -> Unit,
) {
+ if (this == null) return
val builder =
BottomSheetDialog(this)
builder.setContentView(R.layout.bottom_selection_dialog)
@@ -252,12 +271,12 @@ object SingleSelectionHelper {
)
}
- fun Activity.showNginxTextInputDialog(
- name: String,
- value: String,
- textInputType: Int?,
- dismissCallback: () -> Unit,
- callback: (String) -> Unit,
+ fun Activity.showNginxTextInputDialog(
+ name: String,
+ value: String,
+ textInputType: Int?,
+ dismissCallback: () -> Unit,
+ callback: (String) -> Unit,
) {
val builder = BottomSheetDialog(this) // probably the stuff at the bottom
builder.setContentView(R.layout.bottom_input_dialog) // input layout
diff --git a/app/src/main/res/drawable/ic_baseline_add_24.xml b/app/src/main/res/drawable/ic_baseline_add_24.xml
index 70046c48..9fdd2903 100644
--- a/app/src/main/res/drawable/ic_baseline_add_24.xml
+++ b/app/src/main/res/drawable/ic_baseline_add_24.xml
@@ -1,4 +1,4 @@
-
diff --git a/app/src/main/res/layout/add_remove_sites.xml b/app/src/main/res/layout/add_remove_sites.xml
new file mode 100644
index 00000000..9ef6ad6a
--- /dev/null
+++ b/app/src/main/res/layout/add_remove_sites.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/add_site_input.xml b/app/src/main/res/layout/add_site_input.xml
new file mode 100644
index 00000000..1c61f8b4
--- /dev/null
+++ b/app/src/main/res/layout/add_site_input.xml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml
index 4c1aef8a..3a9de7ad 100644
--- a/app/src/main/res/layout/fragment_result.xml
+++ b/app/src/main/res/layout/fragment_result.xml
@@ -138,6 +138,34 @@
android:background="?attr/primaryBlackBackground"
android:orientation="vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/result_episode_large.xml b/app/src/main/res/layout/result_episode_large.xml
index 5842d42d..8fc917ec 100644
--- a/app/src/main/res/layout/result_episode_large.xml
+++ b/app/src/main/res/layout/result_episode_large.xml
@@ -5,7 +5,7 @@
android:nextFocusLeft="@id/episode_poster"
android:nextFocusRight="@id/result_episode_download"
- android:id="@+id/episode_holder"
+ android:id="@+id/episode_holder_large"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -142,11 +142,13 @@
diff --git a/app/src/main/res/layout/trailer_custom_layout.xml b/app/src/main/res/layout/trailer_custom_layout.xml
index 37a32184..ac155ad1 100644
--- a/app/src/main/res/layout/trailer_custom_layout.xml
+++ b/app/src/main/res/layout/trailer_custom_layout.xml
@@ -58,7 +58,6 @@
android:background="@color/black_overlay" />
-
+
+
+ android:layout_marginEnd="20dp"
+ android:layout_width="30dp"
+ android:layout_height="30dp" />
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b1232736..a4a22cb5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -50,6 +50,7 @@
bottom_title_key
poster_ui_key
subtitles_encoding_key
+ override_site_key
%d %s | %sMB
@@ -382,6 +383,10 @@
DNS over HTTPS
Useful for bypassing ISP blocks
+ Clone site
+ Remove site
+ Add a clone of an existing site, with a different url
+
Download path
Nginx server url
@@ -440,6 +445,9 @@
MyCoolUsername
hello@world.com
127.0.0.1
+ MyCoolSite
+ example.com
+ Language code (en)