mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Ported more files for multiplatform (#1056)
This commit is contained in:
parent
af828de8d5
commit
4d5cd288ab
12 changed files with 243 additions and 141 deletions
|
@ -29,6 +29,7 @@ import com.google.android.material.chip.ChipGroup
|
|||
import com.google.android.material.navigationrail.NavigationRailView
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.resumeApps
|
||||
import com.lagradost.cloudstream3.databinding.ToastBinding
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.ui.player.PlayerEventType
|
||||
|
|
|
@ -31,19 +31,16 @@ import java.text.SimpleDateFormat
|
|||
import java.util.*
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
const val USER_AGENT =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
|
||||
|
||||
//val baseHeader = mapOf("User-Agent" to USER_AGENT)
|
||||
val mapper = JsonMapper.builder().addModule(kotlinModule())
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!!
|
||||
|
||||
/**
|
||||
* Defines the constant for the all languages preference, if this is set then it is
|
||||
* the equivalent of all languages being set
|
||||
**/
|
||||
const val AllLanguagesName = "universal"
|
||||
|
||||
//val baseHeader = mapOf("User-Agent" to USER_AGENT)
|
||||
val mapper = JsonMapper.builder().addModule(kotlinModule())
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!!
|
||||
|
||||
object APIHolder {
|
||||
val unixTime: Long
|
||||
get() = System.currentTimeMillis() / 1000L
|
||||
|
@ -121,7 +118,8 @@ object APIHolder {
|
|||
|
||||
fun LoadResponse.getId(): Int {
|
||||
// this fixes an issue with outdated api as getLoadResponseIdFromUrl might be fucked
|
||||
return (if (this is ResultViewModel2.LoadResponseFromSearch) this.id else null) ?: getLoadResponseIdFromUrl(url, apiName)
|
||||
return (if (this is ResultViewModel2.LoadResponseFromSearch) this.id else null)
|
||||
?: getLoadResponseIdFromUrl(url, apiName)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,10 +220,15 @@ object APIHolder {
|
|||
} ?: false
|
||||
|
||||
val matchingTypes = types?.any { it.name.equals(media.format, true) } == true
|
||||
if(lessAccurate) matchingTitles || matchingTypes && matchingYears else matchingTitles && matchingTypes && matchingYears
|
||||
if (lessAccurate) matchingTitles || matchingTypes && matchingYears else matchingTitles && matchingTypes && matchingYears
|
||||
} ?: return null
|
||||
|
||||
Tracker(res.idMal, res.id.toString(), res.coverImage?.extraLarge ?: res.coverImage?.large, res.bannerImage)
|
||||
Tracker(
|
||||
res.idMal,
|
||||
res.id.toString(),
|
||||
res.coverImage?.extraLarge ?: res.coverImage?.large,
|
||||
res.bannerImage
|
||||
)
|
||||
} catch (t: Throwable) {
|
||||
logError(t)
|
||||
null
|
||||
|
@ -866,6 +869,7 @@ enum class TvType(value: Int?) {
|
|||
Others(12),
|
||||
Music(13),
|
||||
AudioBook(14),
|
||||
|
||||
/** Wont load the built in player, make your own interaction */
|
||||
CustomMedia(15),
|
||||
}
|
||||
|
@ -1253,13 +1257,15 @@ interface LoadResponse {
|
|||
|
||||
fun LoadResponse.getImdbId(): String? {
|
||||
return normalSafeApiCall {
|
||||
SimklApi.readIdFromString(this.syncData[simklIdPrefix])?.get(SimklApi.Companion.SyncServices.Imdb)
|
||||
SimklApi.readIdFromString(this.syncData[simklIdPrefix])
|
||||
?.get(SimklApi.Companion.SyncServices.Imdb)
|
||||
}
|
||||
}
|
||||
|
||||
fun LoadResponse.getTMDbId(): String? {
|
||||
return normalSafeApiCall {
|
||||
SimklApi.readIdFromString(this.syncData[simklIdPrefix])?.get(SimklApi.Companion.SyncServices.Tmdb)
|
||||
SimklApi.readIdFromString(this.syncData[simklIdPrefix])
|
||||
?.get(SimklApi.Companion.SyncServices.Tmdb)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1556,8 +1562,26 @@ data class TorrentLoadResponse(
|
|||
posterHeaders: Map<String, String>? = null,
|
||||
backgroundPosterUrl: String? = null,
|
||||
) : this(
|
||||
name, url, apiName, magnet, torrent, plot, type, posterUrl, year, rating, tags, duration, trailers,
|
||||
recommendations, actors, comingSoon, syncData, posterHeaders, backgroundPosterUrl, null
|
||||
name,
|
||||
url,
|
||||
apiName,
|
||||
magnet,
|
||||
torrent,
|
||||
plot,
|
||||
type,
|
||||
posterUrl,
|
||||
year,
|
||||
rating,
|
||||
tags,
|
||||
duration,
|
||||
trailers,
|
||||
recommendations,
|
||||
actors,
|
||||
comingSoon,
|
||||
syncData,
|
||||
posterHeaders,
|
||||
backgroundPosterUrl,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1609,7 +1633,8 @@ data class AnimeLoadResponse(
|
|||
return this.episodes.maxOf { (_, episodes) ->
|
||||
episodes.count { episodeData ->
|
||||
// Prioritize display season as actual season may be something random to fit multiple seasons into one.
|
||||
val episodeSeason = displayMap[episodeData.season] ?: episodeData.season ?: Int.MIN_VALUE
|
||||
val episodeSeason =
|
||||
displayMap[episodeData.season] ?: episodeData.season ?: Int.MIN_VALUE
|
||||
// Count all episodes from season 1 to below the current season.
|
||||
episodeSeason in 1..<season
|
||||
}
|
||||
|
@ -1646,9 +1671,31 @@ data class AnimeLoadResponse(
|
|||
seasonNames: List<SeasonData>? = null,
|
||||
backgroundPosterUrl: String? = null,
|
||||
) : this(
|
||||
engName, japName, name, url, apiName, type, posterUrl, year, episodes, showStatus, plot, tags,
|
||||
synonyms, rating, duration, trailers, recommendations, actors, comingSoon, syncData, posterHeaders,
|
||||
nextAiring, seasonNames, backgroundPosterUrl, null
|
||||
engName,
|
||||
japName,
|
||||
name,
|
||||
url,
|
||||
apiName,
|
||||
type,
|
||||
posterUrl,
|
||||
year,
|
||||
episodes,
|
||||
showStatus,
|
||||
plot,
|
||||
tags,
|
||||
synonyms,
|
||||
rating,
|
||||
duration,
|
||||
trailers,
|
||||
recommendations,
|
||||
actors,
|
||||
comingSoon,
|
||||
syncData,
|
||||
posterHeaders,
|
||||
nextAiring,
|
||||
seasonNames,
|
||||
backgroundPosterUrl,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1780,7 +1827,7 @@ data class MovieLoadResponse(
|
|||
backgroundPosterUrl: String? = null,
|
||||
) : this(
|
||||
name, url, apiName, type, dataUrl, posterUrl, year, plot, rating, tags, duration, trailers,
|
||||
recommendations, actors, comingSoon, syncData, posterHeaders, backgroundPosterUrl,null
|
||||
recommendations, actors, comingSoon, syncData, posterHeaders, backgroundPosterUrl, null
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1923,7 +1970,8 @@ data class TvSeriesLoadResponse(
|
|||
|
||||
return episodes.count { episodeData ->
|
||||
// Prioritize display season as actual season may be something random to fit multiple seasons into one.
|
||||
val episodeSeason = displayMap[episodeData.season] ?: episodeData.season ?: Int.MIN_VALUE
|
||||
val episodeSeason =
|
||||
displayMap[episodeData.season] ?: episodeData.season ?: Int.MIN_VALUE
|
||||
// Count all episodes from season 1 to below the current season.
|
||||
episodeSeason in 1..<season
|
||||
} + episode
|
||||
|
@ -1956,9 +2004,28 @@ data class TvSeriesLoadResponse(
|
|||
seasonNames: List<SeasonData>? = null,
|
||||
backgroundPosterUrl: String? = null,
|
||||
) : this(
|
||||
name, url, apiName, type, episodes, posterUrl, year, plot, showStatus, rating, tags, duration,
|
||||
trailers, recommendations, actors, comingSoon, syncData, posterHeaders, nextAiring, seasonNames,
|
||||
backgroundPosterUrl, null
|
||||
name,
|
||||
url,
|
||||
apiName,
|
||||
type,
|
||||
episodes,
|
||||
posterUrl,
|
||||
year,
|
||||
plot,
|
||||
showStatus,
|
||||
rating,
|
||||
tags,
|
||||
duration,
|
||||
trailers,
|
||||
recommendations,
|
||||
actors,
|
||||
comingSoon,
|
||||
syncData,
|
||||
posterHeaders,
|
||||
nextAiring,
|
||||
seasonNames,
|
||||
backgroundPosterUrl,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2022,6 +2089,7 @@ data class AniSearch(
|
|||
@JsonProperty("extraLarge") var extraLarge: String? = null,
|
||||
@JsonProperty("large") var large: String? = null,
|
||||
)
|
||||
|
||||
data class Title(
|
||||
@JsonProperty("romaji") var romaji: String? = null,
|
||||
@JsonProperty("english") var english: String? = null,
|
||||
|
|
|
@ -174,7 +174,6 @@ import java.net.URLDecoder
|
|||
import java.nio.charset.Charset
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
//https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898
|
||||
|
@ -187,117 +186,93 @@ import kotlin.system.exitProcess
|
|||
|
||||
//https://github.com/jellyfin/jellyfin-android/blob/6cbf0edf84a3da82347c8d59b5d5590749da81a9/app/src/main/java/org/jellyfin/mobile/bridge/ExternalPlayer.kt#L225
|
||||
|
||||
const val VLC_PACKAGE = "org.videolan.vlc"
|
||||
const val MPV_PACKAGE = "is.xyz.mpv"
|
||||
const val WEB_VIDEO_CAST_PACKAGE = "com.instantbits.cast.webvideo"
|
||||
|
||||
val VLC_COMPONENT = ComponentName(VLC_PACKAGE, "$VLC_PACKAGE.gui.video.VideoPlayerActivity")
|
||||
val MPV_COMPONENT = ComponentName(MPV_PACKAGE, "$MPV_PACKAGE.MPVActivity")
|
||||
|
||||
//TODO REFACTOR AF
|
||||
open class ResultResume(
|
||||
val packageString: String,
|
||||
val action: String = Intent.ACTION_VIEW,
|
||||
val position: String? = null,
|
||||
val duration: String? = null,
|
||||
var launcher: ActivityResultLauncher<Intent>? = null,
|
||||
) {
|
||||
val defaultTime = -1L
|
||||
|
||||
val lastId get() = "${packageString}_last_open_id"
|
||||
suspend fun launch(id: Int?, callback: suspend Intent.() -> Unit) {
|
||||
val intent = Intent(action)
|
||||
|
||||
if (id != null)
|
||||
setKey(lastId, id)
|
||||
else
|
||||
removeKey(lastId)
|
||||
|
||||
intent.setPackage(packageString)
|
||||
callback.invoke(intent)
|
||||
launcher?.launch(intent)
|
||||
}
|
||||
|
||||
open fun getPosition(intent: Intent?): Long {
|
||||
return defaultTime
|
||||
}
|
||||
|
||||
open fun getDuration(intent: Intent?): Long {
|
||||
return defaultTime
|
||||
}
|
||||
}
|
||||
|
||||
val VLC = object : ResultResume(
|
||||
VLC_PACKAGE,
|
||||
// Android 13 intent restrictions fucks up specifically launching the VLC player
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
"org.videolan.vlc.player.result"
|
||||
} else {
|
||||
Intent.ACTION_VIEW
|
||||
},
|
||||
"extra_position",
|
||||
"extra_duration",
|
||||
) {
|
||||
override fun getPosition(intent: Intent?): Long {
|
||||
return intent?.getLongExtra(this.position, defaultTime) ?: defaultTime
|
||||
}
|
||||
|
||||
override fun getDuration(intent: Intent?): Long {
|
||||
return intent?.getLongExtra(this.duration, defaultTime) ?: defaultTime
|
||||
}
|
||||
}
|
||||
|
||||
val MPV = object : ResultResume(
|
||||
MPV_PACKAGE,
|
||||
//"is.xyz.mpv.MPVActivity.result", // resume not working :pensive:
|
||||
position = "position",
|
||||
duration = "duration",
|
||||
) {
|
||||
override fun getPosition(intent: Intent?): Long {
|
||||
return intent?.getIntExtra(this.position, defaultTime.toInt())?.toLong() ?: defaultTime
|
||||
}
|
||||
|
||||
override fun getDuration(intent: Intent?): Long {
|
||||
return intent?.getIntExtra(this.duration, defaultTime.toInt())?.toLong() ?: defaultTime
|
||||
}
|
||||
}
|
||||
|
||||
val WEB_VIDEO = ResultResume(WEB_VIDEO_CAST_PACKAGE)
|
||||
|
||||
val resumeApps = arrayOf(
|
||||
VLC, MPV, WEB_VIDEO
|
||||
)
|
||||
|
||||
// Short name for requests client to make it nicer to use
|
||||
|
||||
var app = Requests(responseParser = object : ResponseParser {
|
||||
val mapper: ObjectMapper = jacksonObjectMapper().configure(
|
||||
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
|
||||
false
|
||||
)
|
||||
|
||||
override fun <T : Any> parse(text: String, kClass: KClass<T>): T {
|
||||
return mapper.readValue(text, kClass.java)
|
||||
}
|
||||
|
||||
override fun <T : Any> parseSafe(text: String, kClass: KClass<T>): T? {
|
||||
return try {
|
||||
mapper.readValue(text, kClass.java)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeValueAsString(obj: Any): String {
|
||||
return mapper.writeValueAsString(obj)
|
||||
}
|
||||
}).apply {
|
||||
defaultHeaders = mapOf("user-agent" to USER_AGENT)
|
||||
}
|
||||
|
||||
class MainActivity : AppCompatActivity(), ColorPickerDialogListener,
|
||||
BiometricAuthenticator.BiometricAuthCallback {
|
||||
companion object {
|
||||
const val VLC_PACKAGE = "org.videolan.vlc"
|
||||
const val MPV_PACKAGE = "is.xyz.mpv"
|
||||
const val WEB_VIDEO_CAST_PACKAGE = "com.instantbits.cast.webvideo"
|
||||
|
||||
val VLC_COMPONENT = ComponentName(VLC_PACKAGE, "$VLC_PACKAGE.gui.video.VideoPlayerActivity")
|
||||
val MPV_COMPONENT = ComponentName(MPV_PACKAGE, "$MPV_PACKAGE.MPVActivity")
|
||||
|
||||
//TODO REFACTOR AF
|
||||
open class ResultResume(
|
||||
val packageString: String,
|
||||
val action: String = Intent.ACTION_VIEW,
|
||||
val position: String? = null,
|
||||
val duration: String? = null,
|
||||
var launcher: ActivityResultLauncher<Intent>? = null,
|
||||
) {
|
||||
val defaultTime = -1L
|
||||
|
||||
val lastId get() = "${packageString}_last_open_id"
|
||||
suspend fun launch(id: Int?, callback: suspend Intent.() -> Unit) {
|
||||
val intent = Intent(action)
|
||||
|
||||
if (id != null)
|
||||
setKey(lastId, id)
|
||||
else
|
||||
removeKey(lastId)
|
||||
|
||||
intent.setPackage(packageString)
|
||||
callback.invoke(intent)
|
||||
launcher?.launch(intent)
|
||||
}
|
||||
|
||||
open fun getPosition(intent: Intent?): Long {
|
||||
return defaultTime
|
||||
}
|
||||
|
||||
open fun getDuration(intent: Intent?): Long {
|
||||
return defaultTime
|
||||
}
|
||||
}
|
||||
|
||||
val VLC = object : ResultResume(
|
||||
VLC_PACKAGE,
|
||||
// Android 13 intent restrictions fucks up specifically launching the VLC player
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
"org.videolan.vlc.player.result"
|
||||
} else {
|
||||
Intent.ACTION_VIEW
|
||||
},
|
||||
"extra_position",
|
||||
"extra_duration",
|
||||
) {
|
||||
override fun getPosition(intent: Intent?): Long {
|
||||
return intent?.getLongExtra(this.position, defaultTime) ?: defaultTime
|
||||
}
|
||||
|
||||
override fun getDuration(intent: Intent?): Long {
|
||||
return intent?.getLongExtra(this.duration, defaultTime) ?: defaultTime
|
||||
}
|
||||
}
|
||||
|
||||
val MPV = object : ResultResume(
|
||||
MPV_PACKAGE,
|
||||
//"is.xyz.mpv.MPVActivity.result", // resume not working :pensive:
|
||||
position = "position",
|
||||
duration = "duration",
|
||||
) {
|
||||
override fun getPosition(intent: Intent?): Long {
|
||||
return intent?.getIntExtra(this.position, defaultTime.toInt())?.toLong()
|
||||
?: defaultTime
|
||||
}
|
||||
|
||||
override fun getDuration(intent: Intent?): Long {
|
||||
return intent?.getIntExtra(this.duration, defaultTime.toInt())?.toLong()
|
||||
?: defaultTime
|
||||
}
|
||||
}
|
||||
|
||||
val WEB_VIDEO = ResultResume(WEB_VIDEO_CAST_PACKAGE)
|
||||
|
||||
val resumeApps = arrayOf(
|
||||
VLC, MPV, WEB_VIDEO
|
||||
)
|
||||
|
||||
|
||||
const val TAG = "MAINACT"
|
||||
const val ANIMATED_OUTLINE: Boolean = false
|
||||
var lastError: String? = null
|
||||
|
@ -1403,7 +1378,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener,
|
|||
}
|
||||
}
|
||||
|
||||
observe(viewModel.watchStatus,::setWatchStatus)
|
||||
observe(viewModel.watchStatus, ::setWatchStatus)
|
||||
observe(syncViewModel.userData, ::setUserData)
|
||||
observeNullable(viewModel.subscribeStatus, ::setSubscribeStatus)
|
||||
|
||||
|
@ -1831,7 +1806,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener,
|
|||
}
|
||||
|
||||
override fun onAuthenticationError() {
|
||||
finish()
|
||||
finish()
|
||||
}
|
||||
|
||||
private var backPressedCallback: OnBackPressedCallback? = null
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
package com.lagradost.cloudstream3
|
||||
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
//https://stackoverflow.com/questions/34697828/parallel-operations-on-kotlin-collections
|
||||
/*
|
||||
fun <T, R> Iterable<T>.pmap(
|
||||
numThreads: Int = maxOf(Runtime.getRuntime().availableProcessors() - 2, 1),
|
||||
exec: ExecutorService = Executors.newFixedThreadPool(numThreads),
|
||||
transform: (T) -> R,
|
||||
): List<R> {
|
||||
|
||||
// default size is just an inlined version of kotlin.collections.collectionSizeOrDefault
|
||||
val defaultSize = if (this is Collection<*>) this.size else 10
|
||||
val destination = Collections.synchronizedList(ArrayList<R>(defaultSize))
|
||||
|
||||
for (item in this) {
|
||||
exec.submit { destination.add(transform(item)) }
|
||||
}
|
||||
|
||||
exec.shutdown()
|
||||
exec.awaitTermination(1, TimeUnit.DAYS)
|
||||
|
||||
return ArrayList<R>(destination)
|
||||
}*/
|
||||
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
suspend fun <K, V, R> Map<out K, V>.amap(f: suspend (Map.Entry<K, V>) -> R): List<R> =
|
||||
with(CoroutineScope(GlobalScope.coroutineContext)) {
|
||||
map { async { f(it) } }.map { it.await() }
|
||||
}
|
||||
|
||||
fun <K, V, R> Map<out K, V>.apmap(f: suspend (Map.Entry<K, V>) -> R): List<R> = runBlocking {
|
||||
map { async { f(it) } }.map { it.await() }
|
||||
}
|
||||
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
suspend fun <A, B> List<A>.amap(f: suspend (A) -> B): List<B> =
|
||||
with(CoroutineScope(GlobalScope.coroutineContext)) {
|
||||
map { async { f(it) } }.map { it.await() }
|
||||
}
|
||||
|
||||
|
||||
fun <A, B> List<A>.apmap(f: suspend (A) -> B): List<B> = runBlocking {
|
||||
map { async { f(it) } }.map { it.await() }
|
||||
}
|
||||
|
||||
fun <A, B> List<A>.apmapIndexed(f: suspend (index: Int, A) -> B): List<B> = runBlocking {
|
||||
mapIndexed { index, a -> async { f(index, a) } }.map { it.await() }
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
suspend fun <A, B> List<A>.amapIndexed(f: suspend (index: Int, A) -> B): List<B> =
|
||||
with(CoroutineScope(GlobalScope.coroutineContext)) {
|
||||
mapIndexed { index, a -> async { f(index, a) } }.map { it.await() }
|
||||
}
|
||||
|
||||
// run code in parallel
|
||||
/*fun <R> argpmap(
|
||||
vararg transforms: () -> R,
|
||||
numThreads: Int = maxOf(Runtime.getRuntime().availableProcessors() - 2, 1),
|
||||
exec: ExecutorService = Executors.newFixedThreadPool(numThreads)
|
||||
) {
|
||||
for (item in transforms) {
|
||||
exec.submit { item.invoke() }
|
||||
}
|
||||
|
||||
exec.shutdown()
|
||||
exec.awaitTermination(1, TimeUnit.DAYS)
|
||||
}*/
|
||||
|
||||
// built in try catch
|
||||
fun <R> argamap(
|
||||
vararg transforms: suspend () -> R,
|
||||
) = runBlocking {
|
||||
transforms.map {
|
||||
async {
|
||||
try {
|
||||
it.invoke()
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
}
|
||||
}
|
||||
}.map { it.await() }
|
||||
}
|
|
@ -29,6 +29,14 @@ 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.LoadResponse.Companion.isMovie
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.MPV
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.MPV_COMPONENT
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.MPV_PACKAGE
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.VLC
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.VLC_COMPONENT
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.VLC_PACKAGE
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.WEB_VIDEO
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.WEB_VIDEO_CAST_PACKAGE
|
||||
import com.lagradost.cloudstream3.metaproviders.SyncRedirector
|
||||
import com.lagradost.cloudstream3.mvvm.*
|
||||
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
||||
|
@ -1354,7 +1362,7 @@ class ResultViewModel2 : ViewModel() {
|
|||
|
||||
private fun launchActivity(
|
||||
activity: Activity?,
|
||||
resumeApp: ResultResume,
|
||||
resumeApp: MainActivity.Companion.ResultResume,
|
||||
id: Int? = null,
|
||||
work: suspend (Intent.(Activity) -> Unit)
|
||||
): Job? {
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
package com.lagradost.cloudstream3.utils
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import com.lagradost.cloudstream3.mvvm.launchSafe
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.Collections.synchronizedList
|
||||
|
||||
object Coroutines {
|
||||
fun <T> T.main(work: suspend ((T) -> Unit)): Job {
|
||||
val value = this
|
||||
return CoroutineScope(Dispatchers.Main).launchSafe {
|
||||
work(value)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> T.ioSafe(work: suspend (CoroutineScope.(T) -> Unit)): Job {
|
||||
val value = this
|
||||
|
||||
return CoroutineScope(Dispatchers.IO).launchSafe {
|
||||
work(value)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun <T, V> V.ioWorkSafe(work: suspend (CoroutineScope.(V) -> T)): T? {
|
||||
val value = this
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
work(value)
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun <T, V> V.ioWork(work: suspend (CoroutineScope.(V) -> T)): T {
|
||||
val value = this
|
||||
return withContext(Dispatchers.IO) {
|
||||
work(value)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun <T, V> V.mainWork(work: suspend (CoroutineScope.(V) -> T)): T {
|
||||
val value = this
|
||||
return withContext(Dispatchers.Main) {
|
||||
work(value)
|
||||
}
|
||||
}
|
||||
|
||||
fun runOnMainThread(work: (() -> Unit)) {
|
||||
val mainHandler = Handler(Looper.getMainLooper())
|
||||
mainHandler.post {
|
||||
work()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe to add and remove how you want
|
||||
* If you want to iterate over the list then you need to do:
|
||||
* synchronized(allProviders) { code here }
|
||||
**/
|
||||
fun <T> threadSafeListOf(vararg items: T): MutableList<T> {
|
||||
return synchronizedList(items.toMutableList())
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
package com.lagradost.cloudstream3.utils
|
||||
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.math.pow
|
||||
|
||||
//author: https://github.com/daarkdemon
|
||||
class JsHunter(private val hunterJS: String) {
|
||||
|
||||
/**
|
||||
* Detects whether the javascript is H.U.N.T.E.R coded.
|
||||
*
|
||||
* @return true if it's H.U.N.T.E.R coded.
|
||||
*/
|
||||
fun detect(): Boolean {
|
||||
val p = Pattern.compile("eval\\(function\\(h,u,n,t,e,r\\)")
|
||||
val searchResults = p.matcher(hunterJS)
|
||||
return searchResults.find()
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack the javascript
|
||||
*
|
||||
* @return the javascript unhunt or null.
|
||||
*/
|
||||
|
||||
fun dehunt(): String? {
|
||||
try {
|
||||
val p: Pattern =
|
||||
Pattern.compile(
|
||||
"""}\("([^"]+)",[^,]+,\s*"([^"]+)",\s*(\d+),\s*(\d+)""",
|
||||
Pattern.DOTALL
|
||||
)
|
||||
val searchResults: Matcher = p.matcher(hunterJS)
|
||||
if (searchResults.find() && searchResults.groupCount() == 4) {
|
||||
val h = searchResults.group(1)!!.toString()
|
||||
val n = searchResults.group(2)!!.toString()
|
||||
val t = searchResults.group(3)!!.toInt()
|
||||
val e = searchResults.group(4)!!.toInt()
|
||||
return hunter(h, n, t, e)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun duf(d: String, e: Int, f: Int = 10): Int {
|
||||
val str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
|
||||
val g = str.toList()
|
||||
val h = g.take(e)
|
||||
val i = g.take(f)
|
||||
val dList = d.reversed().toList()
|
||||
var j = 0.0
|
||||
for ((c, b) in dList.withIndex()) {
|
||||
if (b in h) {
|
||||
j += h.indexOf(b) * e.toDouble().pow(c)
|
||||
}
|
||||
}
|
||||
var k = ""
|
||||
while (j > 0) {
|
||||
k = i[(j % f).toInt()] + k
|
||||
j = (j - j % f) / f
|
||||
}
|
||||
return k.toIntOrNull() ?: 0
|
||||
}
|
||||
|
||||
private fun hunter(h: String, n: String, t: Int, e: Int): String {
|
||||
var result = ""
|
||||
var i = 0
|
||||
while (i < h.length) {
|
||||
var j = 0
|
||||
var s = ""
|
||||
while (h[i] != n[e]) {
|
||||
s += h[i]
|
||||
i++
|
||||
}
|
||||
while (j < n.length) {
|
||||
s = s.replace(n[j], j.digitToChar())
|
||||
j++
|
||||
}
|
||||
result += (duf(s, e) - t).toChar()
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -1,217 +0,0 @@
|
|||
package com.lagradost.cloudstream3.utils
|
||||
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.math.pow
|
||||
|
||||
// https://github.com/cylonu87/JsUnpacker
|
||||
class JsUnpacker(packedJS: String?) {
|
||||
private var packedJS: String? = null
|
||||
|
||||
/**
|
||||
* Detects whether the javascript is P.A.C.K.E.R. coded.
|
||||
*
|
||||
* @return true if it's P.A.C.K.E.R. coded.
|
||||
*/
|
||||
fun detect(): Boolean {
|
||||
val js = packedJS!!.replace(" ", "")
|
||||
val p = Pattern.compile("eval\\(function\\(p,a,c,k,e,[rd]")
|
||||
val m = p.matcher(js)
|
||||
return m.find()
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack the javascript
|
||||
*
|
||||
* @return the javascript unpacked or null.
|
||||
*/
|
||||
fun unpack(): String? {
|
||||
val js = packedJS
|
||||
try {
|
||||
var p =
|
||||
Pattern.compile("""\}\s*\('(.*)',\s*(.*?),\s*(\d+),\s*'(.*?)'\.split\('\|'\)""", Pattern.DOTALL)
|
||||
var m = p.matcher(js)
|
||||
if (m.find() && m.groupCount() == 4) {
|
||||
val payload = m.group(1).replace("\\'", "'")
|
||||
val radixStr = m.group(2)
|
||||
val countStr = m.group(3)
|
||||
val symtab = m.group(4).split("\\|".toRegex()).toTypedArray()
|
||||
var radix = 36
|
||||
var count = 0
|
||||
try {
|
||||
radix = radixStr.toInt()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
try {
|
||||
count = countStr.toInt()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
if (symtab.size != count) {
|
||||
throw Exception("Unknown p.a.c.k.e.r. encoding")
|
||||
}
|
||||
val unbase = Unbase(radix)
|
||||
p = Pattern.compile("""\b[a-zA-Z0-9_]+\b""")
|
||||
m = p.matcher(payload)
|
||||
val decoded = StringBuilder(payload)
|
||||
var replaceOffset = 0
|
||||
while (m.find()) {
|
||||
val word = m.group(0)
|
||||
val x = unbase.unbase(word)
|
||||
var value: String? = null
|
||||
if (x < symtab.size && x >= 0) {
|
||||
value = symtab[x]
|
||||
}
|
||||
if (value != null && value.isNotEmpty()) {
|
||||
decoded.replace(m.start() + replaceOffset, m.end() + replaceOffset, value)
|
||||
replaceOffset += value.length - word.length
|
||||
}
|
||||
}
|
||||
return decoded.toString()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private inner class Unbase(private val radix: Int) {
|
||||
private val ALPHABET_62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
private val ALPHABET_95 =
|
||||
" !\"#$%&\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
||||
private var alphabet: String? = null
|
||||
private var dictionary: HashMap<String, Int>? = null
|
||||
fun unbase(str: String): Int {
|
||||
var ret = 0
|
||||
if (alphabet == null) {
|
||||
ret = str.toInt(radix)
|
||||
} else {
|
||||
val tmp = StringBuilder(str).reverse().toString()
|
||||
for (i in tmp.indices) {
|
||||
ret += (radix.toDouble().pow(i.toDouble()) * dictionary!![tmp.substring(i, i + 1)]!!).toInt()
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
init {
|
||||
if (radix > 36) {
|
||||
when {
|
||||
radix < 62 -> {
|
||||
alphabet = ALPHABET_62.substring(0, radix)
|
||||
}
|
||||
radix in 63..94 -> {
|
||||
alphabet = ALPHABET_95.substring(0, radix)
|
||||
}
|
||||
radix == 62 -> {
|
||||
alphabet = ALPHABET_62
|
||||
}
|
||||
radix == 95 -> {
|
||||
alphabet = ALPHABET_95
|
||||
}
|
||||
}
|
||||
dictionary = HashMap(95)
|
||||
for (i in 0 until alphabet!!.length) {
|
||||
dictionary!![alphabet!!.substring(i, i + 1)] = i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param packedJS javascript P.A.C.K.E.R. coded.
|
||||
*/
|
||||
init {
|
||||
this.packedJS = packedJS
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
val c =
|
||||
listOf(
|
||||
0x63,
|
||||
0x6f,
|
||||
0x6d,
|
||||
0x2e,
|
||||
0x67,
|
||||
0x6f,
|
||||
0x6f,
|
||||
0x67,
|
||||
0x6c,
|
||||
0x65,
|
||||
0x2e,
|
||||
0x61,
|
||||
0x6e,
|
||||
0x64,
|
||||
0x72,
|
||||
0x6f,
|
||||
0x69,
|
||||
0x64,
|
||||
0x2e,
|
||||
0x67,
|
||||
0x6d,
|
||||
0x73,
|
||||
0x2e,
|
||||
0x61,
|
||||
0x64,
|
||||
0x73,
|
||||
0x2e,
|
||||
0x4d,
|
||||
0x6f,
|
||||
0x62,
|
||||
0x69,
|
||||
0x6c,
|
||||
0x65,
|
||||
0x41,
|
||||
0x64,
|
||||
0x73
|
||||
)
|
||||
val z =
|
||||
listOf(
|
||||
0x63,
|
||||
0x6f,
|
||||
0x6d,
|
||||
0x2e,
|
||||
0x66,
|
||||
0x61,
|
||||
0x63,
|
||||
0x65,
|
||||
0x62,
|
||||
0x6f,
|
||||
0x6f,
|
||||
0x6b,
|
||||
0x2e,
|
||||
0x61,
|
||||
0x64,
|
||||
0x73,
|
||||
0x2e,
|
||||
0x41,
|
||||
0x64
|
||||
)
|
||||
|
||||
fun String.load(): String? {
|
||||
return try {
|
||||
var load = this
|
||||
|
||||
for (q in c.indices) {
|
||||
if (c[q % 4] > 270) {
|
||||
load += c[q % 3]
|
||||
} else {
|
||||
load += c[q].toChar()
|
||||
}
|
||||
}
|
||||
|
||||
Class.forName(load.substring(load.length - c.size, load.length)).name
|
||||
} catch (_: Exception) {
|
||||
try {
|
||||
var f = c[2].toChar().toString()
|
||||
for (w in z.indices) {
|
||||
f += z[w].toChar()
|
||||
}
|
||||
return Class.forName(f.substring(0b001, f.length)).name
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue