Add:Player setting to scale elapsed time by playback speed #1028

- Moved player settings to new playerSettings key in storage preferences
This commit is contained in:
advplyr
2024-01-04 15:28:25 -06:00
parent 5e7d039454
commit 3fe943e989
20 changed files with 118 additions and 82 deletions
+77 -38
View File
@@ -15,7 +15,7 @@
<p class="top-4 absolute left-0 right-0 mx-auto text-center uppercase tracking-widest text-opacity-75" :class="{ 'text-black text-opacity-75': coverBgIsLight }" style="font-size: 10px">{{ isDirectPlayMethod ? $strings.LabelPlaybackDirect : isLocalPlayMethod ? $strings.LabelPlaybackLocal : $strings.LabelPlaybackTranscode }}</p> <p class="top-4 absolute left-0 right-0 mx-auto text-center uppercase tracking-widest text-opacity-75" :class="{ 'text-black text-opacity-75': coverBgIsLight }" style="font-size: 10px">{{ isDirectPlayMethod ? $strings.LabelPlaybackDirect : isLocalPlayMethod ? $strings.LabelPlaybackLocal : $strings.LabelPlaybackTranscode }}</p>
</div> </div>
<div v-if="useChapterTrack && useTotalTrack && showFullscreen" class="absolute total-track w-full z-30 px-6"> <div v-if="playerSettings.useChapterTrack && playerSettings.useTotalTrack && showFullscreen" class="absolute total-track w-full z-30 px-6">
<div class="flex"> <div class="flex">
<p class="font-mono text-fg" style="font-size: 0.8rem">{{ currentTimePretty }}</p> <p class="font-mono text-fg" style="font-size: 0.8rem">{{ currentTimePretty }}</p>
<div class="flex-grow" /> <div class="flex-grow" />
@@ -66,17 +66,17 @@
<div v-else class="w-full h-full absolute top-0 left-0 pointer-events-none" style="background: var(--gradient-minimized-audio-player)" /> <div v-else class="w-full h-full absolute top-0 left-0 pointer-events-none" style="background: var(--gradient-minimized-audio-player)" />
<div id="playerControls" class="absolute right-0 bottom-0 mx-auto" style="max-width: 414px"> <div id="playerControls" class="absolute right-0 bottom-0 mx-auto" style="max-width: 414px">
<div class="flex items-center max-w-full" :class="lockUi ? 'justify-center' : 'justify-between'"> <div class="flex items-center max-w-full" :class="playerSettings.lockUi ? 'justify-center' : 'justify-between'">
<span v-show="showFullscreen && !lockUi" class="material-icons next-icon text-fg cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="jumpChapterStart">first_page</span> <span v-show="showFullscreen && !playerSettings.lockUi" class="material-icons next-icon text-fg cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="jumpChapterStart">first_page</span>
<span v-show="!lockUi" class="material-icons jump-icon text-fg cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="jumpBackwards">{{ jumpBackwardsIcon }}</span> <span v-show="!playerSettings.lockUi" class="material-icons jump-icon text-fg cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="jumpBackwards">{{ jumpBackwardsIcon }}</span>
<div class="play-btn cursor-pointer shadow-sm flex items-center justify-center rounded-full text-primary mx-4 relative overflow-hidden" :style="{ backgroundColor: coverRgb }" :class="{ 'animate-spin': seekLoading }" @mousedown.prevent @mouseup.prevent @click.stop="playPauseClick"> <div class="play-btn cursor-pointer shadow-sm flex items-center justify-center rounded-full text-primary mx-4 relative overflow-hidden" :style="{ backgroundColor: coverRgb }" :class="{ 'animate-spin': seekLoading }" @mousedown.prevent @mouseup.prevent @click.stop="playPauseClick">
<div v-if="!coverBgIsLight" class="absolute top-0 left-0 w-full h-full bg-white bg-opacity-20 pointer-events-none" /> <div v-if="!coverBgIsLight" class="absolute top-0 left-0 w-full h-full bg-white bg-opacity-20 pointer-events-none" />
<span v-if="!isLoading" class="material-icons" :class="{ 'text-white': coverRgb && !coverBgIsLight }">{{ seekLoading ? 'autorenew' : !isPlaying ? 'play_arrow' : 'pause' }}</span> <span v-if="!isLoading" class="material-icons" :class="{ 'text-white': coverRgb && !coverBgIsLight }">{{ seekLoading ? 'autorenew' : !isPlaying ? 'play_arrow' : 'pause' }}</span>
<widgets-spinner-icon v-else class="h-8 w-8" /> <widgets-spinner-icon v-else class="h-8 w-8" />
</div> </div>
<span v-show="!lockUi" class="material-icons jump-icon text-fg cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="jumpForward">{{ jumpForwardIcon }}</span> <span v-show="!playerSettings.lockUi" class="material-icons jump-icon text-fg cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="jumpForward">{{ jumpForwardIcon }}</span>
<span v-show="showFullscreen && !lockUi" class="material-icons next-icon text-fg cursor-pointer" :class="nextChapter && !isLoading ? 'text-opacity-75' : 'text-opacity-10'" @click.stop="jumpNextChapter">last_page</span> <span v-show="showFullscreen && !playerSettings.lockUi" class="material-icons next-icon text-fg cursor-pointer" :class="nextChapter && !isLoading ? 'text-opacity-75' : 'text-opacity-10'" @click.stop="jumpNextChapter">last_page</span>
</div> </div>
</div> </div>
@@ -90,7 +90,7 @@
<div ref="readyTrack" class="h-full bg-track-buffered absolute top-0 left-0 rounded-full pointer-events-none" /> <div ref="readyTrack" class="h-full bg-track-buffered absolute top-0 left-0 rounded-full pointer-events-none" />
<div ref="bufferedTrack" class="h-full bg-track absolute top-0 left-0 rounded-full pointer-events-none" /> <div ref="bufferedTrack" class="h-full bg-track absolute top-0 left-0 rounded-full pointer-events-none" />
<div ref="playedTrack" class="h-full bg-track-cursor absolute top-0 left-0 rounded-full pointer-events-none" /> <div ref="playedTrack" class="h-full bg-track-cursor absolute top-0 left-0 rounded-full pointer-events-none" />
<div ref="trackCursor" class="h-7 w-7 rounded-full absolute pointer-events-auto flex items-center justify-center" :style="{ top: '-11px' }" :class="{ 'opacity-0': lockUi || !showFullscreen }" @touchstart="touchstartCursor"> <div ref="trackCursor" class="h-7 w-7 rounded-full absolute pointer-events-auto flex items-center justify-center" :style="{ top: '-11px' }" :class="{ 'opacity-0': playerSettings.lockUi || !showFullscreen }" @touchstart="touchstartCursor">
<div class="bg-track-cursor rounded-full w-3.5 h-3.5 pointer-events-none" /> <div class="bg-track-cursor rounded-full w-3.5 h-3.5 pointer-events-none" />
</div> </div>
</div> </div>
@@ -98,7 +98,7 @@
</div> </div>
<modals-chapters-modal v-model="showChapterModal" :current-chapter="currentChapter" :chapters="chapters" :playback-rate="currentPlaybackRate" @select="selectChapter" /> <modals-chapters-modal v-model="showChapterModal" :current-chapter="currentChapter" :chapters="chapters" :playback-rate="currentPlaybackRate" @select="selectChapter" />
<modals-dialog v-model="showMoreMenuDialog" :items="menuItems" @action="clickMenuAction" /> <modals-dialog v-model="showMoreMenuDialog" :items="menuItems" width="80vw" @action="clickMenuAction" />
</div> </div>
</template> </template>
@@ -146,9 +146,12 @@ export default {
touchStartY: 0, touchStartY: 0,
touchStartTime: 0, touchStartTime: 0,
touchEndY: 0, touchEndY: 0,
useChapterTrack: false, playerSettings: {
useTotalTrack: true, useChapterTrack: false,
lockUi: false, useTotalTrack: true,
scaleElapsedTimeBySpeed: true,
lockUi: false
},
isLoading: false, isLoading: false,
isDraggingCursor: false, isDraggingCursor: false,
draggingTouchStartX: 0, draggingTouchStartX: 0,
@@ -164,7 +167,7 @@ export default {
showFullscreen(val) { showFullscreen(val) {
this.updateScreenSize() this.updateScreenSize()
this.$store.commit('setPlayerFullscreen', !!val) this.$store.commit('setPlayerFullscreen', !!val)
document.querySelector('body').style.backgroundColor = this.showFullscreen ? this.coverRgb : "" document.querySelector('body').style.backgroundColor = this.showFullscreen ? this.coverRgb : ''
}, },
bookCoverAspectRatio() { bookCoverAspectRatio() {
this.updateScreenSize() this.updateScreenSize()
@@ -187,17 +190,22 @@ export default {
{ {
text: this.$strings.LabelTotalTrack, text: this.$strings.LabelTotalTrack,
value: 'total_track', value: 'total_track',
icon: this.useTotalTrack ? 'check_box' : 'check_box_outline_blank' icon: this.playerSettings.useTotalTrack ? 'check_box' : 'check_box_outline_blank'
}, },
{ {
text: this.$strings.LabelChapterTrack, text: this.$strings.LabelChapterTrack,
value: 'chapter_track', value: 'chapter_track',
icon: this.useChapterTrack ? 'check_box' : 'check_box_outline_blank' icon: this.playerSettings.useChapterTrack ? 'check_box' : 'check_box_outline_blank'
}, },
{ {
text: this.lockUi ? this.$strings.LabelUnlockPlayer : this.$strings.LabelLockPlayer, text: this.$strings.LabelScaleElapsedTimeBySpeed,
value: 'scale_elapsed_time',
icon: this.playerSettings.scaleElapsedTimeBySpeed ? 'check_box' : 'check_box_outline_blank'
},
{
text: this.playerSettings.lockUi ? this.$strings.LabelUnlockPlayer : this.$strings.LabelLockPlayer,
value: 'lock', value: 'lock',
icon: this.lockUi ? 'lock' : 'lock_open' icon: this.playerSettings.lockUi ? 'lock' : 'lock_open'
}, },
{ {
text: this.$strings.LabelClosePlayer, text: this.$strings.LabelClosePlayer,
@@ -322,11 +330,14 @@ export default {
}, },
currentTimePretty() { currentTimePretty() {
let currentTimeToUse = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime let currentTimeToUse = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime
return this.$secondsToTimestamp(currentTimeToUse / this.currentPlaybackRate) if (this.playerSettings.scaleElapsedTimeBySpeed) {
currentTimeToUse = currentTimeToUse / this.currentPlaybackRate
}
return this.$secondsToTimestamp(currentTimeToUse)
}, },
timeRemaining() { timeRemaining() {
let currentTimeToUse = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime let currentTimeToUse = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime
if (this.useChapterTrack && this.currentChapter) { if (this.playerSettings.useChapterTrack && this.currentChapter) {
var currChapTime = currentTimeToUse - this.currentChapter.start var currChapTime = currentTimeToUse - this.currentChapter.start
return (this.currentChapterDuration - currChapTime) / this.currentPlaybackRate return (this.currentChapterDuration - currChapTime) / this.currentPlaybackRate
} }
@@ -494,7 +505,7 @@ export default {
this.updateReadyTrack() this.updateReadyTrack()
}, },
updateReadyTrack() { updateReadyTrack() {
if (this.useChapterTrack) { if (this.playerSettings.useChapterTrack) {
if (this.$refs.totalReadyTrack) { if (this.$refs.totalReadyTrack) {
this.$refs.totalReadyTrack.style.width = this.readyTrackWidth + 'px' this.$refs.totalReadyTrack.style.width = this.readyTrackWidth + 'px'
} }
@@ -511,11 +522,14 @@ export default {
} }
let currentTime = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime let currentTime = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime
if (this.useChapterTrack && this.currentChapter) { if (this.playerSettings.useChapterTrack && this.currentChapter) {
currentTime = Math.max(0, currentTime - this.currentChapter.start) currentTime = Math.max(0, currentTime - this.currentChapter.start)
} }
if (this.playerSettings.scaleElapsedTimeBySpeed) {
currentTime = currentTime / this.currentPlaybackRate
}
ts.innerText = this.$secondsToTimestamp(currentTime / this.currentPlaybackRate) ts.innerText = this.$secondsToTimestamp(currentTime)
}, },
timeupdate() { timeupdate() {
if (!this.$refs.playedTrack) { if (!this.$refs.playedTrack) {
@@ -543,7 +557,7 @@ export default {
let bufferedPercent = this.bufferedTime / this.totalDuration let bufferedPercent = this.bufferedTime / this.totalDuration
const totalBufferedPercent = bufferedPercent const totalBufferedPercent = bufferedPercent
if (this.useChapterTrack && this.currentChapter) { if (this.playerSettings.useChapterTrack && this.currentChapter) {
const currChapTime = currentTimeToUse - this.currentChapter.start const currChapTime = currentTimeToUse - this.currentChapter.start
percentDone = currChapTime / this.currentChapterDuration percentDone = currChapTime / this.currentChapterDuration
bufferedPercent = Math.max(0, Math.min(1, (this.bufferedTime - this.currentChapter.start) / this.currentChapterDuration)) bufferedPercent = Math.max(0, Math.min(1, (this.bufferedTime - this.currentChapter.start) / this.currentChapterDuration))
@@ -557,7 +571,7 @@ export default {
this.$refs.trackCursor.style.left = ptWidth - 14 + 'px' this.$refs.trackCursor.style.left = ptWidth - 14 + 'px'
} }
if (this.useChapterTrack) { if (this.playerSettings.useChapterTrack) {
if (this.$refs.totalPlayedTrack) this.$refs.totalPlayedTrack.style.width = Math.round(totalPercentDone * this.trackWidth) + 'px' if (this.$refs.totalPlayedTrack) this.$refs.totalPlayedTrack.style.width = Math.round(totalPercentDone * this.trackWidth) + 'px'
if (this.$refs.totalBufferedTrack) this.$refs.totalBufferedTrack.style.width = Math.round(totalBufferedPercent * this.trackWidth) + 'px' if (this.$refs.totalBufferedTrack) this.$refs.totalBufferedTrack.style.width = Math.round(totalBufferedPercent * this.trackWidth) + 'px'
} }
@@ -584,7 +598,7 @@ export default {
} }
}, },
async touchstartCursor(e) { async touchstartCursor(e) {
if (!e || !e.touches || !this.$refs.track || !this.showFullscreen || this.lockUi) return if (!e || !e.touches || !this.$refs.track || !this.showFullscreen || this.playerSettings.lockUi) return
await this.$hapticsImpact() await this.$hapticsImpact()
this.isDraggingCursor = true this.isDraggingCursor = true
@@ -670,7 +684,7 @@ export default {
let duration = this.totalDuration let duration = this.totalDuration
let minTime = 0 let minTime = 0
let maxTime = duration let maxTime = duration
if (this.useChapterTrack && this.currentChapter) { if (this.playerSettings.useChapterTrack && this.currentChapter) {
duration = this.currentChapterDuration duration = this.currentChapterDuration
minTime = this.currentChapter.start minTime = this.currentChapter.start
maxTime = minTime + duration maxTime = minTime + duration
@@ -690,37 +704,40 @@ export default {
if (action === 'history') { if (action === 'history') {
this.$router.push(`/media/${this.mediaId}/history?title=${this.title}`) this.$router.push(`/media/${this.mediaId}/history?title=${this.title}`)
this.showFullscreen = false this.showFullscreen = false
} else if (action === 'scale_elapsed_time') {
this.playerSettings.scaleElapsedTimeBySpeed = !this.playerSettings.scaleElapsedTimeBySpeed
this.updateTimestamp()
this.savePlayerSettings()
} else if (action === 'lock') { } else if (action === 'lock') {
this.lockUi = !this.lockUi this.playerSettings.lockUi = !this.playerSettings.lockUi
this.$localStore.setPlayerLock(this.lockUi) this.savePlayerSettings()
} else if (action === 'chapter_track') { } else if (action === 'chapter_track') {
this.useChapterTrack = !this.useChapterTrack this.playerSettings.useChapterTrack = !this.playerSettings.useChapterTrack
this.useTotalTrack = !this.useChapterTrack || this.useTotalTrack this.playerSettings.useTotalTrack = !this.playerSettings.useChapterTrack || this.playerSettings.useTotalTrack
this.updateTimestamp() this.updateTimestamp()
this.updateTrack() this.updateTrack()
this.updateReadyTrack() this.updateReadyTrack()
this.updateUseChapterTrack() this.updateUseChapterTrack()
this.$localStore.setUseTotalTrack(this.useTotalTrack) this.savePlayerSettings()
} else if (action === 'total_track') { } else if (action === 'total_track') {
this.useTotalTrack = !this.useTotalTrack this.playerSettings.useTotalTrack = !this.playerSettings.useTotalTrack
this.useChapterTrack = !this.useTotalTrack || this.useChapterTrack this.playerSettings.useChapterTrack = !this.playerSettings.useTotalTrack || this.playerSettings.useChapterTrack
this.updateTimestamp() this.updateTimestamp()
this.updateTrack() this.updateTrack()
this.updateReadyTrack() this.updateReadyTrack()
this.updateUseChapterTrack() this.updateUseChapterTrack()
this.$localStore.setUseTotalTrack(this.useTotalTrack) this.savePlayerSettings()
} else if (action === 'close') { } else if (action === 'close') {
this.closePlayback() this.closePlayback()
} }
}) })
}, },
updateUseChapterTrack() { updateUseChapterTrack() {
this.$localStore.setUseChapterTrack(this.useChapterTrack)
// Chapter track in NowPlaying only supported on iOS for now // Chapter track in NowPlaying only supported on iOS for now
if (this.$platform === 'ios') { if (this.$platform === 'ios') {
AbsAudioPlayer.setChapterTrack({ enabled: this.useChapterTrack }) AbsAudioPlayer.setChapterTrack({ enabled: this.playerSettings.useChapterTrack })
} }
}, },
forceCloseDropdownMenu() { forceCloseDropdownMenu() {
@@ -739,6 +756,30 @@ export default {
this.isLoading = false this.isLoading = false
this.playbackSession = null this.playbackSession = null
}, },
async loadPlayerSettings() {
const savedPlayerSettings = await this.$localStore.getPlayerSettings()
if (!savedPlayerSettings) {
// In 0.9.72-beta 'useChapterTrack', 'useTotalTrack' and 'playerLock' was replaced with 'playerSettings' JSON object
// Check if this old key was set and if so migrate them over to 'playerSettings'
const chapterTrackPref = await this.$localStore.getPreferenceByKey('useChapterTrack')
if (chapterTrackPref) {
this.playerSettings.useChapterTrack = chapterTrackPref === '1'
const totalTrackPref = await this.$localStore.getPreferenceByKey('useTotalTrack')
this.playerSettings.useTotalTrack = totalTrackPref === '1'
const playerLockPref = await this.$localStore.getPreferenceByKey('playerLock')
this.playerSettings.lockUi = playerLockPref === '1'
}
this.savePlayerSettings()
} else {
this.playerSettings.useChapterTrack = !!savedPlayerSettings.useChapterTrack
this.playerSettings.useTotalTrack = !!savedPlayerSettings.useTotalTrack
this.playerSettings.lockUi = !!savedPlayerSettings.lockUi
this.playerSettings.scaleElapsedTimeBySpeed = !!savedPlayerSettings.scaleElapsedTimeBySpeed
}
},
savePlayerSettings() {
return this.$localStore.setPlayerSettings({ ...this.playerSettings })
},
// //
// Listeners from audio AbsAudioPlayer // Listeners from audio AbsAudioPlayer
// //
@@ -805,9 +846,7 @@ export default {
this.updateTimestamp() this.updateTimestamp()
}, },
async init() { async init() {
this.useChapterTrack = await this.$localStore.getUseChapterTrack() await this.loadPlayerSettings()
this.useTotalTrack = await this.$localStore.getUseTotalTrack()
this.lockUi = await this.$localStore.getPlayerLock()
this.onPlaybackSessionListener = AbsAudioPlayer.addListener('onPlaybackSession', this.onPlaybackSession) this.onPlaybackSessionListener = AbsAudioPlayer.addListener('onPlaybackSession', this.onPlaybackSession)
this.onPlaybackClosedListener = AbsAudioPlayer.addListener('onPlaybackClosed', this.onPlaybackClosed) this.onPlaybackClosedListener = AbsAudioPlayer.addListener('onPlaybackClosed', this.onPlaybackClosed)
+1 -1
View File
@@ -40,7 +40,7 @@ export default {
default: '16px' default: '16px'
}, },
width: { width: {
type: Number, type: [String, Number],
default: 300 default: 300
} }
}, },
+23 -43
View File
@@ -42,56 +42,20 @@ class LocalStorage {
} }
} }
async setUseChapterTrack(useChapterTrack) { async setPlayerSettings(playerSettings) {
try { try {
await Preferences.set({ key: 'useChapterTrack', value: useChapterTrack ? '1' : '0' }) await Preferences.set({ key: 'playerSettings', value: JSON.stringify(playerSettings) })
} catch (error) { } catch (error) {
console.error('[LocalStorage] Failed to set use chapter track', error) console.error('[LocalStorage] Failed to set player settings', error)
} }
} }
async getUseChapterTrack() { async getPlayerSettings() {
try { try {
var obj = await Preferences.get({ key: 'useChapterTrack' }) || {} const playerSettingsObj = await Preferences.get({ key: 'playerSettings' }) || {}
return obj.value === '1' return playerSettingsObj.value ? JSON.parse(playerSettingsObj.value) : null
} catch (error) { } catch (error) {
console.error('[LocalStorage] Failed to get use chapter track', error) console.error('[LocalStorage] Failed to get player settings', error)
return false
}
}
async setUseTotalTrack(useTotalTrack) {
try {
await Preferences.set({ key: 'useTotalTrack', value: useTotalTrack ? '1' : '0' })
} catch (error) {
console.error('[LocalStorage] Failed to set use total track', error)
}
}
async getUseTotalTrack() {
try {
var obj = await Preferences.get({ key: 'useTotalTrack' }) || {}
return obj.value === '1'
} catch (error) {
console.error('[LocalStorage] Failed to get use total track', error)
return false
}
}
async setPlayerLock(lock) {
try {
await Preferences.set({ key: 'playerLock', value: lock ? '1' : '0' })
} catch (error) {
console.error('[LocalStorage] Failed to set player lock', error)
}
}
async getPlayerLock() {
try {
var obj = await Preferences.get({ key: 'playerLock' }) || {}
return obj.value === '1'
} catch (error) {
console.error('[LocalStorage] Failed to get player lock', error)
return false return false
} }
} }
@@ -179,6 +143,22 @@ class LocalStorage {
return false return false
} }
} }
/**
* Get preference value by key
*
* @param {string} key
* @returns {Promise<string>}
*/
async getPreferenceByKey(key) {
try {
const obj = await Preferences.get({ key }) || {}
return obj.value || null
} catch (error) {
console.error(`[LocalStorage] Failed to get preference "${key}"`, error)
return null
}
}
} }
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Vlastní jméno vlastníka", "LabelRSSFeedCustomOwnerName": "Vlastní jméno vlastníka",
"LabelRSSFeedPreventIndexing": "Zabránit indexování", "LabelRSSFeedPreventIndexing": "Zabránit indexování",
"LabelRSSFeedSlug": "RSS kanál Slug", "LabelRSSFeedSlug": "RSS kanál Slug",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Sezóna", "LabelSeason": "Sezóna",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Série", "LabelSeries": "Série",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Brugerdefineret ejerens navn", "LabelRSSFeedCustomOwnerName": "Brugerdefineret ejerens navn",
"LabelRSSFeedPreventIndexing": "Forhindrer indeksering", "LabelRSSFeedPreventIndexing": "Forhindrer indeksering",
"LabelRSSFeedSlug": "RSS-feed-slug", "LabelRSSFeedSlug": "RSS-feed-slug",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Sæson", "LabelSeason": "Sæson",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Serie", "LabelSeries": "Serie",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Benutzerdefinierter Name des Eigentümers", "LabelRSSFeedCustomOwnerName": "Benutzerdefinierter Name des Eigentümers",
"LabelRSSFeedPreventIndexing": "Indizierung verhindern", "LabelRSSFeedPreventIndexing": "Indizierung verhindern",
"LabelRSSFeedSlug": "RSS Feed Schlagwort", "LabelRSSFeedSlug": "RSS Feed Schlagwort",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Staffel", "LabelSeason": "Staffel",
"LabelSelectADevice": "Wähle ein Gerät", "LabelSelectADevice": "Wähle ein Gerät",
"LabelSeries": "Serien", "LabelSeries": "Serien",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Custom owner Name", "LabelRSSFeedCustomOwnerName": "Custom owner Name",
"LabelRSSFeedPreventIndexing": "Prevent Indexing", "LabelRSSFeedPreventIndexing": "Prevent Indexing",
"LabelRSSFeedSlug": "RSS Feed Slug", "LabelRSSFeedSlug": "RSS Feed Slug",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Season", "LabelSeason": "Season",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Series", "LabelSeries": "Series",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Nombre de dueño personalizado", "LabelRSSFeedCustomOwnerName": "Nombre de dueño personalizado",
"LabelRSSFeedPreventIndexing": "Prevenir Indexado", "LabelRSSFeedPreventIndexing": "Prevenir Indexado",
"LabelRSSFeedSlug": "Fuente RSS Slug", "LabelRSSFeedSlug": "Fuente RSS Slug",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Temporada", "LabelSeason": "Temporada",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Series", "LabelSeries": "Series",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Nom propriétaire personnalisé", "LabelRSSFeedCustomOwnerName": "Nom propriétaire personnalisé",
"LabelRSSFeedPreventIndexing": "Empêcher lindexation", "LabelRSSFeedPreventIndexing": "Empêcher lindexation",
"LabelRSSFeedSlug": "Identificateur dadresse du Flux RSS ", "LabelRSSFeedSlug": "Identificateur dadresse du Flux RSS ",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Saison", "LabelSeason": "Saison",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Séries", "LabelSeries": "Séries",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Custom owner Name", "LabelRSSFeedCustomOwnerName": "Custom owner Name",
"LabelRSSFeedPreventIndexing": "Prevent Indexing", "LabelRSSFeedPreventIndexing": "Prevent Indexing",
"LabelRSSFeedSlug": "RSS Feed Slug", "LabelRSSFeedSlug": "RSS Feed Slug",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Season", "LabelSeason": "Season",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Series", "LabelSeries": "Series",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Custom owner Name", "LabelRSSFeedCustomOwnerName": "Custom owner Name",
"LabelRSSFeedPreventIndexing": "Prevent Indexing", "LabelRSSFeedPreventIndexing": "Prevent Indexing",
"LabelRSSFeedSlug": "RSS Feed Slug", "LabelRSSFeedSlug": "RSS Feed Slug",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Season", "LabelSeason": "Season",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Series", "LabelSeries": "Series",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Custom owner Name", "LabelRSSFeedCustomOwnerName": "Custom owner Name",
"LabelRSSFeedPreventIndexing": "Prevent Indexing", "LabelRSSFeedPreventIndexing": "Prevent Indexing",
"LabelRSSFeedSlug": "RSS Feed Slug", "LabelRSSFeedSlug": "RSS Feed Slug",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Sezona", "LabelSeason": "Sezona",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Serije", "LabelSeries": "Serije",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Nome del proprietario personalizzato", "LabelRSSFeedCustomOwnerName": "Nome del proprietario personalizzato",
"LabelRSSFeedPreventIndexing": "Impedisci l'indicizzazione", "LabelRSSFeedPreventIndexing": "Impedisci l'indicizzazione",
"LabelRSSFeedSlug": "RSS Feed Slug", "LabelRSSFeedSlug": "RSS Feed Slug",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Stagione", "LabelSeason": "Stagione",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Serie", "LabelSeries": "Serie",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Pasirinktinis savininko vardas", "LabelRSSFeedCustomOwnerName": "Pasirinktinis savininko vardas",
"LabelRSSFeedPreventIndexing": "Neleisti indeksuoti", "LabelRSSFeedPreventIndexing": "Neleisti indeksuoti",
"LabelRSSFeedSlug": "RSS srauto identifikatorius", "LabelRSSFeedSlug": "RSS srauto identifikatorius",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Sezonas", "LabelSeason": "Sezonas",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Serija", "LabelSeries": "Serija",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Aangepaste naam eigenaar", "LabelRSSFeedCustomOwnerName": "Aangepaste naam eigenaar",
"LabelRSSFeedPreventIndexing": "Voorkom indexering", "LabelRSSFeedPreventIndexing": "Voorkom indexering",
"LabelRSSFeedSlug": "RSS-feed slug", "LabelRSSFeedSlug": "RSS-feed slug",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Seizoen", "LabelSeason": "Seizoen",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Serie", "LabelSeries": "Serie",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Tilpasset eier Navn", "LabelRSSFeedCustomOwnerName": "Tilpasset eier Navn",
"LabelRSSFeedPreventIndexing": "Forhindre indeksering", "LabelRSSFeedPreventIndexing": "Forhindre indeksering",
"LabelRSSFeedSlug": "RSS Feed Slug", "LabelRSSFeedSlug": "RSS Feed Slug",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Sesong", "LabelSeason": "Sesong",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Serier", "LabelSeries": "Serier",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Custom owner Name", "LabelRSSFeedCustomOwnerName": "Custom owner Name",
"LabelRSSFeedPreventIndexing": "Prevent Indexing", "LabelRSSFeedPreventIndexing": "Prevent Indexing",
"LabelRSSFeedSlug": "RSS Feed Slug", "LabelRSSFeedSlug": "RSS Feed Slug",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Sezon", "LabelSeason": "Sezon",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Serie", "LabelSeries": "Serie",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Пользовательское Имя владельца", "LabelRSSFeedCustomOwnerName": "Пользовательское Имя владельца",
"LabelRSSFeedPreventIndexing": "Запретить индексирование", "LabelRSSFeedPreventIndexing": "Запретить индексирование",
"LabelRSSFeedSlug": "Встроить RSS-канал", "LabelRSSFeedSlug": "Встроить RSS-канал",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Сезон", "LabelSeason": "Сезон",
"LabelSelectADevice": "Выбор девайса", "LabelSelectADevice": "Выбор девайса",
"LabelSeries": "Серия", "LabelSeries": "Серия",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "Anpassat ägarnamn", "LabelRSSFeedCustomOwnerName": "Anpassat ägarnamn",
"LabelRSSFeedPreventIndexing": "Förhindra indexering", "LabelRSSFeedPreventIndexing": "Förhindra indexering",
"LabelRSSFeedSlug": "RSS-flödesslag", "LabelRSSFeedSlug": "RSS-flödesslag",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "Säsong", "LabelSeason": "Säsong",
"LabelSelectADevice": "Select a device", "LabelSelectADevice": "Select a device",
"LabelSeries": "Serie", "LabelSeries": "Serie",
+1
View File
@@ -194,6 +194,7 @@
"LabelRSSFeedCustomOwnerName": "自定义所有者名称", "LabelRSSFeedCustomOwnerName": "自定义所有者名称",
"LabelRSSFeedPreventIndexing": "防止索引", "LabelRSSFeedPreventIndexing": "防止索引",
"LabelRSSFeedSlug": "RSS 源段", "LabelRSSFeedSlug": "RSS 源段",
"LabelScaleElapsedTimeBySpeed": "Scale Elapsed Time by Speed",
"LabelSeason": "季", "LabelSeason": "季",
"LabelSelectADevice": "选择设备", "LabelSelectADevice": "选择设备",
"LabelSeries": "系列", "LabelSeries": "系列",