Ported more files for multiplatform (#1056)

This commit is contained in:
CranberrySoup 2024-05-18 11:47:12 +00:00 committed by GitHub
parent af828de8d5
commit 4d5cd288ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 243 additions and 141 deletions

View file

@ -29,6 +29,7 @@ import com.google.android.material.chip.ChipGroup
import com.google.android.material.navigationrail.NavigationRailView import com.google.android.material.navigationrail.NavigationRailView
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.MainActivity.Companion.resumeApps
import com.lagradost.cloudstream3.databinding.ToastBinding import com.lagradost.cloudstream3.databinding.ToastBinding
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.player.PlayerEventType import com.lagradost.cloudstream3.ui.player.PlayerEventType

View file

@ -31,19 +31,16 @@ import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.math.absoluteValue 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 * Defines the constant for the all languages preference, if this is set then it is
* the equivalent of all languages being set * the equivalent of all languages being set
**/ **/
const val AllLanguagesName = "universal" 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 { object APIHolder {
val unixTime: Long val unixTime: Long
get() = System.currentTimeMillis() / 1000L get() = System.currentTimeMillis() / 1000L
@ -121,7 +118,8 @@ object APIHolder {
fun LoadResponse.getId(): Int { fun LoadResponse.getId(): Int {
// this fixes an issue with outdated api as getLoadResponseIdFromUrl might be fucked // 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 } ?: false
val matchingTypes = types?.any { it.name.equals(media.format, true) } == true 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 } ?: 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) { } catch (t: Throwable) {
logError(t) logError(t)
null null
@ -866,6 +869,7 @@ enum class TvType(value: Int?) {
Others(12), Others(12),
Music(13), Music(13),
AudioBook(14), AudioBook(14),
/** Wont load the built in player, make your own interaction */ /** Wont load the built in player, make your own interaction */
CustomMedia(15), CustomMedia(15),
} }
@ -1253,13 +1257,15 @@ interface LoadResponse {
fun LoadResponse.getImdbId(): String? { fun LoadResponse.getImdbId(): String? {
return normalSafeApiCall { 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? { fun LoadResponse.getTMDbId(): String? {
return normalSafeApiCall { 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, posterHeaders: Map<String, String>? = null,
backgroundPosterUrl: String? = null, backgroundPosterUrl: String? = null,
) : this( ) : this(
name, url, apiName, magnet, torrent, plot, type, posterUrl, year, rating, tags, duration, trailers, name,
recommendations, actors, comingSoon, syncData, posterHeaders, backgroundPosterUrl, null 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) -> return this.episodes.maxOf { (_, episodes) ->
episodes.count { episodeData -> episodes.count { episodeData ->
// Prioritize display season as actual season may be something random to fit multiple seasons into one. // 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. // Count all episodes from season 1 to below the current season.
episodeSeason in 1..<season episodeSeason in 1..<season
} }
@ -1646,9 +1671,31 @@ data class AnimeLoadResponse(
seasonNames: List<SeasonData>? = null, seasonNames: List<SeasonData>? = null,
backgroundPosterUrl: String? = null, backgroundPosterUrl: String? = null,
) : this( ) : this(
engName, japName, name, url, apiName, type, posterUrl, year, episodes, showStatus, plot, tags, engName,
synonyms, rating, duration, trailers, recommendations, actors, comingSoon, syncData, posterHeaders, japName,
nextAiring, seasonNames, backgroundPosterUrl, null 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, backgroundPosterUrl: String? = null,
) : this( ) : this(
name, url, apiName, type, dataUrl, posterUrl, year, plot, rating, tags, duration, trailers, 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 -> return episodes.count { episodeData ->
// Prioritize display season as actual season may be something random to fit multiple seasons into one. // 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. // Count all episodes from season 1 to below the current season.
episodeSeason in 1..<season episodeSeason in 1..<season
} + episode } + episode
@ -1956,9 +2004,28 @@ data class TvSeriesLoadResponse(
seasonNames: List<SeasonData>? = null, seasonNames: List<SeasonData>? = null,
backgroundPosterUrl: String? = null, backgroundPosterUrl: String? = null,
) : this( ) : this(
name, url, apiName, type, episodes, posterUrl, year, plot, showStatus, rating, tags, duration, name,
trailers, recommendations, actors, comingSoon, syncData, posterHeaders, nextAiring, seasonNames, url,
backgroundPosterUrl, null 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("extraLarge") var extraLarge: String? = null,
@JsonProperty("large") var large: String? = null, @JsonProperty("large") var large: String? = null,
) )
data class Title( data class Title(
@JsonProperty("romaji") var romaji: String? = null, @JsonProperty("romaji") var romaji: String? = null,
@JsonProperty("english") var english: String? = null, @JsonProperty("english") var english: String? = null,

View file

@ -174,7 +174,6 @@ import java.net.URLDecoder
import java.nio.charset.Charset import java.nio.charset.Charset
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.reflect.KClass
import kotlin.system.exitProcess import kotlin.system.exitProcess
//https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898 //https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898
@ -187,21 +186,24 @@ import kotlin.system.exitProcess
//https://github.com/jellyfin/jellyfin-android/blob/6cbf0edf84a3da82347c8d59b5d5590749da81a9/app/src/main/java/org/jellyfin/mobile/bridge/ExternalPlayer.kt#L225 //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" class MainActivity : AppCompatActivity(), ColorPickerDialogListener,
const val MPV_PACKAGE = "is.xyz.mpv" BiometricAuthenticator.BiometricAuthCallback {
const val WEB_VIDEO_CAST_PACKAGE = "com.instantbits.cast.webvideo" 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 VLC_COMPONENT = ComponentName(VLC_PACKAGE, "$VLC_PACKAGE.gui.video.VideoPlayerActivity")
val MPV_COMPONENT = ComponentName(MPV_PACKAGE, "$MPV_PACKAGE.MPVActivity") val MPV_COMPONENT = ComponentName(MPV_PACKAGE, "$MPV_PACKAGE.MPVActivity")
//TODO REFACTOR AF //TODO REFACTOR AF
open class ResultResume( open class ResultResume(
val packageString: String, val packageString: String,
val action: String = Intent.ACTION_VIEW, val action: String = Intent.ACTION_VIEW,
val position: String? = null, val position: String? = null,
val duration: String? = null, val duration: String? = null,
var launcher: ActivityResultLauncher<Intent>? = null, var launcher: ActivityResultLauncher<Intent>? = null,
) { ) {
val defaultTime = -1L val defaultTime = -1L
val lastId get() = "${packageString}_last_open_id" val lastId get() = "${packageString}_last_open_id"
@ -225,9 +227,9 @@ open class ResultResume(
open fun getDuration(intent: Intent?): Long { open fun getDuration(intent: Intent?): Long {
return defaultTime return defaultTime
} }
} }
val VLC = object : ResultResume( val VLC = object : ResultResume(
VLC_PACKAGE, VLC_PACKAGE,
// Android 13 intent restrictions fucks up specifically launching the VLC player // Android 13 intent restrictions fucks up specifically launching the VLC player
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
@ -237,7 +239,7 @@ val VLC = object : ResultResume(
}, },
"extra_position", "extra_position",
"extra_duration", "extra_duration",
) { ) {
override fun getPosition(intent: Intent?): Long { override fun getPosition(intent: Intent?): Long {
return intent?.getLongExtra(this.position, defaultTime) ?: defaultTime return intent?.getLongExtra(this.position, defaultTime) ?: defaultTime
} }
@ -245,59 +247,32 @@ val VLC = object : ResultResume(
override fun getDuration(intent: Intent?): Long { override fun getDuration(intent: Intent?): Long {
return intent?.getLongExtra(this.duration, defaultTime) ?: defaultTime return intent?.getLongExtra(this.duration, defaultTime) ?: defaultTime
} }
} }
val MPV = object : ResultResume( val MPV = object : ResultResume(
MPV_PACKAGE, MPV_PACKAGE,
//"is.xyz.mpv.MPVActivity.result", // resume not working :pensive: //"is.xyz.mpv.MPVActivity.result", // resume not working :pensive:
position = "position", position = "position",
duration = "duration", duration = "duration",
) { ) {
override fun getPosition(intent: Intent?): Long { override fun getPosition(intent: Intent?): Long {
return intent?.getIntExtra(this.position, defaultTime.toInt())?.toLong() ?: defaultTime return intent?.getIntExtra(this.position, defaultTime.toInt())?.toLong()
?: defaultTime
} }
override fun getDuration(intent: Intent?): Long { override fun getDuration(intent: Intent?): Long {
return intent?.getIntExtra(this.duration, defaultTime.toInt())?.toLong() ?: defaultTime return intent?.getIntExtra(this.duration, defaultTime.toInt())?.toLong()
?: defaultTime
}
} }
}
val WEB_VIDEO = ResultResume(WEB_VIDEO_CAST_PACKAGE) val WEB_VIDEO = ResultResume(WEB_VIDEO_CAST_PACKAGE)
val resumeApps = arrayOf( val resumeApps = arrayOf(
VLC, MPV, WEB_VIDEO 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 TAG = "MAINACT" const val TAG = "MAINACT"
const val ANIMATED_OUTLINE: Boolean = false const val ANIMATED_OUTLINE: Boolean = false
var lastError: String? = null var lastError: String? = null
@ -1403,7 +1378,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener,
} }
} }
observe(viewModel.watchStatus,::setWatchStatus) observe(viewModel.watchStatus, ::setWatchStatus)
observe(syncViewModel.userData, ::setUserData) observe(syncViewModel.userData, ::setUserData)
observeNullable(viewModel.subscribeStatus, ::setSubscribeStatus) observeNullable(viewModel.subscribeStatus, ::setSubscribeStatus)

View file

@ -29,6 +29,14 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.LoadResponse.Companion.getAniListId import com.lagradost.cloudstream3.LoadResponse.Companion.getAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId
import com.lagradost.cloudstream3.LoadResponse.Companion.isMovie 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.metaproviders.SyncRedirector
import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AccountManager
@ -1354,7 +1362,7 @@ class ResultViewModel2 : ViewModel() {
private fun launchActivity( private fun launchActivity(
activity: Activity?, activity: Activity?,
resumeApp: ResultResume, resumeApp: MainActivity.Companion.ResultResume,
id: Int? = null, id: Int? = null,
work: suspend (Intent.(Activity) -> Unit) work: suspend (Intent.(Activity) -> Unit)
): Job? { ): Job? {

View file

@ -0,0 +1,11 @@
package com.lagradost.cloudstream3.utils
import android.os.Handler
import android.os.Looper
actual fun runOnMainThreadNative(work: () -> Unit) {
val mainHandler = Handler(Looper.getMainLooper())
mainHandler.post {
work()
}
}

View file

@ -0,0 +1,35 @@
package com.lagradost.cloudstream3
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.ResponseParser
import kotlin.reflect.KClass
// 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)
}

View file

@ -1,3 +1,6 @@
package com.lagradost.cloudstream3 package com.lagradost.cloudstream3
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"
class ErrorLoadingException(message: String? = null) : Exception(message) class ErrorLoadingException(message: String? = null) : Exception(message)

View file

@ -1,12 +1,11 @@
package com.lagradost.cloudstream3.utils package com.lagradost.cloudstream3.utils
import android.os.Handler
import android.os.Looper
import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.mvvm.launchSafe
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.util.Collections.synchronizedList import java.util.Collections.synchronizedList
expect fun runOnMainThreadNative(work: (() -> Unit))
object Coroutines { object Coroutines {
fun <T> T.main(work: suspend ((T) -> Unit)): Job { fun <T> T.main(work: suspend ((T) -> Unit)): Job {
val value = this val value = this
@ -50,10 +49,7 @@ object Coroutines {
} }
fun runOnMainThread(work: (() -> Unit)) { fun runOnMainThread(work: (() -> Unit)) {
val mainHandler = Handler(Looper.getMainLooper()) runOnMainThreadNative(work)
mainHandler.post {
work()
}
} }
/** /**

View file

@ -0,0 +1,5 @@
package com.lagradost.cloudstream3.utils
actual fun runOnMainThreadNative(work: () -> Unit) {
work.invoke()
}