MPV: Adjust subtitle delay/offset (#470)

**This is for MPV playback backend only!** Since this only works in MPV
for now, the option is not shown if using ExoPlayer.

Adds a setting (the gear icon during playback) to adjust the subtitle
delay.

A negative delay shows subtitles sooner. A positive delay shows them
later. For example, if a subtitle is supposed to show between
1000ms-2000ms, a negative 250ms delay will show it at 750ms-1750ms
instead.

The delay is persisted between plays of the same media & subtitle track.

Ref: #12 


![subtitle_delay](https://github.com/user-attachments/assets/824e3a28-650a-4619-9535-b67addaabf1e)
This commit is contained in:
damontecres 2025-12-15 10:06:36 -05:00 committed by GitHub
parent 3f5ce703d2
commit ab8bbf2bd7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 751 additions and 9 deletions

View file

@ -9,6 +9,7 @@ import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.github.damontecres.wholphin.data.model.GetItemsFilter
import com.github.damontecres.wholphin.data.model.ItemPlayback
import com.github.damontecres.wholphin.data.model.ItemTrackModification
import com.github.damontecres.wholphin.data.model.JellyfinServer
import com.github.damontecres.wholphin.data.model.JellyfinUser
import com.github.damontecres.wholphin.data.model.LibraryDisplayInfo
@ -30,8 +31,9 @@ import java.util.UUID
NavDrawerPinnedItem::class,
LibraryDisplayInfo::class,
PlaybackLanguageChoice::class,
ItemTrackModification::class,
],
version = 11,
version = 12,
exportSchema = true,
autoMigrations = [
AutoMigration(3, 4),
@ -42,6 +44,7 @@ import java.util.UUID
AutoMigration(8, 9),
AutoMigration(9, 10),
AutoMigration(10, 11),
AutoMigration(11, 12),
],
)
@TypeConverters(Converters::class)

View file

@ -5,6 +5,7 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.github.damontecres.wholphin.data.model.ItemPlayback
import com.github.damontecres.wholphin.data.model.ItemTrackModification
import com.github.damontecres.wholphin.data.model.JellyfinUser
import java.util.UUID
@ -26,4 +27,20 @@ interface ItemPlaybackDao {
@Query("SELECT * from ItemPlayback WHERE userId=:userId")
fun getItems(userId: Int): List<ItemPlayback>
@Query("SELECT * FROM ItemTrackModification WHERE userId=:userId AND itemId=:itemId")
suspend fun getTrackModifications(
userId: Int,
itemId: UUID,
): List<ItemTrackModification>
@Query("SELECT * FROM ItemTrackModification WHERE userId=:userId AND itemId=:itemId AND trackIndex=:trackIndex")
suspend fun getTrackModifications(
userId: Int,
itemId: UUID,
trackIndex: Int,
): ItemTrackModification
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun saveItem(item: ItemTrackModification): Long
}

View file

@ -2,6 +2,7 @@ package com.github.damontecres.wholphin.data
import com.github.damontecres.wholphin.data.model.BaseItem
import com.github.damontecres.wholphin.data.model.ItemPlayback
import com.github.damontecres.wholphin.data.model.ItemTrackModification
import com.github.damontecres.wholphin.data.model.PlaybackLanguageChoice
import com.github.damontecres.wholphin.data.model.TrackIndex
import com.github.damontecres.wholphin.preferences.UserPreferences
@ -16,6 +17,7 @@ import timber.log.Timber
import java.util.UUID
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.time.Duration
@Singleton
class ItemPlaybackRepository
@ -179,6 +181,32 @@ class ItemPlaybackRepository
val id = itemPlaybackDao.saveItem(toSave)
return toSave.copy(rowId = id)
}
suspend fun getTrackModifications(
itemId: UUID,
trackIndex: Int,
): ItemTrackModification? =
serverRepository.currentUser.value?.rowId?.let { userId ->
itemPlaybackDao.getTrackModifications(userId, itemId, trackIndex)
}
suspend fun saveTrackModifications(
itemId: UUID,
trackIndex: Int,
delay: Duration,
) {
serverRepository.currentUser.value?.rowId?.let { userId ->
Timber.v("Saving track mod item=%s, track=%s, delay=%s", itemId, trackIndex, delay)
itemPlaybackDao.saveItem(
ItemTrackModification(
userId,
itemId,
trackIndex,
delay.inWholeMilliseconds,
),
)
}
}
}
data class ChosenStreams(

View file

@ -0,0 +1,30 @@
@file:UseSerializers(UUIDSerializer::class)
package com.github.damontecres.wholphin.data.model
import androidx.room.Entity
import androidx.room.ForeignKey
import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import org.jellyfin.sdk.model.serializer.UUIDSerializer
import java.util.UUID
@Entity(
foreignKeys = [
ForeignKey(
entity = JellyfinUser::class,
parentColumns = arrayOf("rowId"),
childColumns = arrayOf("userId"),
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE,
),
],
primaryKeys = ["userId", "itemId", "trackIndex"],
)
@Serializable
data class ItemTrackModification(
val userId: Int,
val itemId: UUID,
val trackIndex: Int,
val delayMs: Long,
)