Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5477a1f
chore(refactor): optimize stream parsing layers and regex caching
Himanth-reddy Jun 3, 2026
c54d84a
refactor(core): harden exception safety, fix GC churn, and optimize r…
Himanth-reddy Jun 14, 2026
ba94e5d
refactor(core): harden exception safety, fix GC churn, and optimize r…
Himanth-reddy Jun 14, 2026
e51dfb7
refactor(core): harden exception safety and edge-case validation in r…
Himanth-reddy Jun 15, 2026
6960dfb
refactor(core): harden exception safety and edge-case validation in r…
Himanth-reddy Jun 15, 2026
fb39c53
refactor(core): harden exception safety and edge-case validation in r…
Himanth-reddy Jun 15, 2026
b0f89eb
refactor(core): harden exception safety and edge-case validation in r…
Himanth-reddy Jun 15, 2026
6b23103
chore(refactor): optimize memory usage by hoisting inline regexes to …
Himanth-reddy Jun 19, 2026
5a5f7e9
refactor(core): harden exception safety and modernize sealed classes
Himanth-reddy Jun 24, 2026
63c9b44
chore(refactor): optimize recomposition, regex allocations and harden…
Himanth-reddy Jun 25, 2026
079008b
chore(refactor): optimize stream regex parsing and home coroutine err…
Himanth-reddy Jun 28, 2026
435f14f
chore(refactor): harden exception safety and extract regex parsing in…
Himanth-reddy Jun 29, 2026
4f99deb
refactor(core): harden exception safety and edge-case validation in r…
Himanth-reddy Jun 30, 2026
31594d8
chore(refactor): harden exception safety and extract regex parsing in…
Himanth-reddy Jul 1, 2026
570e803
fix(core): preserve cancellation in HomeViewModel preloadContinueWatc…
Himanth-reddy Jul 1, 2026
e37d72c
chore(refactor): optimize recomposition, regex allocations and harden…
Himanth-reddy Jul 1, 2026
fc0faf0
refactor(core): optimize recomposition, state objects and exception s…
Himanth-reddy Jul 1, 2026
4762345
Merge PR #38: refactor(core): harden exception safety and edge-case v…
Himanth-reddy Jul 1, 2026
01546b1
Merge PR #37: chore(refactor): harden exception safety and optimize r…
Himanth-reddy Jul 1, 2026
61adb1a
Merge PR #36: chore(refactor): optimize stream regex parsing and home…
Himanth-reddy Jul 1, 2026
8398413
Merge PR #35: chore(refactor): optimize recomposition, regex allocati…
Himanth-reddy Jul 1, 2026
05734b7
Merge PR #34: refactor(core): harden exception safety and modernize s…
Himanth-reddy Jul 1, 2026
5985fc7
Merge PR #33: chore(refactor): optimize memory usage by hoisting inli…
Himanth-reddy Jul 1, 2026
6950165
Merge PR #31: refactor(core): harden exception safety and edge-case v…
Himanth-reddy Jul 1, 2026
1f1345e
Merge PR #30: refactor(core): harden exception safety, fix GC churn, …
Himanth-reddy Jul 1, 2026
b22b6d0
Merge PR #28: chore(refactor): optimize stream parsing layers and reg…
Himanth-reddy Jul 1, 2026
c1a97de
chore: remove obsolete debugging scripts and issue investigation files
Himanth-reddy Jul 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/src/main/kotlin/com/arflix/tv/cast/CastManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ class CastManager @Inject constructor(
@ApplicationContext private val context: Context
) {
sealed class CastState {
object NotAvailable : CastState()
object NotConnected : CastState()
object Connecting : CastState()
data object NotAvailable : CastState()
data object NotConnected : CastState()
data object Connecting : CastState()
data class Casting(val deviceName: String) : CastState()
}

Expand Down
6 changes: 6 additions & 0 deletions app/src/main/kotlin/com/arflix/tv/data/api/StalkerApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class StalkerApi(
token = parsed?.js?.token ?: return false
true
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

System.err.println("[Stalker] Handshake failed: ${e.message}")
false
}
Expand Down Expand Up @@ -93,6 +95,8 @@ class StalkerApi(
page++
}
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

System.err.println("[Stalker] Get channels failed: ${e.message}")
}
return channels
Expand All @@ -107,6 +111,8 @@ class StalkerApi(
val parsed = gson.fromJson(response, StalkerLinkResponse::class.java)
parsed?.js?.cmd?.replace("ffmpeg ", "")?.trim()
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

System.err.println("[Stalker] Resolve stream failed: ${e.message}")
null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ class InAppYouTubeExtractor @Inject constructor() {
} catch (e: kotlinx.coroutines.CancellationException) {
throw e
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Log.w(TAG, "[$videoId] extraction failed: ${e.message}")
null
}
Expand Down Expand Up @@ -221,6 +223,8 @@ class InAppYouTubeExtractor @Inject constructor() {
} catch (e: kotlinx.coroutines.CancellationException) {
throw e
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

val msg = e.message.orEmpty()
if (msg.contains("401") || msg.contains("403")) keyRejected = true
null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ class YoutubeChunkedDataSourceFactory(
openNextChunk()
upstream.read(buffer, offset, length)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Log.w(TAG, "Failed to open next chunk at $currentChunkStart: ${e.message}")
C.RESULT_END_OF_INPUT
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class PluginDataStore @Inject constructor(
try {
moshi.adapter<List<PluginRepository>>(repoListType).fromJson(json) ?: emptyList()
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

emptyList()
}
} ?: emptyList()
Expand Down Expand Up @@ -124,6 +126,8 @@ class PluginDataStore @Inject constructor(
try {
moshi.adapter<List<ScraperInfo>>(scraperListType).fromJson(json) ?: emptyList()
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

emptyList()
}
} ?: emptyList()
Expand Down Expand Up @@ -219,6 +223,8 @@ class PluginDataStore @Inject constructor(
@Suppress("UNCHECKED_CAST")
moshi.adapter<Map<String, Map<String, Any>>>(settingsMapType).fromJson(json) ?: emptyMap()
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

emptyMap()
}
} ?: emptyMap()
Expand All @@ -235,6 +241,8 @@ class PluginDataStore @Inject constructor(
moshi.adapter<Map<String, Map<String, Any>>>(settingsMapType).fromJson(json)?.toMutableMap()
?: mutableMapOf()
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

mutableMapOf()
}
} ?: mutableMapOf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class AnimeScoreRepository @Inject constructor(
if (cached != null || malIdCache.containsKey(imdbId)) return cached

val resolved = withTimeoutOrNull(2_000L) {
runCatching { armApi.resolve(imdbId).firstOrNull()?.myanimelist }.getOrNull()
try { armApi.resolve(imdbId).firstOrNull()?.myanimelist } catch (e: kotlinx.coroutines.CancellationException) { throw e } catch (e: Exception) { null }
}
malIdCache[imdbId] = resolved
return resolved
Expand All @@ -79,7 +79,7 @@ class AnimeScoreRepository @Inject constructor(
if (cached != null || scoreCache.containsKey(malId)) return cached

val score = withTimeoutOrNull(2_000L) {
runCatching { jikanApi.getAnime(malId).data?.score }.getOrNull()
try { jikanApi.getAnime(malId).data?.score } catch (e: kotlinx.coroutines.CancellationException) { throw e } catch (e: Exception) { null }
}
scoreCache[malId] = score
return score
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ internal fun accountSyncPayloadSaveSucceeded(
userSettingsSaved: Boolean,
profileAddonsSaved: Boolean
): Boolean {
return accountSyncSaved || userSettingsSaved
return accountSyncSaved || userSettingsSaved || profileAddonsSaved
}

private fun accountSyncPayloadsMatch(expected: String, actual: String?): Boolean {
Expand Down Expand Up @@ -344,8 +344,8 @@ private fun com.google.gson.JsonObject.booleanValue(key: String): Boolean {
* Authentication state
*/
sealed class AuthState {
object Loading : AuthState()
object NotAuthenticated : AuthState()
data object Loading : AuthState()
data object NotAuthenticated : AuthState()
data class Authenticated(
val userId: String,
val email: String,
Expand Down Expand Up @@ -471,6 +471,8 @@ class AuthRepository @Inject constructor(
}
}
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

}

// Second try: Import from cached tokens
Expand All @@ -487,6 +489,8 @@ class AuthRepository @Inject constructor(
storeSession(session)
}
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

}
}

Expand Down Expand Up @@ -528,6 +532,8 @@ class AuthRepository @Inject constructor(
_authState.value = AuthState.NotAuthenticated
}
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

_authState.value = AuthState.NotAuthenticated
}
}
Expand Down Expand Up @@ -583,6 +589,8 @@ class AuthRepository @Inject constructor(
Result.failure(Exception(message))
}
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

val message = safeErrorMessage(e, context.getString(R.string.auth_signin_failed))
_authState.value = AuthState.Error(message)
AppLogger.breadcrumb("Auth", "email_sign_in_failed ${e::class.java.simpleName}", severity = "warning")
Expand Down Expand Up @@ -611,6 +619,8 @@ class AuthRepository @Inject constructor(
}
}
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

val message = safeErrorMessage(e, context.getString(R.string.auth_signup_failed))
_authState.value = AuthState.Error(message)
AppLogger.recordException(
Expand Down Expand Up @@ -748,6 +758,8 @@ class AuthRepository @Inject constructor(
Result.failure(Exception(message))
}
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

val message = safeErrorMessage(e, context.getString(R.string.auth_signin_failed))
_authState.value = AuthState.Error(message)
AppLogger.recordException(
Expand Down Expand Up @@ -844,6 +856,8 @@ class AuthRepository @Inject constructor(
_authState.value = AuthState.Error(context.getString(R.string.auth_failed_parse_google))
Result.failure(e)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

_authState.value = AuthState.Error(e.message ?: context.getString(R.string.auth_google_failed))
Result.failure(e)
}
Expand Down Expand Up @@ -912,6 +926,8 @@ class AuthRepository @Inject constructor(

result
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

null
}
}
Expand All @@ -934,6 +950,8 @@ class AuthRepository @Inject constructor(
traktRepositoryProvider.get().setUserId(userId)
traktRepositoryProvider.get().syncLocalTokensToProfileIfNeeded()
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

}

return UserProfile(
Expand Down Expand Up @@ -977,6 +995,8 @@ class AuthRepository @Inject constructor(
_userProfile.value = profile
Result.success(Unit)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand Down Expand Up @@ -1089,6 +1109,8 @@ class AuthRepository @Inject constructor(
storeSession(refreshed)
refreshed.accessToken
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

null
}
}
Expand Down Expand Up @@ -1131,6 +1153,8 @@ class AuthRepository @Inject constructor(
accessToken
}
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

null
}
}
Expand All @@ -1147,6 +1171,8 @@ class AuthRepository @Inject constructor(
supabase.auth.loadFromStorage(false)
session = supabase.auth.currentSessionOrNull()
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

}
}
if (session != null && !isSessionExpired(session)) {
Expand All @@ -1165,6 +1191,8 @@ class AuthRepository @Inject constructor(
storeSession(refreshed)
refreshed
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

null
}
}
Expand Down Expand Up @@ -1204,6 +1232,8 @@ class AuthRepository @Inject constructor(
val now = Clock.System.now().epochSeconds
exp <= now + bufferSeconds
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

true
}
}
Expand All @@ -1212,7 +1242,11 @@ class AuthRepository @Inject constructor(
// 1. Explicitly save through session manager (for Supabase SDK auto-restore)
try {
sessionManager.saveSession(session)
} catch (e: kotlinx.coroutines.CancellationException) {
throw e
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

}

// 2. Also save tokens directly (fallback for manual restoration)
Expand Down Expand Up @@ -1261,6 +1295,8 @@ class AuthRepository @Inject constructor(
)
JSONObject(payload)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

null
}
}
Expand Down Expand Up @@ -1308,6 +1344,8 @@ class AuthRepository @Inject constructor(

Result.success(row?.addons)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand Down Expand Up @@ -1353,6 +1391,8 @@ class AuthRepository @Inject constructor(
)
Result.success(Unit)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand Down Expand Up @@ -1390,6 +1430,8 @@ class AuthRepository @Inject constructor(
_userProfile.value = currentProfile.copy(default_subtitle = subtitle)
Result.success(Unit)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand Down Expand Up @@ -1427,6 +1469,8 @@ class AuthRepository @Inject constructor(
_userProfile.value = currentProfile.copy(auto_play_next = autoPlayNext)
Result.success(Unit)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand Down Expand Up @@ -1679,6 +1723,8 @@ class AuthRepository @Inject constructor(
}
Result.success(Unit)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand Down Expand Up @@ -1722,6 +1768,8 @@ class AuthRepository @Inject constructor(
}
Result.success(Unit)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand Down Expand Up @@ -1894,6 +1942,8 @@ class AuthRepository @Inject constructor(
}
Result.success(Unit)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand Down Expand Up @@ -1927,6 +1977,8 @@ class AuthRepository @Inject constructor(
}
)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand Down Expand Up @@ -1963,6 +2015,8 @@ class AuthRepository @Inject constructor(
}
Result.success(Unit)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand All @@ -1988,6 +2042,8 @@ class AuthRepository @Inject constructor(
}
)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand Down Expand Up @@ -2023,6 +2079,8 @@ class AuthRepository @Inject constructor(
)
Result.success(Unit)
} catch (e: Exception) {
if (e is kotlinx.coroutines.CancellationException) throw e

Result.failure(e)
}
}
Expand Down
Loading
Loading