skip credits

This commit is contained in:
cd16b 2024-01-22 13:40:45 +01:00
parent 015ddd19bd
commit 92eaefe6e1
38 changed files with 194 additions and 35 deletions

View file

@ -32,6 +32,7 @@ import androidx.media3.ui.DefaultTimeBar
import androidx.media3.ui.PlayerView
import androidx.navigation.navArgs
import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.core.R as CoreR
import dev.jdtech.jellyfin.databinding.ActivityPlayerBinding
import dev.jdtech.jellyfin.dialogs.SpeedSelectionDialogFragment
import dev.jdtech.jellyfin.dialogs.TrackSelectionDialogFragment
@ -136,10 +137,15 @@ class PlayerActivity : BasePlayerActivity() {
videoNameTextView.text = currentItemTitle
// Skip Intro button
skipIntroButton.isVisible = !isInPictureInPictureMode && currentIntro != null
skipIntroButton.isVisible = !isInPictureInPictureMode && (currentIntro != null || currentCredit != null)
skipIntroButton.text = if (currentCredit != null) getString(CoreR.string.skip_credit_button) else getString(CoreR.string.skip_intro_button)
skipIntroButton.setOnClickListener {
currentIntro?.let {
binding.playerView.player?.seekTo((it.introEnd * 1000).toLong())
if (currentIntro != null) {
currentIntro?.let {
binding.playerView.player?.seekTo((it.introEnd * 1000).toLong())
}
} else if (currentCredit != null) {
binding.playerView.player?.seekToNext()
}
}

View file

@ -55,7 +55,6 @@
android:layout_gravity="end|bottom"
android:layout_marginEnd="24dp"
android:layout_marginBottom="64dp"
android:text="@string/player_controls_skip_intro"
android:textColor="@android:color/white"
android:visibility="gone"
app:backgroundTint="@color/player_background"

View file

@ -15,6 +15,7 @@ import dev.jdtech.jellyfin.models.FindroidMovie
import dev.jdtech.jellyfin.models.FindroidSource
import dev.jdtech.jellyfin.models.TrickPlayManifest
import dev.jdtech.jellyfin.models.UiText
import dev.jdtech.jellyfin.models.toCreditDto
import dev.jdtech.jellyfin.models.toFindroidEpisodeDto
import dev.jdtech.jellyfin.models.toFindroidMediaStreamDto
import dev.jdtech.jellyfin.models.toFindroidMovieDto
@ -46,6 +47,7 @@ class DownloaderImpl(
try {
val source = jellyfinRepository.getMediaSources(item.id, true).first { it.id == sourceId }
val intro = jellyfinRepository.getIntroTimestamps(item.id)
val credit = jellyfinRepository.getCreditTimestamps(item.id)
val trickPlayManifest = jellyfinRepository.getTrickPlayManifest(item.id)
val trickPlayData = if (trickPlayManifest != null) {
jellyfinRepository.getTrickPlayData(
@ -81,6 +83,9 @@ class DownloaderImpl(
if (intro != null) {
database.insertIntro(intro.toIntroDto(item.id))
}
if (credit != null) {
database.insertCredit(credit.toCreditDto(item.id))
}
if (trickPlayManifest != null && trickPlayData != null) {
downloadTrickPlay(item, trickPlayManifest, trickPlayData)
}
@ -110,6 +115,9 @@ class DownloaderImpl(
if (intro != null) {
database.insertIntro(intro.toIntroDto(item.id))
}
if (credit != null) {
database.insertCredit(credit.toCreditDto(item.id))
}
if (trickPlayManifest != null && trickPlayData != null) {
downloadTrickPlay(item, trickPlayManifest, trickPlayData)
}

View file

@ -185,4 +185,6 @@
<string name="live_tv">Diretta TV</string>
<string name="play">Riproduci</string>
<string name="remove_from_favorites">Rimuovi dai preferiti</string>
<string name="skip_intro_button">Salta intro</string>
<string name="skip_credit_button">Prossimo episodio</string>
</resources>

View file

@ -140,9 +140,9 @@
<string name="pref_player_mpv_vo">Video output</string>
<string name="pref_player_mpv_ao">Audio output</string>
<string name="pref_player_intro_skipper">Intro Skipper</string>
<string name="pref_player_intro_skipper_summary">Requires ConfusedPolarBear\'s Intro Skipper plugin to be installed on the server</string>
<string name="pref_player_intro_skipper_summary">Requires <i>ConfusedPolarBear\'s</i> <b>Intro Skipper</b> plugin to be installed on the server.\nInstall <i>jumoog\'s</i> <b>Intro Skipper v0.1.8 or higher</b> to skip end credits.</string>
<string name="pref_player_trick_play">Trick Play</string>
<string name="pref_player_trick_play_summary">Requires nicknsy\'s Jellyscrub plugin to be installed on the server</string>
<string name="pref_player_trick_play_summary">Requires <i>nicknsy\'s</i> <b>Jellyscrub</b> plugin to be installed on the server</string>
<string name="addresses">Addresses</string>
<string name="add_address">Add address</string>
<string name="add_server_address">Add server address</string>
@ -185,4 +185,6 @@
<string name="unmark_as_played">Unmark as played</string>
<string name="add_to_favorites">Add to favorites</string>
<string name="remove_from_favorites">Remove from favorites</string>
<string name="skip_intro_button">Skip Intro</string>
<string name="skip_credit_button">Next episode</string>
</resources>

View file

@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "3cb9aaa3295b9e461cb94dfc708258ed",
"identityHash": "2611f255654b3d481be40f080a8b5401",
"entities": [
{
"tableName": "servers",
@ -758,6 +758,50 @@
"indices": [],
"foreignKeys": []
},
{
"tableName": "credits",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `start` REAL NOT NULL, `end` REAL NOT NULL, `showAt` REAL NOT NULL, `hideAt` REAL NOT NULL, PRIMARY KEY(`itemId`))",
"fields": [
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "start",
"columnName": "start",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "end",
"columnName": "end",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "showAt",
"columnName": "showAt",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "hideAt",
"columnName": "hideAt",
"affinity": "REAL",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"itemId"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "userdata",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` TEXT NOT NULL, `itemId` TEXT NOT NULL, `played` INTEGER NOT NULL, `favorite` INTEGER NOT NULL, `playbackPositionTicks` INTEGER NOT NULL, `toBeSynced` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`))",
@ -813,7 +857,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3cb9aaa3295b9e461cb94dfc708258ed')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2611f255654b3d481be40f080a8b5401')"
]
}
}

View file

@ -4,6 +4,7 @@ import androidx.room.AutoMigration
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import dev.jdtech.jellyfin.models.CreditDto
import dev.jdtech.jellyfin.models.FindroidEpisodeDto
import dev.jdtech.jellyfin.models.FindroidMediaStreamDto
import dev.jdtech.jellyfin.models.FindroidMovieDto
@ -18,7 +19,7 @@ import dev.jdtech.jellyfin.models.TrickPlayManifestDto
import dev.jdtech.jellyfin.models.User
@Database(
entities = [Server::class, ServerAddress::class, User::class, FindroidMovieDto::class, FindroidShowDto::class, FindroidSeasonDto::class, FindroidEpisodeDto::class, FindroidSourceDto::class, FindroidMediaStreamDto::class, TrickPlayManifestDto::class, IntroDto::class, FindroidUserDataDto::class],
entities = [Server::class, ServerAddress::class, User::class, FindroidMovieDto::class, FindroidShowDto::class, FindroidSeasonDto::class, FindroidEpisodeDto::class, FindroidSourceDto::class, FindroidMediaStreamDto::class, TrickPlayManifestDto::class, IntroDto::class, CreditDto::class, FindroidUserDataDto::class],
version = 3,
autoMigrations = [
AutoMigration(from = 2, to = 3),

View file

@ -6,6 +6,7 @@ import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import dev.jdtech.jellyfin.models.CreditDto
import dev.jdtech.jellyfin.models.FindroidEpisodeDto
import dev.jdtech.jellyfin.models.FindroidMediaStreamDto
import dev.jdtech.jellyfin.models.FindroidMovieDto
@ -222,6 +223,15 @@ interface ServerDatabaseDao {
@Query("DELETE FROM intros WHERE itemId = :itemId")
fun deleteIntro(itemId: UUID)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertCredit(credit: CreditDto)
@Query("SELECT * FROM credits WHERE itemId = :itemId")
fun getCredit(itemId: UUID): CreditDto?
@Query("DELETE FROM credits WHERE itemId = :itemId")
fun deleteCredit(itemId: UUID)
@Query("SELECT * FROM seasons")
fun getSeasons(): List<FindroidSeasonDto>

View file

@ -0,0 +1,33 @@
package dev.jdtech.jellyfin.models
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Credit(
@SerialName("Credits")
val credit: Credits,
)
@Serializable
data class Credits(
@SerialName("IntroStart")
val introStart: Double,
@SerialName("IntroEnd")
val introEnd: Double,
@SerialName("ShowSkipPromptAt")
val showSkipPromptAt: Double,
@SerialName("HideSkipPromptAt")
val hideSkipPromptAt: Double,
)
fun CreditDto.toCredit(): Credit {
return Credit(
credit = Credits(
introStart = start,
introEnd = end,
showSkipPromptAt = showAt,
hideSkipPromptAt = hideAt,
)
)
}

View file

@ -0,0 +1,25 @@
package dev.jdtech.jellyfin.models
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.util.UUID
@Entity(tableName = "credits")
data class CreditDto(
@PrimaryKey
val itemId: UUID,
val start: Double,
val end: Double,
val showAt: Double,
val hideAt: Double,
)
fun Credit.toCreditDto(itemId: UUID): CreditDto {
return CreditDto(
itemId = itemId,
start = credit.introStart,
end = credit.introEnd,
showAt = credit.showSkipPromptAt,
hideAt = credit.hideSkipPromptAt,
)
}

View file

@ -1,6 +1,7 @@
package dev.jdtech.jellyfin.repository
import androidx.paging.PagingData
import dev.jdtech.jellyfin.models.Credit
import dev.jdtech.jellyfin.models.FindroidCollection
import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.models.FindroidItem
@ -86,6 +87,8 @@ interface JellyfinRepository {
suspend fun getIntroTimestamps(itemId: UUID): Intro?
suspend fun getCreditTimestamps(itemId: UUID): Credit?
suspend fun getTrickPlayManifest(itemId: UUID): TrickPlayManifest?
suspend fun getTrickPlayData(itemId: UUID, width: Int): ByteArray?

View file

@ -7,6 +7,7 @@ import androidx.paging.PagingData
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.api.JellyfinApi
import dev.jdtech.jellyfin.database.ServerDatabaseDao
import dev.jdtech.jellyfin.models.Credit
import dev.jdtech.jellyfin.models.FindroidCollection
import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.models.FindroidItem
@ -17,6 +18,7 @@ import dev.jdtech.jellyfin.models.FindroidSource
import dev.jdtech.jellyfin.models.Intro
import dev.jdtech.jellyfin.models.SortBy
import dev.jdtech.jellyfin.models.TrickPlayManifest
import dev.jdtech.jellyfin.models.toCredit
import dev.jdtech.jellyfin.models.toFindroidCollection
import dev.jdtech.jellyfin.models.toFindroidEpisode
import dev.jdtech.jellyfin.models.toFindroidItem
@ -372,6 +374,28 @@ class JellyfinRepositoryImpl(
}
}
override suspend fun getCreditTimestamps(itemId: UUID): Credit? =
withContext(Dispatchers.IO) {
val credit = database.getCredit(itemId)?.toCredit()
if (credit != null) {
return@withContext credit
}
// https://github.com/ConfusedPolarBear/intro-skipper/blob/master/docs/api.md
val pathParameters = mutableMapOf<String, UUID>()
pathParameters["itemId"] = itemId
try {
return@withContext jellyfinApi.api.get<Credit>(
"/Episode/{itemId}/IntroSkipperSegments",
pathParameters,
).content
} catch (e: Exception) {
return@withContext null
}
}
override suspend fun getTrickPlayManifest(itemId: UUID): TrickPlayManifest? =
withContext(Dispatchers.IO) {
val trickPlayManifest = database.getTrickPlayManifest(itemId)

View file

@ -5,6 +5,7 @@ import androidx.paging.PagingData
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.api.JellyfinApi
import dev.jdtech.jellyfin.database.ServerDatabaseDao
import dev.jdtech.jellyfin.models.Credit
import dev.jdtech.jellyfin.models.FindroidCollection
import dev.jdtech.jellyfin.models.FindroidEpisode
import dev.jdtech.jellyfin.models.FindroidItem
@ -15,6 +16,7 @@ import dev.jdtech.jellyfin.models.FindroidSource
import dev.jdtech.jellyfin.models.Intro
import dev.jdtech.jellyfin.models.SortBy
import dev.jdtech.jellyfin.models.TrickPlayManifest
import dev.jdtech.jellyfin.models.toCredit
import dev.jdtech.jellyfin.models.toFindroidEpisode
import dev.jdtech.jellyfin.models.toFindroidMovie
import dev.jdtech.jellyfin.models.toFindroidSeason
@ -184,6 +186,11 @@ class JellyfinRepositoryOfflineImpl(
database.getIntro(itemId)?.toIntro()
}
override suspend fun getCreditTimestamps(itemId: UUID): Credit? =
withContext(Dispatchers.IO) {
database.getCredit(itemId)?.toCredit()
}
override suspend fun getTrickPlayManifest(itemId: UUID): TrickPlayManifest? =
withContext(Dispatchers.IO) {
database.getTrickPlayManifest(itemId)?.toTrickPlayManifest()

View file

@ -18,6 +18,8 @@ import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.models.Credit
import dev.jdtech.jellyfin.models.Credits
import dev.jdtech.jellyfin.models.Intro
import dev.jdtech.jellyfin.models.PlayerItem
import dev.jdtech.jellyfin.mpv.MPVPlayer
@ -55,6 +57,7 @@ constructor(
UiState(
currentItemTitle = "",
currentIntro = null,
currentCredit = null,
currentTrickPlay = null,
fileLoaded = false,
),
@ -65,12 +68,14 @@ constructor(
val eventsChannelFlow = eventsChannel.receiveAsFlow()
private val intros: MutableMap<UUID, Intro> = mutableMapOf()
private val credits: MutableMap<UUID, Credits> = mutableMapOf()
private val trickPlays: MutableMap<UUID, BifData> = mutableMapOf()
data class UiState(
val currentItemTitle: String,
val currentIntro: Intro?,
val currentCredit: Credits?,
val currentTrickPlay: BifData?,
val fileLoaded: Boolean,
)
@ -152,6 +157,9 @@ constructor(
jellyfinRepository.getIntroTimestamps(item.itemId)?.let { intro ->
intros[item.itemId] = intro
}
jellyfinRepository.getCreditTimestamps(item.itemId)?.let { credit ->
credits[item.itemId] = credit.credit
}
}
Timber.d("Stream url: $streamUrl")
@ -241,10 +249,11 @@ constructor(
handler.postDelayed(this, 5000L)
}
}
val introCheckRunnable = object : Runnable {
val skipCheckRunnable = object : Runnable {
override fun run() {
if (player.currentMediaItem != null && player.currentMediaItem!!.mediaId.isNotEmpty()) {
val itemId = UUID.fromString(player.currentMediaItem!!.mediaId)
intros[itemId]?.let { intro ->
val seconds = player.currentPosition / 1000.0
if (seconds > intro.showSkipPromptAt && seconds < intro.hideSkipPromptAt) {
@ -253,12 +262,22 @@ constructor(
}
_uiState.update { it.copy(currentIntro = null) }
}
credits[itemId]?.let { credit ->
val seconds = player.currentPosition / 1000.0
if (seconds > credit.showSkipPromptAt && seconds < credit.hideSkipPromptAt) {
_uiState.update { it.copy(currentCredit = credit) }
return@let
}
_uiState.update { it.copy(currentCredit = null) }
}
}
handler.postDelayed(this, 1000L)
}
}
handler.post(playbackProgressRunnable)
if (intros.isNotEmpty()) handler.post(introCheckRunnable)
if (intros.isNotEmpty()) handler.post(skipCheckRunnable)
}
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {

View file

@ -7,7 +7,6 @@
<string name="player_controls_rewind">Rebobinar</string>&gt;
<string name="player_controls_exit">Salir de reproductor</string>
<string name="player_controls_fast_forward">Avanzar</string>
<string name="player_controls_skip_intro">Saltar intro</string>
<string name="external">Externo</string>
<string name="player_controls_skip_back">Saltar atrás</string>
<string name="player_controls_play_pause">Reproducir pausar</string>

View file

@ -14,6 +14,5 @@
<string name="player_trickplay">Truques</string>
<string name="player_controls_play_pause">Pausa na reprodução</string>
<string name="player_controls_exit">Sair do reprodutor</string>
<string name="player_controls_skip_intro">Pular introdução</string>
<string name="player_controls_picture_in_picture">Insira imagem em imagem</string>
</resources>

View file

@ -9,7 +9,6 @@
<string name="player_controls_skip_back">Přeskočit zpět</string>
<string name="player_controls_play_pause">Přehrát pauza</string>
<string name="player_controls_rewind">Přetočit</string>
<string name="player_controls_skip_intro">Přeskočit úvod</string>
<string name="player_controls_exit">Ukončit přehrávač</string>
<string name="player_controls_fast_forward">Rychlý posun vpřed</string>
<string name="player_controls_skip_forward">Přeskočit vpřed</string>

View file

@ -8,5 +8,4 @@
<string name="player_controls_exit">Player verlassen</string>
<string name="player_controls_rewind">Wiederholen</string>
<string name="player_controls_fast_forward">Vorspulen</string>
<string name="player_controls_skip_intro">Intro überspringen</string>
</resources>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">Salir de reproductor</string>
<string name="player_controls_rewind">Atrasar</string>
<string name="player_controls_fast_forward">Avanzar</string>
<string name="player_controls_skip_intro">Saltar intro</string>
<string name="player_controls_skip_forward">Saltar adelante</string>
<string name="player_trickplay">Avance</string>
<string name="player_controls_lock">Bloquea el reproductor</string>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">Salir del reproductor</string>
<string name="player_controls_rewind">Rebobinar</string>
<string name="player_controls_fast_forward">Avanzar</string>
<string name="player_controls_skip_intro">Saltar introducción</string>
<string name="player_controls_skip_forward">Saltar adelante</string>
<string name="player_controls_lock">Bloquea el reproductor</string>
<string name="player_controls_play_pause">Reproducir pausar</string>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">Quitter le lecteur</string>
<string name="player_controls_rewind">Rembobiner</string>
<string name="player_controls_fast_forward">Avance rapide</string>
<string name="player_controls_skip_intro">Ignorer l\'introduction</string>
<string name="player_controls_lock">Verrouille le lecteur</string>
<string name="player_controls_play_pause">Lecture / Pause</string>
<string name="player_controls_skip_back">Retour en arrière</string>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">Kilépés a lejátszóból</string>
<string name="player_controls_rewind">Visszatekerés</string>
<string name="player_controls_fast_forward">Előrepörgetés</string>
<string name="player_controls_skip_intro">Intro kihagyása</string>
<string name="player_controls_lock">Zárolja a lejátszót</string>
<string name="player_controls_skip_back">Ugrás vissza</string>
<string name="player_controls_skip_forward">Ugrás előre</string>

View file

@ -14,7 +14,6 @@
<string name="player_controls_rewind">Riavvolgi</string>
<string name="player_controls_lock">Blocca il player</string>
<string name="player_controls_exit">Esci dal player</string>
<string name="player_controls_skip_intro">Salta intro</string>
<string name="player_controls_picture_in_picture">Attiva picture in picture</string>
<string name="none">Nessuno</string>
</resources>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">צא מהנגן</string>
<string name="player_controls_rewind">הרצה אחורה</string>
<string name="player_controls_fast_forward">הרצה קדימה</string>
<string name="player_controls_skip_intro">דלג פתיח</string>
<string name="player_controls_skip_forward">דלג קדימה</string>
<string name="player_controls_lock">נועל את הנגן</string>
<string name="player_controls_skip_back">דלג אחורה</string>

View file

@ -8,7 +8,6 @@
<string name="player_controls_rewind">되감기</string>
<string name="player_controls_fast_forward">빨리 감기</string>
<string name="player_controls_exit">플레이어 나가기</string>
<string name="player_controls_skip_intro">오프닝 스킵</string>
<string name="player_controls_lock">플레이어 잠금</string>
<string name="player_trickplay">Trickplay</string>
<string name="player_controls_skip_back">뒤로 건너뛰기</string>

View file

@ -8,5 +8,4 @@
<string name="player_controls_exit">Sluit speler</string>
<string name="player_controls_rewind">Terugspoelen</string>
<string name="player_controls_fast_forward">Snel vooruit</string>
<string name="player_controls_skip_intro">Intro overslaan</string>
</resources>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">Zamknij odtwarzacz</string>
<string name="player_controls_rewind">Przewiń</string>
<string name="player_controls_fast_forward">Przewiń do przodu</string>
<string name="player_controls_skip_intro">Pomiń czołówkę</string>
<string name="player_controls_lock">Zablokuj odtwarzacz</string>
<string name="player_controls_skip_back">Skocz do tyłu</string>
<string name="player_trickplay">Trickplay</string>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">Sair do reprodutor</string>
<string name="player_controls_rewind">Retroceder</string>
<string name="player_controls_fast_forward">Avanço rápido</string>
<string name="player_controls_skip_intro">Pular introdução</string>
<string name="player_controls_lock">Bloqueia o reprodutor</string>
<string name="player_controls_skip_back">Saltar para trás</string>
<string name="player_trickplay">Miniatura de pré-visualização</string>

View file

@ -14,6 +14,5 @@
<string name="player_controls_progress">Barra de progresso</string>
<string name="player_controls_skip_forward">Avançar</string>
<string name="player_controls_skip_back">Pular para trás</string>
<string name="player_controls_skip_intro">Pular introdução</string>
<string name="player_controls_picture_in_picture">Insira imagem em imagem</string>
</resources>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">Выйти из проигрывателя</string>
<string name="player_controls_rewind">Перемотка</string>
<string name="player_controls_fast_forward">Быстрая перемотка</string>
<string name="player_controls_skip_intro">Пропустить заставку</string>
<string name="player_controls_lock">Блокировка</string>
<string name="player_controls_skip_back">Перейти назад</string>
<string name="player_controls_play_pause">Плей пауза</string>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">Zavrieť prehrávač</string>
<string name="player_controls_rewind">Pretočiť dozadu</string>
<string name="player_controls_fast_forward">Pretočiť dopredu</string>
<string name="player_controls_skip_intro">Preskočiť úvodnú zvučku</string>
<string name="player_controls_lock">Zamkne prehrávač</string>
<string name="player_controls_skip_back">Preskočiť späť</string>
<string name="player_controls_skip_forward">Preskočiť dopredu</string>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">Izhod iz predvajalnika</string>
<string name="player_controls_rewind">Previj nazaj</string>
<string name="player_controls_fast_forward">Navijaj naprej</string>
<string name="player_controls_skip_intro">Preskoči uvod</string>
<string name="player_controls_lock">Zaklene predvajalnik</string>
<string name="player_controls_skip_back">Preskoči nazaj</string>
<string name="player_controls_play_pause">Predvajaj ustavi</string>

View file

@ -8,5 +8,4 @@
<string name="player_controls_exit">Avsluta spelare</string>
<string name="player_controls_rewind">Spola tillbaka</string>
<string name="player_controls_fast_forward">Spola framåt</string>
<string name="player_controls_skip_intro">Hoppa över intro</string>
</resources>

View file

@ -8,5 +8,4 @@
<string name="player_controls_rewind">Відмотка</string>
<string name="player_controls_fast_forward">Швидке перемотування</string>
<string name="player_controls_exit">Вийти з плеєра</string>
<string name="player_controls_skip_intro">Пропустити вступ</string>
</resources>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">Thoát khỏi trình xem</string>
<string name="player_controls_rewind">Tua lùi</string>
<string name="player_controls_fast_forward">Tua tới</string>
<string name="player_controls_skip_intro">Bỏ qua đoạn mở đầu</string>
<string name="player_controls_skip_back">Bỏ qua / Trở về</string>
<string name="player_controls_lock">Khoá trình phát</string>
<string name="player_controls_play_pause">Phát / Tạm dừng</string>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">退出播放器</string>
<string name="player_controls_rewind">快退</string>
<string name="player_controls_fast_forward">快进</string>
<string name="player_controls_skip_intro">跳过片头</string>
<string name="player_controls_lock">锁定播放器</string>
<string name="player_controls_skip_back">跳回</string>
<string name="player_controls_play_pause">播放暂停</string>

View file

@ -8,7 +8,6 @@
<string name="player_controls_exit">關閉播放器</string>
<string name="player_controls_rewind">倒帶</string>
<string name="player_controls_fast_forward">快轉</string>
<string name="player_controls_skip_intro">跳過片頭</string>
<string name="player_controls_lock">鎖定播放器</string>
<string name="player_controls_skip_back">跳回</string>
<string name="player_controls_play_pause">播放暫停</string>

View file

@ -11,7 +11,6 @@
<string name="player_controls_play_pause">Play pause</string>
<string name="player_controls_rewind">Rewind</string>
<string name="player_controls_exit">Exit player</string>
<string name="player_controls_skip_intro">Skip Intro</string>
<string name="player_controls_fast_forward">Fast forward</string>
<string name="player_controls_skip_forward">Skip forward</string>
<string name="player_trickplay">Trickplay</string>