Skip to content

Commit

Permalink
Minor cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
arkon committed May 19, 2024
1 parent 468ea60 commit 1f228e8
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class ChatService @Inject constructor(
val urlPrefix = "https://www.youtube.com/live_chat"

if (isLive) {
return "$urlPrefix?v=$videoId&embed_domain=www.livetl.app&app=desktop"
return "$urlPrefix?v=$videoId&embed_domain=www.livetl.app"
}

val result = client.get("https://www.youtube.com/watch?v=$videoId") {
Expand All @@ -156,9 +156,8 @@ class ChatService @Inject constructor(
}
val matches = CHAT_CONTINUATION_PATTERN.matcher(result.bodyAsText())
if (matches.find()) {
return "${urlPrefix}_replay?v=$videoId&continuation=${matches.group(
1,
)}&embed_domain=www.livetl.app&app=desktop"
val continuation = matches.group(1)
return "${urlPrefix}_replay?continuation=$continuation&embed_domain=www.livetl.app"
} else {
throw NoChatContinuationFoundException(videoId)
}
Expand Down
22 changes: 11 additions & 11 deletions app/src/main/kotlin/com/livetl/android/data/chat/Models.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.util.fastMap
import kotlinx.serialization.Serializable

sealed class ChatMessage {
abstract val author: MessageAuthor
abstract val content: List<ChatMessageContent>
abstract val timestamp: Long
sealed interface ChatMessage {
val author: MessageAuthor
val content: List<ChatMessageContent>
val timestamp: Long

data class RegularChat(
override val author: MessageAuthor,
override val content: List<ChatMessageContent>,
override val timestamp: Long,
) : ChatMessage()
) : ChatMessage

// TODO: handle super stickers
data class SuperChat(
Expand All @@ -22,7 +22,7 @@ sealed class ChatMessage {
override val timestamp: Long,
val amount: String,
val level: Level,
) : ChatMessage() {
) : ChatMessage {
enum class Level(val backgroundColor: Color, val textColor: Color) {
BLUE(Color(0xFF1565BF), Color.White),
LIGHT_BLUE(Color(0xFF00E4FE), Color.Black),
Expand All @@ -35,7 +35,7 @@ sealed class ChatMessage {
}

// TODO: handle member milestone messages
data class NewMember(override val author: MessageAuthor, override val timestamp: Long) : ChatMessage() {
data class NewMember(override val author: MessageAuthor, override val timestamp: Long) : ChatMessage {
override val content: List<ChatMessageContent> = emptyList()

val backgroundColor = Color(0xFF0E9D58)
Expand All @@ -47,7 +47,7 @@ sealed class ChatMessage {
override val content: List<ChatMessageContent>,
override val timestamp: Long,
val milestone: String,
) : ChatMessage() {
) : ChatMessage {
val backgroundColor = Color(0xFF0E9D58)
val textColor = Color.White
}
Expand All @@ -64,11 +64,11 @@ sealed class ChatMessage {
}
}

sealed class ChatMessageContent {
data class Text(val text: String) : ChatMessageContent() {
sealed interface ChatMessageContent {
data class Text(val text: String) : ChatMessageContent {
override fun toString() = text
}
data class Emoji(val id: String, val src: String) : ChatMessageContent() {
data class Emoji(val id: String, val src: String) : ChatMessageContent {
override fun toString() = id
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.service.notification.StatusBarNotification
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.getSystemService
import dagger.hilt.android.AndroidEntryPoint
import timber.log.Timber
import javax.inject.Inject

@AndroidEntryPoint
Expand All @@ -22,10 +23,11 @@ class YouTubeNotificationListenerService : NotificationListenerService() {
return
}

Timber.d("YouTube notification posted")

val mediaSessionManager = getSystemService<MediaSessionManager>() ?: return

val component = ComponentName(this, YouTubeNotificationListenerService::class.java)
val sessions = mediaSessionManager.getActiveSessions(component)
val sessions = mediaSessionManager.getActiveSessions(ComponentName(this, javaClass))
youTubeSessionService.onActiveSessionsChanged(sessions)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
Expand All @@ -36,7 +35,7 @@ class YouTubeSessionService @Inject constructor(
get() = _session.asSharedFlow()

private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val component = ComponentName(context, YouTubeSessionService::class.java)
private val component = ComponentName(context, javaClass)

private var mediaController: MediaController? = null
private var progressJob: Job? = null
Expand All @@ -45,14 +44,15 @@ class YouTubeSessionService @Inject constructor(
scope.launch {
_session.value = getYouTubeSession()

// We don't really get progress updates, so we simulate per-second updates
// while it's playing
if (state?.state == PlaybackState.STATE_PLAYING && _session.value?.isLive == false) {
progressJob = launch {
while (true) {
delay(1.seconds)
delay(2.seconds)
_session.value = _session.value?.copy(
positionInMs = (_session.value?.positionInMs ?: 0L) + 1000L,
positionInMs = (_session.value?.positionInMs ?: 0L) + 2000L,
)
Timber.d("Updating progress to: ${_session.value?.positionInMs}")
}
}
} else {
Expand Down Expand Up @@ -91,29 +91,37 @@ class YouTubeSessionService @Inject constructor(
}

override fun onActiveSessionsChanged(controllers: List<MediaController>?) {
Timber.d("Active media sessions updated")
listenToYouTubeMediaSession(controllers)
}

private fun listenToYouTubeMediaSession(controllers: List<MediaController>?) {
controllers
?.find { it.packageName == YOUTUBE_PACKAGE_NAME }
?.let {
if (mediaController != null) {
mediaController?.unregisterCallback(mediaControllerCallback)
mediaController = null
}
if (mediaController?.sessionToken != it.sessionToken) {
Timber.d("Found YouTube media session: ${it.sessionToken}")

if (mediaController != null) {
mediaController?.unregisterCallback(mediaControllerCallback)
mediaController = null
}

mediaController = it
it.registerCallback(mediaControllerCallback)
mediaController = it
it.registerCallback(mediaControllerCallback)
}
}
}

private suspend fun getYouTubeSession(): YouTubeSession? {
if (mediaController == null) return null

val title = mediaController?.metadata?.getString(MediaMetadata.METADATA_KEY_TITLE) ?: return null
val channelName = mediaController?.metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST) ?: return null
val title = mediaController?.metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
val channelName = mediaController?.metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST)

if (title.isNullOrEmpty() || channelName.isNullOrEmpty()) {
return null
}

val state = when (mediaController?.playbackState?.state) {
PlaybackState.STATE_PAUSED -> YouTubeVideoPlaybackState.PAUSED
PlaybackState.STATE_PLAYING -> YouTubeVideoPlaybackState.PLAYING
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ fun AboutScreen(onBackPressed: () -> Unit, navigateToLicenses: () -> Unit, navig
)
}

// TODO: prompt this better
if (BuildConfig.DEBUG) {
item {
PreferenceRow(
Expand All @@ -135,7 +136,7 @@ fun AboutScreen(onBackPressed: () -> Unit, navigateToLicenses: () -> Unit, navig
val componentName =
ComponentName(
context.packageName,
YouTubeNotificationListenerService::class.java.getName(),
YouTubeNotificationListenerService::class.java.name,
)
putExtra(
Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ import com.livetl.android.util.AppPreferences
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds

@HiltViewModel
class PlayerViewModel @Inject constructor(
Expand All @@ -41,10 +42,10 @@ class PlayerViewModel @Inject constructor(

viewModelScope.launch(Dispatchers.IO) {
youTubeSessionService.session
.distinctUntilChanged()
.filterNotNull()
.debounce(2.seconds)
.collectLatest {
Timber.i(
Timber.d(
"Current YouTube video: ${it.videoId} / ${it.title} / ${it.positionInMs} / ${it.playbackState}",
)
youTubeSession = it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.launch

sealed class ChatState {
data object LOADING : ChatState()
data object LOADED : ChatState()
data object ERROR : ChatState()
sealed interface ChatState {
data object LOADING : ChatState
data object LOADED : ChatState
data object ERROR : ChatState
}

@Composable
Expand Down

0 comments on commit 1f228e8

Please sign in to comment.