mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-17 15:32:01 +00:00
update required stubs
This commit is contained in:
parent
fdde8ba1c2
commit
632d30c6c8
12 changed files with 204 additions and 9 deletions
|
|
@ -367,6 +367,19 @@ tasks.matching { it.name == "packageReleaseDistributionForCurrentOS" || it.name
|
|||
finalizedBy(renameReleaseDmgArtifact)
|
||||
}
|
||||
|
||||
val buildDesktopMpvBridge = tasks.register<Exec>("buildDesktopMpvBridge") {
|
||||
onlyIf { System.getProperty("os.name").contains("Mac", ignoreCase = true) }
|
||||
workingDir = rootProject.file("MPVKit")
|
||||
commandLine("swift", "build", "-c", "release", "--product", "DesktopMPVBridge")
|
||||
inputs.file(rootProject.file("MPVKit/Package.swift"))
|
||||
inputs.dir(rootProject.file("MPVKit/Sources/DesktopMPVBridge"))
|
||||
outputs.dir(rootProject.file("MPVKit/.build"))
|
||||
}
|
||||
|
||||
tasks.matching { it.name == "run" || it.name == "desktopRun" }.configureEach {
|
||||
dependsOn(buildDesktopMpvBridge)
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
exclude(group = "androidx.media3", module = "media3-exoplayer")
|
||||
exclude(group = "androidx.media3", module = "media3-ui")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
internal actual fun isLiquidGlassNativeTabBarSupported(): Boolean = false
|
||||
|
||||
internal actual fun publishLiquidGlassNativeTabBarEnabled(enabled: Boolean) = Unit
|
||||
|
||||
internal actual fun publishNativeTabBarVisible(visible: Boolean) = Unit
|
||||
|
||||
internal actual fun publishNativeSelectedTab(tabName: String) = Unit
|
||||
|
||||
internal actual fun publishNativeTabAccentColor(hexColor: String) = Unit
|
||||
|
||||
internal actual fun publishNativeProfileTabIcon(
|
||||
name: String?,
|
||||
avatarColorHex: String?,
|
||||
avatarImageUrl: String?,
|
||||
avatarBackgroundColorHex: String?,
|
||||
) = Unit
|
||||
|
|
@ -35,6 +35,11 @@ private val addonHttpClient: HttpClient = HttpClient.newBuilder()
|
|||
.followRedirects(HttpClient.Redirect.NORMAL)
|
||||
.build()
|
||||
|
||||
private val addonHttpClientNoRedirects: HttpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(60))
|
||||
.followRedirects(HttpClient.Redirect.NEVER)
|
||||
.build()
|
||||
|
||||
private const val maxRawResponseBodyChars = 1024 * 1024
|
||||
private const val truncationSuffix = "\n...[truncated]"
|
||||
|
||||
|
|
@ -54,6 +59,7 @@ private suspend fun executeRequest(
|
|||
url: String,
|
||||
headers: Map<String, String>,
|
||||
body: String,
|
||||
followRedirects: Boolean = true,
|
||||
) = withContext(Dispatchers.IO) {
|
||||
val builder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
|
|
@ -69,7 +75,8 @@ private suspend fun executeRequest(
|
|||
builder.method(method.uppercase(), HttpRequest.BodyPublishers.noBody())
|
||||
}.build()
|
||||
|
||||
addonHttpClient.send(request, HttpResponse.BodyHandlers.ofString())
|
||||
val client = if (followRedirects) addonHttpClient else addonHttpClientNoRedirects
|
||||
client.send(request, HttpResponse.BodyHandlers.ofString())
|
||||
}
|
||||
|
||||
private suspend fun executeTextRequest(
|
||||
|
|
@ -137,8 +144,9 @@ actual suspend fun httpRequestRaw(
|
|||
url: String,
|
||||
headers: Map<String, String>,
|
||||
body: String,
|
||||
followRedirects: Boolean,
|
||||
): RawHttpResponse {
|
||||
val response = executeRequest(method, url, headers, body)
|
||||
val response = executeRequest(method, url, headers, body, followRedirects)
|
||||
val payload = response.body()
|
||||
val limitedPayload = if (payload.length > maxRawResponseBodyChars) {
|
||||
payload.take(maxRawResponseBodyChars) + truncationSuffix
|
||||
|
|
@ -154,4 +162,4 @@ actual suspend fun httpRequestRaw(
|
|||
key.lowercase() to values.joinToString(",")
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package com.nuvio.app.features.collection
|
||||
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import com.nuvio.app.desktop.DesktopPreferences
|
||||
|
||||
internal actual object CollectionMobileSettingsStorage {
|
||||
private const val preferencesName = "nuvio_collection_mobile_settings"
|
||||
private const val payloadKey = "collection_mobile_settings_payload"
|
||||
|
||||
actual fun loadPayload(): String? =
|
||||
DesktopPreferences.getString(preferencesName, ProfileScopedKey.of(payloadKey))
|
||||
|
||||
actual fun savePayload(payload: String) {
|
||||
DesktopPreferences.putString(preferencesName, ProfileScopedKey.of(payloadKey), payload)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ package com.nuvio.app.features.downloads
|
|||
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import com.nuvio.app.desktop.DesktopPreferences
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
internal actual object DownloadsStorage {
|
||||
private const val preferencesName = "nuvio_downloads"
|
||||
|
|
@ -33,12 +35,24 @@ internal actual object DownloadsPlatformDownloader {
|
|||
actual fun removeFile(localFileUri: String?): Boolean = false
|
||||
|
||||
actual fun removePartialFile(destinationFileName: String): Boolean = false
|
||||
|
||||
actual fun resolveLocalFileUri(localFileUri: String?, destinationFileName: String): String? =
|
||||
localFileUri
|
||||
?.toLocalFileOrNull()
|
||||
?.takeIf { it.exists() }
|
||||
?.toURI()
|
||||
?.toString()
|
||||
}
|
||||
|
||||
private fun String.toLocalFileOrNull(): File? =
|
||||
runCatching {
|
||||
if (startsWith("file://")) File(URI(this)) else File(this)
|
||||
}.getOrNull()
|
||||
|
||||
internal actual object DownloadsLiveStatusPlatform {
|
||||
actual fun onItemsChanged(items: List<DownloadItem>) = Unit
|
||||
}
|
||||
|
||||
internal actual object DownloadsClock {
|
||||
actual fun nowEpochMs(): Long = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -466,6 +466,8 @@ internal actual object PlayerSettingsStorage {
|
|||
private const val skipIntroEnabledKey = "skip_intro_enabled"
|
||||
private const val animeSkipEnabledKey = "animeskip_enabled"
|
||||
private const val animeSkipClientIdKey = "animeskip_client_id"
|
||||
private const val introDbApiKeyKey = "introdb_api_key"
|
||||
private const val introSubmitEnabledKey = "intro_submit_enabled"
|
||||
private const val streamAutoPlayNextEpisodeEnabledKey = "stream_auto_play_next_episode_enabled"
|
||||
private const val streamAutoPlayPreferBingeGroupKey = "stream_auto_play_prefer_binge_group"
|
||||
private const val nextEpisodeThresholdModeKey = "next_episode_threshold_mode"
|
||||
|
|
@ -496,6 +498,8 @@ internal actual object PlayerSettingsStorage {
|
|||
skipIntroEnabledKey,
|
||||
animeSkipEnabledKey,
|
||||
animeSkipClientIdKey,
|
||||
introDbApiKeyKey,
|
||||
introSubmitEnabledKey,
|
||||
streamAutoPlayNextEpisodeEnabledKey,
|
||||
streamAutoPlayPreferBingeGroupKey,
|
||||
nextEpisodeThresholdModeKey,
|
||||
|
|
@ -661,6 +665,18 @@ internal actual object PlayerSettingsStorage {
|
|||
saveString(animeSkipClientIdKey, clientId)
|
||||
}
|
||||
|
||||
actual fun loadIntroDbApiKey(): String? = loadString(introDbApiKeyKey)
|
||||
|
||||
actual fun saveIntroDbApiKey(apiKey: String) {
|
||||
saveString(introDbApiKeyKey, apiKey)
|
||||
}
|
||||
|
||||
actual fun loadIntroSubmitEnabled(): Boolean? = loadBoolean(introSubmitEnabledKey)
|
||||
|
||||
actual fun saveIntroSubmitEnabled(enabled: Boolean) {
|
||||
saveBoolean(introSubmitEnabledKey, enabled)
|
||||
}
|
||||
|
||||
actual fun loadStreamAutoPlayNextEpisodeEnabled(): Boolean? = loadBoolean(streamAutoPlayNextEpisodeEnabledKey)
|
||||
|
||||
actual fun saveStreamAutoPlayNextEpisodeEnabled(enabled: Boolean) {
|
||||
|
|
@ -726,6 +742,8 @@ internal actual object PlayerSettingsStorage {
|
|||
loadSkipIntroEnabled()?.let { put(skipIntroEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadAnimeSkipEnabled()?.let { put(animeSkipEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadAnimeSkipClientId()?.let { put(animeSkipClientIdKey, encodeSyncString(it)) }
|
||||
loadIntroDbApiKey()?.let { put(introDbApiKeyKey, encodeSyncString(it)) }
|
||||
loadIntroSubmitEnabled()?.let { put(introSubmitEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadStreamAutoPlayNextEpisodeEnabled()?.let { put(streamAutoPlayNextEpisodeEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadStreamAutoPlayPreferBingeGroup()?.let { put(streamAutoPlayPreferBingeGroupKey, encodeSyncBoolean(it)) }
|
||||
loadNextEpisodeThresholdMode()?.let { put(nextEpisodeThresholdModeKey, encodeSyncString(it)) }
|
||||
|
|
@ -760,6 +778,8 @@ internal actual object PlayerSettingsStorage {
|
|||
payload.decodeSyncBoolean(skipIntroEnabledKey)?.let(::saveSkipIntroEnabled)
|
||||
payload.decodeSyncBoolean(animeSkipEnabledKey)?.let(::saveAnimeSkipEnabled)
|
||||
payload.decodeSyncString(animeSkipClientIdKey)?.let(::saveAnimeSkipClientId)
|
||||
payload.decodeSyncString(introDbApiKeyKey)?.let(::saveIntroDbApiKey)
|
||||
payload.decodeSyncBoolean(introSubmitEnabledKey)?.let(::saveIntroSubmitEnabled)
|
||||
payload.decodeSyncBoolean(streamAutoPlayNextEpisodeEnabledKey)?.let(::saveStreamAutoPlayNextEpisodeEnabled)
|
||||
payload.decodeSyncBoolean(streamAutoPlayPreferBingeGroupKey)?.let(::saveStreamAutoPlayPreferBingeGroup)
|
||||
payload.decodeSyncString(nextEpisodeThresholdModeKey)?.let(::saveNextEpisodeThresholdMode)
|
||||
|
|
@ -819,7 +839,7 @@ internal actual object PlayerSettingsStorage {
|
|||
actual fun LockPlayerToLandscape() = Unit
|
||||
|
||||
@Composable
|
||||
actual fun EnterImmersivePlayerMode() = Unit
|
||||
actual fun EnterImmersivePlayerMode(keepScreenAwake: Boolean) = Unit
|
||||
|
||||
@Composable
|
||||
actual fun ManagePlayerPictureInPicture(
|
||||
|
|
@ -828,4 +848,4 @@ actual fun ManagePlayerPictureInPicture(
|
|||
) = Unit
|
||||
|
||||
@Composable
|
||||
actual fun rememberPlayerGestureController(): PlayerGestureController? = null
|
||||
actual fun rememberPlayerGestureController(): PlayerGestureController? = null
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.nuvio.app.features.profiles
|
||||
|
||||
import com.nuvio.app.desktop.DesktopPreferences
|
||||
|
||||
internal actual object AvatarStorage {
|
||||
private const val preferencesName = "nuvio_avatar_cache"
|
||||
private const val payloadKey = "avatar_catalog_payload"
|
||||
|
||||
actual fun loadPayload(): String? =
|
||||
DesktopPreferences.getString(preferencesName, payloadKey)
|
||||
|
||||
actual fun savePayload(payload: String) {
|
||||
DesktopPreferences.putString(preferencesName, payloadKey, payload)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.nuvio.app.features.profiles
|
||||
|
||||
internal actual object ProfileHoverHapticFeedback {
|
||||
actual fun prepare() = Unit
|
||||
actual fun perform() = Unit
|
||||
actual fun release() = Unit
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.nuvio.app.features.profiles
|
||||
|
||||
import com.nuvio.app.desktop.DesktopPreferences
|
||||
|
||||
internal actual object ProfilePinCacheStorage {
|
||||
private const val preferencesName = "nuvio_profile_pin_cache"
|
||||
|
||||
actual fun loadPayload(profileIndex: Int): String? =
|
||||
DesktopPreferences.getString(preferencesName, payloadKey(profileIndex))
|
||||
|
||||
actual fun savePayload(profileIndex: Int, payload: String) {
|
||||
DesktopPreferences.putString(preferencesName, payloadKey(profileIndex), payload)
|
||||
}
|
||||
|
||||
actual fun removePayload(profileIndex: Int) {
|
||||
DesktopPreferences.remove(preferencesName, payloadKey(profileIndex))
|
||||
}
|
||||
|
||||
private fun payloadKey(profileIndex: Int): String = "profile_pin_cache_$profileIndex"
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.nuvio.app.features.profiles
|
||||
|
||||
import java.security.MessageDigest
|
||||
|
||||
internal actual object ProfilePinCrypto {
|
||||
actual fun sha256Hex(value: String): String {
|
||||
val digest = MessageDigest.getInstance("SHA-256").digest(value.encodeToByteArray())
|
||||
return digest.joinToString(separator = "") { byte ->
|
||||
byte.toUByte().toString(16).padStart(2, '0')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import kotlinx.serialization.json.JsonObject
|
|||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import nuvio.composeapp.generated.resources.Res
|
||||
import nuvio.composeapp.generated.resources.introdb_favicon
|
||||
import nuvio.composeapp.generated.resources.mdblist_logo
|
||||
import nuvio.composeapp.generated.resources.rating_tmdb
|
||||
import nuvio.composeapp.generated.resources.trakt_tv_favicon
|
||||
|
|
@ -22,7 +23,14 @@ internal actual object ThemeSettingsStorage {
|
|||
private const val preferencesName = "nuvio_theme_settings"
|
||||
private const val selectedThemeKey = "selected_theme"
|
||||
private const val amoledEnabledKey = "amoled_enabled"
|
||||
private val syncKeys = listOf(selectedThemeKey, amoledEnabledKey)
|
||||
private const val liquidGlassNativeTabBarEnabledKey = "liquid_glass_native_tab_bar_enabled"
|
||||
private const val selectedAppLanguageKey = "selected_app_language"
|
||||
private val profileScopedSyncKeys = listOf(
|
||||
selectedThemeKey,
|
||||
amoledEnabledKey,
|
||||
liquidGlassNativeTabBarEnabledKey,
|
||||
)
|
||||
private val globalSyncKeys = listOf(selectedAppLanguageKey)
|
||||
|
||||
actual fun loadSelectedTheme(): String? =
|
||||
DesktopPreferences.getString(preferencesName, ProfileScopedKey.of(selectedThemeKey))
|
||||
|
|
@ -38,16 +46,43 @@ internal actual object ThemeSettingsStorage {
|
|||
DesktopPreferences.putBoolean(preferencesName, ProfileScopedKey.of(amoledEnabledKey), enabled)
|
||||
}
|
||||
|
||||
actual fun loadLiquidGlassNativeTabBarEnabled(): Boolean? =
|
||||
DesktopPreferences.getBoolean(preferencesName, ProfileScopedKey.of(liquidGlassNativeTabBarEnabledKey))
|
||||
|
||||
actual fun saveLiquidGlassNativeTabBarEnabled(enabled: Boolean) {
|
||||
DesktopPreferences.putBoolean(preferencesName, ProfileScopedKey.of(liquidGlassNativeTabBarEnabledKey), enabled)
|
||||
}
|
||||
|
||||
actual fun loadSelectedAppLanguage(): String? {
|
||||
val value = DesktopPreferences.getString(preferencesName, selectedAppLanguageKey)
|
||||
if (value != null) return value
|
||||
val legacy = DesktopPreferences.getString(preferencesName, ProfileScopedKey.of(selectedAppLanguageKey))
|
||||
if (legacy != null) saveSelectedAppLanguage(legacy)
|
||||
return legacy
|
||||
}
|
||||
|
||||
actual fun saveSelectedAppLanguage(languageCode: String) {
|
||||
DesktopPreferences.putString(preferencesName, selectedAppLanguageKey, languageCode)
|
||||
}
|
||||
|
||||
actual fun applySelectedAppLanguage(languageCode: String) = Unit
|
||||
|
||||
actual fun exportToSyncPayload(): JsonObject = buildJsonObject {
|
||||
loadSelectedTheme()?.let { put(selectedThemeKey, encodeSyncString(it)) }
|
||||
loadAmoledEnabled()?.let { put(amoledEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadLiquidGlassNativeTabBarEnabled()?.let { put(liquidGlassNativeTabBarEnabledKey, encodeSyncBoolean(it)) }
|
||||
loadSelectedAppLanguage()?.let { put(selectedAppLanguageKey, encodeSyncString(it)) }
|
||||
}
|
||||
|
||||
actual fun replaceFromSyncPayload(payload: JsonObject) {
|
||||
syncKeys.forEach { DesktopPreferences.remove(preferencesName, ProfileScopedKey.of(it)) }
|
||||
profileScopedSyncKeys.forEach { DesktopPreferences.remove(preferencesName, ProfileScopedKey.of(it)) }
|
||||
globalSyncKeys.forEach { DesktopPreferences.remove(preferencesName, it) }
|
||||
|
||||
payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme)
|
||||
payload.decodeSyncBoolean(amoledEnabledKey)?.let(::saveAmoledEnabled)
|
||||
payload.decodeSyncBoolean(liquidGlassNativeTabBarEnabledKey)?.let(::saveLiquidGlassNativeTabBarEnabled)
|
||||
payload.decodeSyncString(selectedAppLanguageKey)?.let(::saveSelectedAppLanguage)
|
||||
applySelectedAppLanguage(loadSelectedAppLanguage() ?: AppLanguage.ENGLISH.code)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,4 +94,5 @@ internal actual fun integrationLogoPainter(logo: IntegrationLogo): Painter =
|
|||
IntegrationLogo.Tmdb -> painterResource(Res.drawable.rating_tmdb)
|
||||
IntegrationLogo.Trakt -> painterResource(Res.drawable.trakt_tv_favicon)
|
||||
IntegrationLogo.MdbList -> painterResource(Res.drawable.mdblist_logo)
|
||||
}
|
||||
IntegrationLogo.IntroDb -> painterResource(Res.drawable.introdb_favicon)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package com.nuvio.app.features.trakt
|
||||
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import com.nuvio.app.desktop.DesktopPreferences
|
||||
|
||||
internal actual object TraktSettingsStorage {
|
||||
private const val preferencesName = "nuvio_trakt_settings"
|
||||
private const val payloadKey = "trakt_settings_payload"
|
||||
|
||||
actual fun loadPayload(): String? =
|
||||
DesktopPreferences.getString(preferencesName, ProfileScopedKey.of(payloadKey))
|
||||
|
||||
actual fun savePayload(payload: String) {
|
||||
DesktopPreferences.putString(preferencesName, ProfileScopedKey.of(payloadKey), payload)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue