mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-03-07 00:19:41 +00:00
Merge 978128bf99 into 1d0b7e383a
This commit is contained in:
commit
98b29ecdb4
5 changed files with 145 additions and 2 deletions
|
|
@ -280,9 +280,11 @@ export default {
|
||||||
this.playerHandler.playPause()
|
this.playerHandler.playPause()
|
||||||
},
|
},
|
||||||
jumpForward() {
|
jumpForward() {
|
||||||
|
this._manualSeekTime = Date.now()
|
||||||
this.playerHandler.jumpForward()
|
this.playerHandler.jumpForward()
|
||||||
},
|
},
|
||||||
jumpBackward() {
|
jumpBackward() {
|
||||||
|
this._manualSeekTime = Date.now()
|
||||||
this.playerHandler.jumpBackward()
|
this.playerHandler.jumpBackward()
|
||||||
},
|
},
|
||||||
setVolume(volume) {
|
setVolume(volume) {
|
||||||
|
|
@ -293,6 +295,7 @@ export default {
|
||||||
this.playerHandler.setPlaybackRate(playbackRate)
|
this.playerHandler.setPlaybackRate(playbackRate)
|
||||||
},
|
},
|
||||||
seek(time) {
|
seek(time) {
|
||||||
|
this._manualSeekTime = Date.now()
|
||||||
this.playerHandler.seek(time)
|
this.playerHandler.seek(time)
|
||||||
},
|
},
|
||||||
playbackTimeUpdate(time) {
|
playbackTimeUpdate(time) {
|
||||||
|
|
@ -308,6 +311,9 @@ export default {
|
||||||
if (this.sleepTimerType === this.$constants.SleepTimerTypes.CHAPTER && this.sleepTimerSet) {
|
if (this.sleepTimerType === this.$constants.SleepTimerTypes.CHAPTER && this.sleepTimerSet) {
|
||||||
this.checkChapterEnd()
|
this.checkChapterEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for intro/outro and skip if needed
|
||||||
|
this.checkAndSkipIntroOutro(time)
|
||||||
},
|
},
|
||||||
setDuration(duration) {
|
setDuration(duration) {
|
||||||
this.totalDuration = duration
|
this.totalDuration = duration
|
||||||
|
|
@ -543,6 +549,87 @@ export default {
|
||||||
this.playerHandler.resetPlayer() // Closes player without reporting to server
|
this.playerHandler.resetPlayer() // Closes player without reporting to server
|
||||||
this.$store.commit('setMediaPlaying', null)
|
this.$store.commit('setMediaPlaying', null)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// get skip settings
|
||||||
|
getSkipSettings() {
|
||||||
|
return {
|
||||||
|
skipIntro: this.$store.getters['user/getUserSetting']('skipIntro'),
|
||||||
|
introDuration: this.$store.getters['user/getUserSetting']('introDuration'),
|
||||||
|
skipOutro: this.$store.getters['user/getUserSetting']('skipOutro'),
|
||||||
|
outroDuration: this.$store.getters['user/getUserSetting']('outroDuration')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// check and skip intro/outro
|
||||||
|
checkAndSkipIntroOutro(currentTime) {
|
||||||
|
const skipSettings = this.getSkipSettings()
|
||||||
|
if (!skipSettings) return
|
||||||
|
|
||||||
|
const doSkipIntro = skipSettings.skipIntro && skipSettings.introDuration > 0
|
||||||
|
const doSkipOutro = skipSettings.skipOutro && skipSettings.outroDuration > 0
|
||||||
|
if (!doSkipIntro && !doSkipOutro) return
|
||||||
|
if (!this.isPlaying || !this.chapters.length) return
|
||||||
|
|
||||||
|
// The skip function is not triggered within 2 seconds after the user manually seeks
|
||||||
|
if (this._manualSeekTime && Date.now() - this._manualSeekTime < 2000) return
|
||||||
|
|
||||||
|
// Reentry guard: When skipping, wait until reaching the target position before cancelling
|
||||||
|
if (this._isSkipping) {
|
||||||
|
if (this._skipTarget != null && currentTime >= this._skipTarget - 0.5) {
|
||||||
|
this._isSkipping = false
|
||||||
|
this._skipTarget = null
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const chapter = this.chapters.find((ch) => ch.start <= currentTime && currentTime < ch.end)
|
||||||
|
if (!chapter) return
|
||||||
|
|
||||||
|
const introDuration = doSkipIntro ? skipSettings.introDuration : 0
|
||||||
|
const outroDuration = doSkipOutro ? skipSettings.outroDuration : 0
|
||||||
|
|
||||||
|
const introEndTime = Math.min(chapter.start + introDuration, chapter.end)
|
||||||
|
const outroStartTime = Math.max(chapter.end - outroDuration, chapter.start)
|
||||||
|
|
||||||
|
// Short chapter: If the intro and outro intervals overlap, do not skip
|
||||||
|
if (doSkipIntro && doSkipOutro && introEndTime > outroStartTime) return
|
||||||
|
|
||||||
|
// Check whether it is within the intro interval
|
||||||
|
if (doSkipIntro && currentTime < introEndTime) {
|
||||||
|
const target = introEndTime + 0.5
|
||||||
|
this._isSkipping = true
|
||||||
|
this._skipTarget = target
|
||||||
|
this.seek(target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether it is within the outro interval
|
||||||
|
if (doSkipOutro && currentTime >= outroStartTime) {
|
||||||
|
const chapterIndex = this.chapters.indexOf(chapter)
|
||||||
|
const nextChapter = this.chapters[chapterIndex + 1]
|
||||||
|
|
||||||
|
if (nextChapter) {
|
||||||
|
// has next chapter: skip to next chapter start (if skipIntro is on, skip intro)
|
||||||
|
let target = nextChapter.start
|
||||||
|
if (doSkipIntro) {
|
||||||
|
const nextIntroEnd = Math.min(nextChapter.start + introDuration, nextChapter.end)
|
||||||
|
const nextOutroStart = Math.max(nextChapter.end - outroDuration, nextChapter.start)
|
||||||
|
// ensure that the next chapter intro/outro does not overlap
|
||||||
|
if (nextIntroEnd <= nextOutroStart) {
|
||||||
|
target = nextIntroEnd + 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._isSkipping = true
|
||||||
|
this._skipTarget = target
|
||||||
|
this.seek(target)
|
||||||
|
} else {
|
||||||
|
// last chapter: skip to end
|
||||||
|
this._isSkipping = true
|
||||||
|
this._skipTarget = chapter.end
|
||||||
|
this.seek(chapter.end)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,28 @@
|
||||||
<div class="flex items-center mb-4">
|
<div class="flex items-center mb-4">
|
||||||
<ui-select-input v-model="playbackRateIncrementDecrement" :label="$strings.LabelPlaybackRateIncrementDecrement" menuMaxHeight="250px" :items="playbackRateIncrementDecrementValues" @input="setPlaybackRateIncrementDecrementAmount" />
|
<ui-select-input v-model="playbackRateIncrementDecrement" :label="$strings.LabelPlaybackRateIncrementDecrement" menuMaxHeight="250px" :items="playbackRateIncrementDecrementValues" @input="setPlaybackRateIncrementDecrementAmount" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="border-t pt-4 mt-6">
|
||||||
|
<h4 class="text-lg font-medium mb-4">{{ $strings.HeaderChapterIntroOutroSkipSettings }}</h4>
|
||||||
|
|
||||||
|
<div class="flex items-center mb-4">
|
||||||
|
<ui-toggle-switch v-model="skipIntro" @input="setSkipIntro" />
|
||||||
|
<div class="pl-4 flex-1">
|
||||||
|
<span>{{ $strings.LabelSkipChapterIntro }}</span>
|
||||||
|
</div>
|
||||||
|
<ui-text-input v-model="introDuration" type="number" min="0" max="60" @input="setIntroDuration" class="w-20" />
|
||||||
|
<span class="ml-2 text-sm text-gray-400">{{ $strings.LabelSeconds }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center mb-4">
|
||||||
|
<ui-toggle-switch v-model="skipOutro" @input="setSkipOutro" />
|
||||||
|
<div class="pl-4 flex-1">
|
||||||
|
<span>{{ $strings.LabelSkipChapterOutro }}</span>
|
||||||
|
</div>
|
||||||
|
<ui-text-input v-model="outroDuration" type="number" min="0" max="60" @input="setOutroDuration" class="w-20" />
|
||||||
|
<span class="ml-2 text-sm text-gray-400">{{ $strings.LabelSeconds }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</modals-modal>
|
</modals-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -40,7 +62,11 @@ export default {
|
||||||
jumpForwardAmount: 10,
|
jumpForwardAmount: 10,
|
||||||
jumpBackwardAmount: 10,
|
jumpBackwardAmount: 10,
|
||||||
playbackRateIncrementDecrementValues: [0.1, 0.05],
|
playbackRateIncrementDecrementValues: [0.1, 0.05],
|
||||||
playbackRateIncrementDecrement: 0.1
|
playbackRateIncrementDecrement: 0.1,
|
||||||
|
skipIntro: false,
|
||||||
|
introDuration: 10,
|
||||||
|
skipOutro: false,
|
||||||
|
outroDuration: 10
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -69,11 +95,29 @@ export default {
|
||||||
this.playbackRateIncrementDecrement = val
|
this.playbackRateIncrementDecrement = val
|
||||||
this.$store.dispatch('user/updateUserSettings', { playbackRateIncrementDecrement: val })
|
this.$store.dispatch('user/updateUserSettings', { playbackRateIncrementDecrement: val })
|
||||||
},
|
},
|
||||||
|
setSkipIntro() {
|
||||||
|
this.$store.dispatch('user/updateUserSettings', { skipIntro: this.skipIntro })
|
||||||
|
},
|
||||||
|
setIntroDuration() {
|
||||||
|
this.introDuration = Math.max(0, Math.min(60, parseInt(this.introDuration) || 0))
|
||||||
|
this.$store.dispatch('user/updateUserSettings', { introDuration: this.introDuration })
|
||||||
|
},
|
||||||
|
setSkipOutro() {
|
||||||
|
this.$store.dispatch('user/updateUserSettings', { skipOutro: this.skipOutro })
|
||||||
|
},
|
||||||
|
setOutroDuration() {
|
||||||
|
this.outroDuration = Math.max(0, Math.min(60, parseInt(this.outroDuration) || 0))
|
||||||
|
this.$store.dispatch('user/updateUserSettings', { outroDuration: this.outroDuration })
|
||||||
|
},
|
||||||
settingsUpdated() {
|
settingsUpdated() {
|
||||||
this.useChapterTrack = this.$store.getters['user/getUserSetting']('useChapterTrack')
|
this.useChapterTrack = this.$store.getters['user/getUserSetting']('useChapterTrack')
|
||||||
this.jumpForwardAmount = this.$store.getters['user/getUserSetting']('jumpForwardAmount')
|
this.jumpForwardAmount = this.$store.getters['user/getUserSetting']('jumpForwardAmount')
|
||||||
this.jumpBackwardAmount = this.$store.getters['user/getUserSetting']('jumpBackwardAmount')
|
this.jumpBackwardAmount = this.$store.getters['user/getUserSetting']('jumpBackwardAmount')
|
||||||
this.playbackRateIncrementDecrement = this.$store.getters['user/getUserSetting']('playbackRateIncrementDecrement')
|
this.playbackRateIncrementDecrement = this.$store.getters['user/getUserSetting']('playbackRateIncrementDecrement')
|
||||||
|
this.skipIntro = this.$store.getters['user/getUserSetting']('skipIntro') || false
|
||||||
|
this.introDuration = this.$store.getters['user/getUserSetting']('introDuration') || 10
|
||||||
|
this.skipOutro = this.$store.getters['user/getUserSetting']('skipOutro') || false
|
||||||
|
this.outroDuration = this.$store.getters['user/getUserSetting']('outroDuration') || 10
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,11 @@ export const state = () => ({
|
||||||
authorSortBy: 'name',
|
authorSortBy: 'name',
|
||||||
authorSortDesc: false,
|
authorSortDesc: false,
|
||||||
jumpForwardAmount: 10,
|
jumpForwardAmount: 10,
|
||||||
jumpBackwardAmount: 10
|
jumpBackwardAmount: 10,
|
||||||
|
skipIntro: false,
|
||||||
|
introDuration: 10,
|
||||||
|
skipOutro: false,
|
||||||
|
outroDuration: 10
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@
|
||||||
"HeaderBackups": "Backups",
|
"HeaderBackups": "Backups",
|
||||||
"HeaderBulkChapterModal": "Add Multiple Chapters",
|
"HeaderBulkChapterModal": "Add Multiple Chapters",
|
||||||
"HeaderChangePassword": "Change Password",
|
"HeaderChangePassword": "Change Password",
|
||||||
|
"HeaderChapterIntroOutroSkipSettings": "Intro/Outro Skip Settings",
|
||||||
"HeaderChapters": "Chapters",
|
"HeaderChapters": "Chapters",
|
||||||
"HeaderChooseAFolder": "Choose a Folder",
|
"HeaderChooseAFolder": "Choose a Folder",
|
||||||
"HeaderCollection": "Collection",
|
"HeaderCollection": "Collection",
|
||||||
|
|
@ -567,6 +568,7 @@
|
||||||
"LabelSearchTitleOrASIN": "Search Title or ASIN",
|
"LabelSearchTitleOrASIN": "Search Title or ASIN",
|
||||||
"LabelSeason": "Season",
|
"LabelSeason": "Season",
|
||||||
"LabelSeasonNumber": "Season #{0}",
|
"LabelSeasonNumber": "Season #{0}",
|
||||||
|
"LabelSeconds": "seconds",
|
||||||
"LabelSelectAll": "Select all",
|
"LabelSelectAll": "Select all",
|
||||||
"LabelSelectAllEpisodes": "Select all episodes",
|
"LabelSelectAllEpisodes": "Select all episodes",
|
||||||
"LabelSelectEpisodesShowing": "Select {0} episodes showing",
|
"LabelSelectEpisodesShowing": "Select {0} episodes showing",
|
||||||
|
|
@ -629,6 +631,8 @@
|
||||||
"LabelShowSeconds": "Show seconds",
|
"LabelShowSeconds": "Show seconds",
|
||||||
"LabelShowSubtitles": "Show Subtitles",
|
"LabelShowSubtitles": "Show Subtitles",
|
||||||
"LabelSize": "Size",
|
"LabelSize": "Size",
|
||||||
|
"LabelSkipChapterIntro": "Skip Chapter Intro",
|
||||||
|
"LabelSkipChapterOutro": "Skip Chapter Outro",
|
||||||
"LabelSleepTimer": "Sleep timer",
|
"LabelSleepTimer": "Sleep timer",
|
||||||
"LabelSlug": "Slug",
|
"LabelSlug": "Slug",
|
||||||
"LabelSortAscending": "Ascending",
|
"LabelSortAscending": "Ascending",
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@
|
||||||
"HeaderBackups": "备份",
|
"HeaderBackups": "备份",
|
||||||
"HeaderBulkChapterModal": "添加多个章节",
|
"HeaderBulkChapterModal": "添加多个章节",
|
||||||
"HeaderChangePassword": "更改密码",
|
"HeaderChangePassword": "更改密码",
|
||||||
|
"HeaderChapterIntroOutroSkipSettings": "片头 / 片尾跳过设置",
|
||||||
"HeaderChapters": "章节",
|
"HeaderChapters": "章节",
|
||||||
"HeaderChooseAFolder": "选择文件夹",
|
"HeaderChooseAFolder": "选择文件夹",
|
||||||
"HeaderCollection": "收藏",
|
"HeaderCollection": "收藏",
|
||||||
|
|
@ -567,6 +568,7 @@
|
||||||
"LabelSearchTitleOrASIN": "搜索标题或 ASIN",
|
"LabelSearchTitleOrASIN": "搜索标题或 ASIN",
|
||||||
"LabelSeason": "季",
|
"LabelSeason": "季",
|
||||||
"LabelSeasonNumber": "第 {0} 季",
|
"LabelSeasonNumber": "第 {0} 季",
|
||||||
|
"LabelSeconds": "秒",
|
||||||
"LabelSelectAll": "全选",
|
"LabelSelectAll": "全选",
|
||||||
"LabelSelectAllEpisodes": "选择所有剧集",
|
"LabelSelectAllEpisodes": "选择所有剧集",
|
||||||
"LabelSelectEpisodesShowing": "选择正在播放的 {0} 剧集",
|
"LabelSelectEpisodesShowing": "选择正在播放的 {0} 剧集",
|
||||||
|
|
@ -629,6 +631,8 @@
|
||||||
"LabelShowSeconds": "显示秒数",
|
"LabelShowSeconds": "显示秒数",
|
||||||
"LabelShowSubtitles": "显示标题",
|
"LabelShowSubtitles": "显示标题",
|
||||||
"LabelSize": "文件大小",
|
"LabelSize": "文件大小",
|
||||||
|
"LabelSkipChapterIntro": "跳过章节片头",
|
||||||
|
"LabelSkipChapterOutro": "跳过章节片尾",
|
||||||
"LabelSleepTimer": "睡眠定时",
|
"LabelSleepTimer": "睡眠定时",
|
||||||
"LabelSlug": "Slug",
|
"LabelSlug": "Slug",
|
||||||
"LabelSortAscending": "升序",
|
"LabelSortAscending": "升序",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue