Skip to content

feat(playback): add video playback support#3230

Draft
kairosci wants to merge 3 commits intoMetrolistGroup:mainfrom
kairosci:feature/video-playback-clean
Draft

feat(playback): add video playback support#3230
kairosci wants to merge 3 commits intoMetrolistGroup:mainfrom
kairosci:feature/video-playback-clean

Conversation

@kairosci
Copy link
Copy Markdown
Contributor

@kairosci kairosci commented Mar 12, 2026

Problem

Users were unable to play YouTube video content within the app, limiting the app's functionality to audio-only playback.

Cause

The app lacked video playback capabilities and the necessary infrastructure to handle video streams from YouTube Music.

Solution

  • Added video playback support to the player
  • Integrated video rendering capabilities
  • Implemented video stream handling
  • Updated UI to support video playback mode

Testing

  • Tested video playback functionality with various video content
  • Verified smooth transitions between audio and video modes
  • Confirmed proper stream handling and playback controls

Related Issues

  • Implements video playback feature"

Summary by CodeRabbit

  • New Features
    • Added video playback capability with seamless toggle between song and video modes during playback.
    • New settings option to enable or disable video playback for compatible content.
    • Enhanced player interface with integrated video player when video content is available.

@adrielGGmotion
Copy link
Copy Markdown
Collaborator

Merge conflicts, lovely.

@kairosci kairosci force-pushed the feature/video-playback-clean branch from e63c7c5 to 49d1db9 Compare March 12, 2026 21:32
@kairosci kairosci marked this pull request as draft March 12, 2026 21:47
@kairosci kairosci force-pushed the feature/video-playback-clean branch from a626c0f to a894b05 Compare March 12, 2026 22:21
@nyxiereal
Copy link
Copy Markdown
Member

holy shit, good luck with the conflicts lmao

@kairosci kairosci force-pushed the feature/video-playback-clean branch 2 times, most recently from b1947e1 to 495f20a Compare March 12, 2026 22:51
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kairosci kairosci force-pushed the feature/video-playback-clean branch from abc12dd to 3480824 Compare March 12, 2026 22:57
Replace AutoMigration with manual migration to handle cases where columns
might already exist from previous development builds or partial migrations.
This fixes SQLiteException 'duplicate column name: videoId' on app launch.
@isotjs
Copy link
Copy Markdown
Contributor

isotjs commented Mar 13, 2026

I see too many force pushes hope everything is alright 😭

@nyxiereal
Copy link
Copy Markdown
Member

remember to not force push that much. to update your branch you should rebase. i do it like this

git checkout main
git pull
git checkout my/feature-branch
git rebase main
git push --force

@MetrolistGroup MetrolistGroup deleted a comment from coderabbitai bot Mar 13, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nyxiereal
Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 13, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f59efeb6-78c8-4910-883b-1801ecad9483

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The changes introduce video playback support to the music player application. A media3-ui dependency is added, SongEntity is extended with video metadata fields (videoId, artworkUrl, squareThumbnailUrl), database migrations are updated, and new preference keys control video playback. A VideoPlayer composable component is created alongside UI controls for toggling between song and video modes.

Changes

Cohort / File(s) Summary
Dependency Management
gradle/libs.versions.toml, app/build.gradle.kts
Added media3-ui library dependency to support video playback component rendering.
Database Schema Updates
app/schemas/com.metrolist.music.db.InternalDatabase/35.json, app/schemas/.../36.json
Extended song table with three new nullable columns: videoId, artworkUrl, and squareThumbnailUrl; updated database identity hashes to reflect schema changes.
Database Migration and Builder
app/src/main/kotlin/com/metrolist/music/db/MusicDatabase.kt
Converted AutoMigration(34→35) to manual migration with custom schema handling; introduced createBuilder() utility method for enhanced database configuration.
Data Models and Mapping
app/src/main/kotlin/com/metrolist/music/db/entities/SongEntity.kt, app/src/main/kotlin/com/metrolist/music/models/MediaMetadata.kt
Extended SongEntity with three new nullable string fields; updated MediaMetadata to replace uploadEntityId with videoId, artworkUrl, and squareThumbnailUrl; updated conversion functions to map new fields.
Preference Keys
app/src/main/kotlin/com/metrolist/music/constants/PreferenceKeys.kt
Added VideoPlaybackKey and SongVideoToggleKey boolean preference keys for controlling video playback behavior.
Playback Control
app/src/main/kotlin/com/metrolist/music/playback/PlayerConnection.kt
Added currentMediaItem state tracking and toggleVideoPlayback() method to enable/disable video track playback via ExoPlayer's track selection.
Video Component
app/src/main/kotlin/com/metrolist/music/ui/component/VideoPlayer.kt
Introduced new VideoPlayer composable that renders video surface when tracks are available; tracks video availability and notifies via callbacks.
UI Updates
app/src/main/kotlin/com/metrolist/music/ui/player/Thumbnail.kt, app/src/main/kotlin/com/metrolist/music/ui/screens/settings/PlayerSettings.kt
Integrated video playback toggle into thumbnail UI with SongVideoToggle control; added video playback preference setting in player settings screen.
String Resources
app/src/main/res/values/metrolist_strings.xml
Added four new string resources for video playback labels and descriptions: enable\_video\_playback, enable\_video\_playback\_desc, song\_toggle, video\_toggle.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(playback): add video playback support' clearly and accurately summarizes the main change—adding video playback functionality to the player.
Description check ✅ Passed The description follows the required template with all major sections completed: Problem, Cause, Solution, Testing, and Related Issues are all present and substantive.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can generate a title for your PR based on the changes with custom instructions.

Set the reviews.auto_title_instructions setting to generate a title for your PR based on the changes in the PR with custom instructions.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
app/src/main/kotlin/com/metrolist/music/playback/PlayerConnection.kt (1)

344-360: LGTM with a minor style suggestion.

The toggleVideoPlayback() function correctly manipulates track selection parameters to enable/disable video playback. The error handling is appropriate for this use case.

Consider importing androidx.media3.common.C at the top of the file (it's already imported at line 12-16 area for other C constants) rather than using the fully qualified name on line 352.

♻️ Optional: Use the existing C import
-            val isVideoDisabled = currentParameters.disabledTrackTypes.contains(androidx.media3.common.C.TRACK_TYPE_VIDEO)
+            val isVideoDisabled = currentParameters.disabledTrackTypes.contains(C.TRACK_TYPE_VIDEO)
             exoPlayer.trackSelectionParameters = currentParameters
                 .buildUpon()
-                .setTrackTypeDisabled(androidx.media3.common.C.TRACK_TYPE_VIDEO, !isVideoDisabled)
+                .setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, !isVideoDisabled)
                 .build()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/kotlin/com/metrolist/music/playback/PlayerConnection.kt` around
lines 344 - 360, The code uses the fully qualified androidx.media3.common.C
constant inside toggleVideoPlayback(); please add an import for
androidx.media3.common.C at the top of the file and replace occurrences of
androidx.media3.common.C.TRACK_TYPE_VIDEO in the toggleVideoPlayback() function
with the simple C.TRACK_TYPE_VIDEO to match the existing import style and keep
imports consistent (refer to function toggleVideoPlayback and constant
TRACK_TYPE_VIDEO).
app/src/main/kotlin/com/metrolist/music/ui/player/Thumbnail.kt (2)

750-758: Unused animation states.

songBackground and videoBackground are animated but never applied to any UI elements. The Box backgrounds on lines 764 and 778 use conditional expressions directly instead of these animated values.

🧹 Remove unused code
-        val songBackground by animateDpAsState(
-            targetValue = if (!isVideoSelected) 0.dp else 4.dp,
-            animationSpec = tween(200),
-            label = "songBackground"
-        )
-        val videoBackground by animateDpAsState(
-            targetValue = if (isVideoSelected) 0.dp else 4.dp,
-            animationSpec = tween(200),
-            label = "videoBackground"
-        )
-
         Box(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/kotlin/com/metrolist/music/ui/player/Thumbnail.kt` around lines
750 - 758, The two animateDpAsState values songBackground and videoBackground
are never used — the Box backgrounds still use conditional expressions directly;
replace those conditional dp values with the animated state variables so the
animated values drive the UI. Locate the animateDpAsState declarations
(songBackground, videoBackground) and update the corresponding Box background
modifiers that currently compute background corner radii or paddings with if
(isVideoSelected) ... to instead reference songBackground and videoBackground so
the animation affects the displayed Boxes.

160-162: Consider logging or handling the caught exception.

The exception is caught but silently swallowed. While this provides resilience, logging the error would help with debugging playback issues.

🛡️ Suggested fix
     val currentMediaItem = try {
         player.currentMediaItem
-    } catch (e: Exception) { null }
+    } catch (e: Exception) {
+        timber.log.Timber.w(e, "Failed to get currentMediaItem")
+        null
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/kotlin/com/metrolist/music/ui/player/Thumbnail.kt` around lines
160 - 162, The currentMediaItem try/catch silently swallows exceptions from
player.currentMediaItem; update the catch in Thumbnail.kt to log or handle the
exception instead of ignoring it — capture the caught Exception (e) and call
your app's logging facility (e.g., Log.e, Timber.e, or the existing logger) with
a concise message like "Failed to get currentMediaItem" plus the exception, or
perform an alternative error handling path before returning null so playback
issues are observable; locate the try block around player.currentMediaItem and
add the logging/handling in the catch clause.
app/src/main/kotlin/com/metrolist/music/db/MusicDatabase.kt (1)

805-811: Consider removing unused Migration34To35 class.

The @Suppress("unused") annotation and empty implementation suggest this class should be removed rather than kept "for reference." Dead code increases maintenance burden and can confuse future developers.

🧹 Remove the unused class
-@Suppress("unused") // Kept for reference, replaced by manual MIGRATION_34_35
-class Migration34To35 : AutoMigrationSpec {
-    override fun onPostMigrate(db: SupportSQLiteDatabase) {
-        // This class is no longer used, kept for schema compatibility
-        // All migration logic moved to MIGRATION_34_35
-    }
-}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/kotlin/com/metrolist/music/db/MusicDatabase.kt` around lines 805
- 811, Remove the unused Migration34To35 class: delete the class declaration
"class Migration34To35 : AutoMigrationSpec { override fun onPostMigrate(db:
SupportSQLiteDatabase) { ... } }" and its `@Suppress`("unused") annotation so the
dead code is eliminated; ensure any references to Migration34To35 elsewhere
(e.g., registration lists or migration constants) are also removed or updated to
use MIGRATION_34_35 to avoid unresolved symbol errors.
app/src/main/kotlin/com/metrolist/music/models/MediaMetadata.kt (1)

140-142: Redundant assignment: videoId = setVideoId duplicates existing field.

In SongItem.toMediaMetadata(), setting videoId = setVideoId (line 140) while also setting setVideoId = setVideoId (line 134) creates redundancy. Both fields will always have the same value from this mapping.

This may be intentional to ensure resolvedVideoId works consistently, but consider documenting why both are needed or simplifying if one field is sufficient for SongItem sources.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/kotlin/com/metrolist/music/models/MediaMetadata.kt` around lines
140 - 142, The mapping in SongItem.toMediaMetadata() redundantly assigns the
same value to both videoId and setVideoId (videoId = setVideoId), causing
duplicate fields; either remove the redundant videoId assignment or explicitly
document why both fields must mirror each other (and add a comment). Locate the
SongItem.toMediaMetadata() mapping and either delete the videoId = setVideoId
line and rely on setVideoId (and resolvedVideoId if used), or keep it but add a
concise comment explaining that videoId must equal setVideoId for downstream
consumers (mentioning videoId, setVideoId, and resolvedVideoId to make the
intent clear).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/kotlin/com/metrolist/music/db/MusicDatabase.kt`:
- Around line 164-193: The fixed thread pools created in createBuilder via
setTransactionExecutor and setQueryExecutor leak because they are never shut
down; change to reuse cached/shared executors (create singleton Executors or use
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()) or a
shared WorkStealing/SharedPool) and store references to those ExecutorService
instances, then ensure MusicDatabase closes them when the DB is closed
(override/extend the InternalDatabase/MusicDatabase close/shutdown path to call
shutdownNow()/shutdown on the stored executors). Update createBuilder to accept
or reference the shared executors instead of creating new ones inline (symbols:
createBuilder, setTransactionExecutor, setQueryExecutor, MusicDatabase,
newInstance).

In `@app/src/main/kotlin/com/metrolist/music/ui/player/Thumbnail.kt`:
- Around line 374-383: The UI updates isVideoShown immediately after calling
playerConnection.toggleVideoPlayback(), which can desync if the operation fails;
change the flow so the UI state is updated only after confirming success —
either modify playerConnection.toggleVideoPlayback() to return a boolean/result
and set isVideoShown based on that, or call a synchronous/asynchronous
playerConnection.getVideoPlaybackState()/isVideoPlaying() after toggle (and in
error catch blocks) to re-sync isVideoShown with the actual player state; ensure
SongVideoToggle's onToggle uses the new return value or the re-read result and
handles exceptions by logging and keeping the UI consistent.

---

Nitpick comments:
In `@app/src/main/kotlin/com/metrolist/music/db/MusicDatabase.kt`:
- Around line 805-811: Remove the unused Migration34To35 class: delete the class
declaration "class Migration34To35 : AutoMigrationSpec { override fun
onPostMigrate(db: SupportSQLiteDatabase) { ... } }" and its `@Suppress`("unused")
annotation so the dead code is eliminated; ensure any references to
Migration34To35 elsewhere (e.g., registration lists or migration constants) are
also removed or updated to use MIGRATION_34_35 to avoid unresolved symbol
errors.

In `@app/src/main/kotlin/com/metrolist/music/models/MediaMetadata.kt`:
- Around line 140-142: The mapping in SongItem.toMediaMetadata() redundantly
assigns the same value to both videoId and setVideoId (videoId = setVideoId),
causing duplicate fields; either remove the redundant videoId assignment or
explicitly document why both fields must mirror each other (and add a comment).
Locate the SongItem.toMediaMetadata() mapping and either delete the videoId =
setVideoId line and rely on setVideoId (and resolvedVideoId if used), or keep it
but add a concise comment explaining that videoId must equal setVideoId for
downstream consumers (mentioning videoId, setVideoId, and resolvedVideoId to
make the intent clear).

In `@app/src/main/kotlin/com/metrolist/music/playback/PlayerConnection.kt`:
- Around line 344-360: The code uses the fully qualified
androidx.media3.common.C constant inside toggleVideoPlayback(); please add an
import for androidx.media3.common.C at the top of the file and replace
occurrences of androidx.media3.common.C.TRACK_TYPE_VIDEO in the
toggleVideoPlayback() function with the simple C.TRACK_TYPE_VIDEO to match the
existing import style and keep imports consistent (refer to function
toggleVideoPlayback and constant TRACK_TYPE_VIDEO).

In `@app/src/main/kotlin/com/metrolist/music/ui/player/Thumbnail.kt`:
- Around line 750-758: The two animateDpAsState values songBackground and
videoBackground are never used — the Box backgrounds still use conditional
expressions directly; replace those conditional dp values with the animated
state variables so the animated values drive the UI. Locate the animateDpAsState
declarations (songBackground, videoBackground) and update the corresponding Box
background modifiers that currently compute background corner radii or paddings
with if (isVideoSelected) ... to instead reference songBackground and
videoBackground so the animation affects the displayed Boxes.
- Around line 160-162: The currentMediaItem try/catch silently swallows
exceptions from player.currentMediaItem; update the catch in Thumbnail.kt to log
or handle the exception instead of ignoring it — capture the caught Exception
(e) and call your app's logging facility (e.g., Log.e, Timber.e, or the existing
logger) with a concise message like "Failed to get currentMediaItem" plus the
exception, or perform an alternative error handling path before returning null
so playback issues are observable; locate the try block around
player.currentMediaItem and add the logging/handling in the catch clause.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 71f6649d-626d-4ea9-837d-62510db3457a

📥 Commits

Reviewing files that changed from the base of the PR and between b8861f4 and b9fc64b.

📒 Files selected for processing (13)
  • app/build.gradle.kts
  • app/schemas/com.metrolist.music.db.InternalDatabase/35.json
  • app/schemas/com.metrolist.music.db.InternalDatabase/36.json
  • app/src/main/kotlin/com/metrolist/music/constants/PreferenceKeys.kt
  • app/src/main/kotlin/com/metrolist/music/db/MusicDatabase.kt
  • app/src/main/kotlin/com/metrolist/music/db/entities/SongEntity.kt
  • app/src/main/kotlin/com/metrolist/music/models/MediaMetadata.kt
  • app/src/main/kotlin/com/metrolist/music/playback/PlayerConnection.kt
  • app/src/main/kotlin/com/metrolist/music/ui/component/VideoPlayer.kt
  • app/src/main/kotlin/com/metrolist/music/ui/player/Thumbnail.kt
  • app/src/main/kotlin/com/metrolist/music/ui/screens/settings/PlayerSettings.kt
  • app/src/main/res/values/metrolist_strings.xml
  • gradle/libs.versions.toml

Comment on lines +164 to +193
fun createBuilder(context: Context): RoomDatabase.Builder<InternalDatabase> =
Room.databaseBuilder(context, InternalDatabase::class.java, DB_NAME)
.addMigrations(
MIGRATION_1_2,
MIGRATION_21_24,
MIGRATION_22_24,
MIGRATION_24_25,
MIGRATION_34_35,
)
.fallbackToDestructiveMigration(dropAllTables = true)
.setJournalMode(RoomDatabase.JournalMode.WRITE_AHEAD_LOGGING)
.setTransactionExecutor(java.util.concurrent.Executors.newFixedThreadPool(4))
.setQueryExecutor(java.util.concurrent.Executors.newFixedThreadPool(4))
.addCallback(object : RoomDatabase.Callback() {
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
try {
db.query("PRAGMA busy_timeout = 60000").close()
db.query("PRAGMA cache_size = -16000").close()
db.query("PRAGMA wal_autocheckpoint = 1000").close()
db.query("PRAGMA synchronous = NORMAL").close()
} catch (e: Exception) {
Timber.tag("MusicDatabase").e(e, "Failed to set PRAGMA settings")
}
}
})

fun newInstance(context: Context): MusicDatabase =
MusicDatabase(
delegate =
Room
.databaseBuilder(context, InternalDatabase::class.java, DB_NAME)
.addMigrations(
MIGRATION_1_2,
MIGRATION_21_24,
MIGRATION_22_24,
MIGRATION_24_25,
)
.fallbackToDestructiveMigration(dropAllTables = true)
.setJournalMode(RoomDatabase.JournalMode.WRITE_AHEAD_LOGGING)
.setTransactionExecutor(java.util.concurrent.Executors.newFixedThreadPool(4))
.setQueryExecutor(java.util.concurrent.Executors.newFixedThreadPool(4))
.addCallback(object : RoomDatabase.Callback() {
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
try {
db.query("PRAGMA busy_timeout = 60000").close()
db.query("PRAGMA cache_size = -16000").close()
db.query("PRAGMA wal_autocheckpoint = 1000").close()
db.query("PRAGMA synchronous = NORMAL").close()
} catch (e: Exception) {
Timber.tag("MusicDatabase").e(e, "Failed to set PRAGMA settings")
}
}
})
.build(),
delegate = createBuilder(context).build()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Thread pool executors are never shut down.

The fixed thread pools created at lines 175-176 are never shut down when the database is closed, which can lead to resource leaks if multiple database instances are created over the app lifecycle.

Additionally, using a hardcoded pool size of 4 may not be optimal for all Android devices with varying core counts.

💡 Consider caching executors or using shared executors
 companion object {
     const val DB_NAME = "song.db"
+    
+    // Use shared executors to avoid creating new pools per instance
+    private val sharedExecutor by lazy {
+        java.util.concurrent.Executors.newFixedThreadPool(
+            Runtime.getRuntime().availableProcessors().coerceIn(2, 4)
+        )
+    }

     fun createBuilder(context: Context): RoomDatabase.Builder<InternalDatabase> =
         Room.databaseBuilder(context, InternalDatabase::class.java, DB_NAME)
             .addMigrations(
                 MIGRATION_1_2,
                 MIGRATION_21_24,
                 MIGRATION_22_24,
                 MIGRATION_24_25,
                 MIGRATION_34_35,
             )
             .fallbackToDestructiveMigration(dropAllTables = true)
             .setJournalMode(RoomDatabase.JournalMode.WRITE_AHEAD_LOGGING)
-            .setTransactionExecutor(java.util.concurrent.Executors.newFixedThreadPool(4))
-            .setQueryExecutor(java.util.concurrent.Executors.newFixedThreadPool(4))
+            .setTransactionExecutor(sharedExecutor)
+            .setQueryExecutor(sharedExecutor)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/kotlin/com/metrolist/music/db/MusicDatabase.kt` around lines 164
- 193, The fixed thread pools created in createBuilder via
setTransactionExecutor and setQueryExecutor leak because they are never shut
down; change to reuse cached/shared executors (create singleton Executors or use
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()) or a
shared WorkStealing/SharedPool) and store references to those ExecutorService
instances, then ensure MusicDatabase closes them when the DB is closed
(override/extend the InternalDatabase/MusicDatabase close/shutdown path to call
shutdownNow()/shutdown on the stored executors). Update createBuilder to accept
or reference the shared executors instead of creating new ones inline (symbols:
createBuilder, setTransactionExecutor, setQueryExecutor, MusicDatabase,
newInstance).

Comment on lines +374 to +383
if (isVideoSong && isSongVideoToggleEnabled && !isListenTogetherGuest) {
SongVideoToggle(
isVideoSelected = isVideoShown,
onToggle = {
playerConnection.toggleVideoPlayback()
isVideoShown = !isVideoShown
},
textColor = textBackgroundColor,
modifier = Modifier.align(Alignment.CenterEnd)
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential state desync if toggleVideoPlayback() fails.

The UI state isVideoShown is updated immediately after calling playerConnection.toggleVideoPlayback(), regardless of whether the toggle operation succeeded. If the toggle fails (exception is caught and logged in PlayerConnection), the UI state will be out of sync with the actual player state.

Consider either:

  1. Having toggleVideoPlayback() return a success indicator
  2. Re-syncing state after toggle by re-reading from the player
♻️ Suggested improvement
 SongVideoToggle(
     isVideoSelected = isVideoShown,
     onToggle = {
         playerConnection.toggleVideoPlayback()
-        isVideoShown = !isVideoShown
+        // Re-read actual state from player to ensure sync
+        val currentParams = playerConnection.player.trackSelectionParameters
+        isVideoShown = !currentParams.disabledTrackTypes.contains(C.TRACK_TYPE_VIDEO)
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/kotlin/com/metrolist/music/ui/player/Thumbnail.kt` around lines
374 - 383, The UI updates isVideoShown immediately after calling
playerConnection.toggleVideoPlayback(), which can desync if the operation fails;
change the flow so the UI state is updated only after confirming success —
either modify playerConnection.toggleVideoPlayback() to return a boolean/result
and set isVideoShown based on that, or call a synchronous/asynchronous
playerConnection.getVideoPlaybackState()/isVideoPlaying() after toggle (and in
error catch blocks) to re-sync isVideoShown with the actual player state; ensure
SongVideoToggle's onToggle uses the new return value or the re-read result and
handles exceptions by logging and keeping the UI consistent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants