4 Commits
v1.0.8 ... main

Author SHA1 Message Date
97c45f14c2 Update SDK version 2026-10-28 10:21:44 -04:00
3d59612d6e Implemented automatic fallback for sources. 2026-10-28 10:20:00 -04:00
bd7d443f21 fixed next episode prompt displaying way too early; update sdk 2026-10-25 22:15:13 -04:00
Devin Bot
8cd9ec3671 updated gitignore 2026-10-24 20:55:11 -07:00
6 changed files with 38 additions and 12 deletions

2
.gitignore vendored
View File

@@ -9,3 +9,5 @@ android/.gradle/
.build .build
www/ www/
android/app/src/main/assets/ android/app/src/main/assets/
release/
android/app/src/main/res/raw/*.crt

View File

@@ -79,6 +79,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import tv.vyla.app.ui.components.VideoTintSampler import tv.vyla.app.ui.components.VideoTintSampler
import tv.vyla.app.ui.components.videoTint import tv.vyla.app.ui.components.videoTint
import android.util.Log
enum class ActivePlayerMenu { enum class ActivePlayerMenu {
NONE, QUALITY, SUBTITLES, VOLUME, SETTINGS, MORE NONE, QUALITY, SUBTITLES, VOLUME, SETTINGS, MORE
@@ -224,6 +225,33 @@ fun PlayerScreen(
exoPlayer.play() exoPlayer.play()
resetHideTimer() resetHideTimer()
} }
DisposableEffect(exoPlayer) {
val errorListener = object : Player.Listener { // source auto-fallback
override fun onPlayerError(error: androidx.media3.common.PlaybackException) {
val currentIdx = resolvedStreams.indexOf(activeStream)
if (currentIdx != -1 && currentIdx + 1 < resolvedStreams.size) {
// android.widget.Toast.makeText(context, "Source failed. Trying next...", android.widget.Toast.LENGTH_SHORT).show()
Log.e("vyla", "Source failed. Trying next...")
coroutineScope.launch {
delay(1500) // follow rate limits
val nextStream = resolvedStreams[currentIdx + 1]
activeStream = nextStream
playStream(nextStream.primaryUrl, nextStream.headers)
}
} else {
isBuffering = false
Log.e("vyla", "All Sources Failed")
android.widget.Toast.makeText(context, "All available sources failed. Please disable any VPN or try again later", android.widget.Toast.LENGTH_LONG).show()
}
}
}
exoPlayer.addListener(errorListener)
onDispose {
exoPlayer.removeListener(errorListener)
}
}
suspend fun focusEpisodeCard() { suspend fun focusEpisodeCard() {
var tries = 0 var tries = 0
while (tries < 15) { while (tries < 15) {
@@ -300,7 +328,9 @@ fun PlayerScreen(
onFirstPlayable = { stream -> onFirstPlayable = { stream ->
coroutineScope.launch { coroutineScope.launch {
activeStream = stream activeStream = stream
resolvedStreams.add(stream) if (!resolvedStreams.any { it.provider == stream.provider }) {
resolvedStreams.add(stream)
}
playStream(stream.primaryUrl, stream.headers) playStream(stream.primaryUrl, stream.headers)
} }
}, },
@@ -520,7 +550,7 @@ fun PlayerScreen(
} }
} }
val seg = activeSegment val seg = activeSegment
val showNextPrompt = isNextEpAvailable && durationMs > 0 && (durationMs - currentPositionMs) in 1..300000 val showNextPrompt = isNextEpAvailable && durationMs > 0 && (durationMs - currentPositionMs) in 1..30000
if ((seg != null || showNextPrompt) && activeMenu == ActivePlayerMenu.NONE && !isEpisodesDrawerOpen) { if ((seg != null || showNextPrompt) && activeMenu == ActivePlayerMenu.NONE && !isEpisodesDrawerOpen) {
Column( Column(
horizontalAlignment = Alignment.End, horizontalAlignment = Alignment.End,
@@ -938,7 +968,7 @@ fun PlayerScreen(
items(resolvedStreams) { stream -> items(resolvedStreams) { stream ->
MenuItemRow( MenuItemRow(
label = stream.label, label = stream.label,
isSelected = activeStream?.provider == stream.provider, isSelected = ((activeStream?.provider == stream.provider) && (activeStream?.primaryUrl == stream.primaryUrl)),
onClick = { onClick = {
activeStream = stream activeStream = stream
playStream(stream.primaryUrl, stream.headers) playStream(stream.primaryUrl, stream.headers)

0
android/gradlew vendored Normal file → Executable file
View File

2
package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "1.0.8", "version": "1.0.8",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@vyla-entertainment/sdk": "^1.18.0", "@vyla-entertainment/sdk": "^1.20.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"hls.js": "^1.7.3" "hls.js": "^1.7.3"

View File

@@ -9,7 +9,7 @@
"android": "node scripts/build-android.mjs" "android": "node scripts/build-android.mjs"
}, },
"dependencies": { "dependencies": {
"@vyla-entertainment/sdk": "^1.18.0", "@vyla-entertainment/sdk": "^1.20.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"hls.js": "^1.7.3" "hls.js": "^1.7.3"

View File

@@ -1,6 +0,0 @@
{
"name": "Vyla TV",
"version": "1.0.6",
"package": "Vyla-TV_1.0.6.apk",
"releaseDate": "2026-10-06T03:41:56.445Z"
}