From 65706a52fc71d3fbf3eb0b7c39fef3fa3063e81f Mon Sep 17 00:00:00 2001 From: advplyr Date: Tue, 2 Nov 2021 19:44:42 -0500 Subject: [PATCH] Add: Bookmarks, Fix: Playback rate updating, Fix: Sleep timer countdown, Fix: Prev chapter btn, Change: Loading indicator for new audio player, Fix: UI alignment issues #26 --- Server.js | 7 + android/app/build.gradle | 4 +- components/app/Appbar.vue | 1 + components/app/AudioPlayer.vue | 82 +++++-- components/app/AudioPlayerContainer.vue | 38 ++- components/app/BookshelfListRow.vue | 2 +- components/app/StreamContainer.vue | 2 +- components/cards/BookCard.vue | 2 +- components/modals/BookmarksModal.vue | 137 +++++++++++ components/modals/ChaptersModal.vue | 14 +- components/modals/Modal.vue | 2 +- components/modals/PlaybackSpeedModal.vue | 10 +- components/modals/SleepTimerModal.vue | 2 +- components/modals/bookmarks/BookmarkItem.vue | 43 ++++ components/widgets/SpinnerIcon.vue | 235 +++++++++++++++++++ layouts/default.vue | 13 +- package-lock.json | 7 +- package.json | 5 +- pages/audiobook/_id/index.vue | 2 +- plugins/init.client.js | 11 + plugins/store.js | 8 +- store/audiobooks.js | 2 +- store/user.js | 24 +- 23 files changed, 600 insertions(+), 53 deletions(-) create mode 100644 components/modals/BookmarksModal.vue create mode 100644 components/modals/bookmarks/BookmarkItem.vue create mode 100644 components/widgets/SpinnerIcon.vue diff --git a/Server.js b/Server.js index dd602512..b11ba106 100644 --- a/Server.js +++ b/Server.js @@ -211,6 +211,13 @@ class Server extends EventEmitter { this.emit('currentUserAudiobookUpdate', payload) }) + this.socket.on('show_error_toast', (payload) => { + this.emit('show_error_toast', payload) + }) + this.socket.on('show_success_toast', (payload) => { + this.emit('show_success_toast', payload) + }) + this.socket.onAny((evt, args) => { console.log(`[SOCKET] ${this.socket.id}: ${evt} ${JSON.stringify(args)}`) }) diff --git a/android/app/build.gradle b/android/app/build.gradle index 8545e582..57142363 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "com.audiobookshelf.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 33 - versionName "0.9.16-beta" + versionCode 35 + versionName "0.9.17-beta" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/components/app/Appbar.vue b/components/app/Appbar.vue index ab87d86a..471b29b2 100644 --- a/components/app/Appbar.vue +++ b/components/app/Appbar.vue @@ -17,6 +17,7 @@
+ source diff --git a/components/app/AudioPlayer.vue b/components/app/AudioPlayer.vue index 4156cf2d..72b47144 100644 --- a/components/app/AudioPlayer.vue +++ b/components/app/AudioPlayer.vue @@ -4,8 +4,8 @@
expand_more
-
- close +
+ close
@@ -23,13 +23,13 @@
- bookmark_border - {{ playbackRate }}x + {{ bookmarks.length ? 'bookmark' : 'bookmark_border' }} + {{ currentPlaybackRate }}x
-

-{{ $secondsToTimestamp(Math.floor(sleepTimerEndOfChapterTime / 1000)) }}

+

-{{ $secondsToTimestamp(timeLeftInChapter) }}

{{ Math.ceil(sleepTimeoutCurrentTime / 1000 / 60) }}m

@@ -39,18 +39,19 @@
- first_page - replay_10 + first_page + replay_10
- {{ seekLoading ? 'autorenew' : isPaused ? 'play_arrow' : 'pause' }} + {{ seekLoading ? 'autorenew' : isPaused ? 'play_arrow' : 'pause' }} +
- forward_10 - last_page + forward_10 + last_page
-
+
@@ -80,6 +81,10 @@ export default { type: Object, default: () => {} }, + bookmarks: { + type: Array, + default: () => [] + }, loading: Boolean, sleepTimerRunning: Boolean, sleepTimeoutCurrentTime: Number, @@ -129,6 +134,10 @@ export default { if (!this.audiobook || !this.chapters.length) return null return this.chapters.find((ch) => ch.start <= this.currentTime && ch.end > this.currentTime) }, + nextChapter() { + if (!this.chapters.length) return + return this.chapters.find((c) => c.start >= this.currentTime) + }, currentChapterTitle() { return this.currentChapter ? this.currentChapter.title : '' }, @@ -138,12 +147,9 @@ export default { totalDurationPretty() { return this.$secondsToTimestamp(this.totalDuration) }, - playbackRate() { - return this.$store.getters['user/getUserSetting']('playbackRate') - }, - nextChapter() { - if (!this.chapters.length) return - return this.chapters.find((c) => c.start >= this.currentTime) + timeLeftInChapter() { + if (!this.currentChapter) return 0 + return this.currentChapter.end - this.currentTime } }, methods: { @@ -154,29 +160,44 @@ export default { this.showFullscreen = false }, jumpNextChapter() { + if (this.loading) return if (!this.nextChapter) return this.seek(this.nextChapter.start) }, jumpChapterStart() { + if (this.loading) return if (!this.currentChapter) { return this.restart() } - this.seek(this.currentChapter.start) + + // If 1 second or less into current chapter, then go to previous + if (this.currentTime - this.currentChapter.start <= 1) { + var currChapterIndex = this.chapters.findIndex((ch) => ch.start <= this.currentTime && ch.end >= this.currentTime) + if (currChapterIndex > 0) { + var prevChapter = this.chapters[currChapterIndex - 1] + this.seek(prevChapter.start) + } + } else { + this.seek(this.currentChapter.start) + } }, showSleepTimerModal() { this.$emit('showSleepTimer') }, - updatePlaybackRate() { - this.currentPlaybackRate = this.playbackRate - MyNativeAudio.setPlaybackSpeed({ speed: this.playbackRate }) + setPlaybackSpeed(speed) { + console.log(`[AudioPlayer] Set Playback Rate: ${speed}`) + this.currentPlaybackRate = speed + MyNativeAudio.setPlaybackSpeed({ speed: speed }) }, restart() { this.seek(0) }, backward10() { + if (this.loading) return MyNativeAudio.seekBackward({ amount: '10000' }) }, forward10() { + if (this.loading) return MyNativeAudio.seekForward({ amount: '10000' }) }, sendStreamUpdate() { @@ -242,6 +263,7 @@ export default { this.playedTrackWidth = ptWidth }, seek(time) { + if (this.loading) return if (this.seekLoading) { console.error('Already seek loading', this.seekedTime) return @@ -263,6 +285,7 @@ export default { }, updateVolume(volume) {}, clickTrack(e) { + if (this.loading) return var offsetX = e.offsetX var perc = offsetX / this.trackWidth var time = perc * this.totalDuration @@ -273,6 +296,7 @@ export default { this.seek(time) }, playPauseClick() { + if (this.loading) return if (this.isPaused) { console.log('playPause PLAY') this.play() @@ -331,6 +355,8 @@ export default { } this.currentPlaybackRate = this.initObject.playbackSpeed + console.log(`[AudioPlayer] Set Stream Playback Rate: ${this.currentPlaybackRate}`) + if (init) MyNativeAudio.initPlayer(this.initObject).then((res) => { if (res && res.success) { @@ -559,10 +585,14 @@ export default { } #playerControls .play-btn { transition: all 0.15s cubic-bezier(0.39, 0.575, 0.565, 1); - transition-property: padding, margin; + transition-property: padding, margin, height, width, min-width, min-height; + height: 40px; + width: 40px; + min-width: 40px; + min-height: 40px; margin: 0px 14px; - padding: 8px; + /* padding: 8px; */ } #playerControls .play-btn .material-icons { transition: all 0.15s cubic-bezier(0.39, 0.575, 0.565, 1); @@ -591,7 +621,11 @@ export default { font-size: 2rem; } .fullscreen #playerControls .play-btn { - padding: 16px; + /* padding: 16px; */ + height: 65px; + width: 65px; + min-width: 65px; + min-height: 65px; margin: 0px 26px; } .fullscreen #playerControls .play-btn .material-icons { diff --git a/components/app/AudioPlayerContainer.vue b/components/app/AudioPlayerContainer.vue index 88f81733..adaa545f 100644 --- a/components/app/AudioPlayerContainer.vue +++ b/components/app/AudioPlayerContainer.vue @@ -6,6 +6,7 @@ :audiobook="audiobook" :download="download" :loading="isLoading" + :bookmarks="bookmarks" :sleep-timer-running="isSleepTimerRunning" :sleep-timer-end-of-chapter-time="sleepTimerEndOfChapterTime" :sleep-timeout-current-time="sleepTimeoutCurrentTime" @@ -14,6 +15,7 @@ @selectPlaybackSpeed="showPlaybackSpeedModal = true" @selectChapter="clickChapterBtn" @showSleepTimer="showSleepTimer" + @showBookmarks="showBookmarks" @hook:mounted="audioPlayerMounted" />
@@ -21,6 +23,7 @@ +
@@ -36,6 +39,7 @@ export default { download: null, lastProgressTimeUpdate: 0, showPlaybackSpeedModal: false, + showBookmarksModal: false, showSleepTimerModal: false, playbackSpeed: 1, showChapterModal: false, @@ -60,6 +64,14 @@ export default { userToken() { return this.$store.getters['user/getToken'] }, + userAudiobook() { + if (!this.audiobookId) return + return this.$store.getters['user/getMostRecentUserAudiobookData'](this.audiobookId) + }, + bookmarks() { + if (!this.userAudiobook) return [] + return this.userAudiobook.bookmarks || [] + }, currentChapter() { if (!this.audiobook || !this.chapters.length) return null return this.chapters.find((ch) => ch.start <= this.currentTime && ch.end > this.currentTime) @@ -127,6 +139,17 @@ export default { } }, methods: { + showBookmarks() { + this.showBookmarksModal = true + }, + selectBookmark(bookmark) { + this.showBookmarksModal = false + if (!bookmark || isNaN(bookmark.time)) return + var bookmarkTime = Number(bookmark.time) + if (this.$refs.audioPlayer) { + this.$refs.audioPlayer.seek(bookmarkTime) + } + }, onSleepTimerEnded({ value: currentPosition }) { this.isSleepTimerRunning = false if (this.sleepInterval) clearInterval(this.sleepInterval) @@ -263,7 +286,7 @@ export default { if (this.$server.connected) { this.$server.socket.emit('progress_update', progressUpdate) } - this.$localStore.updateUserAudiobookProgress(progressUpdate).then(() => { + this.$localStore.updateUserAudiobookData(progressUpdate).then(() => { console.log('Updated user audiobook progress', currentTime) }) } @@ -405,12 +428,18 @@ export default { } }, changePlaybackSpeed(speed) { + console.log(`[AudioPlayerContainer] Change Playback Speed: ${speed}`) + if (this.$refs.audioPlayer) { + this.$refs.audioPlayer.setPlaybackSpeed(speed) + } this.$store.dispatch('user/updateUserSettings', { playbackRate: speed }) }, settingsUpdated(settings) { + console.log(`[AudioPlayerContainer] Settings Update | PlaybackRate: ${settings.playbackRate}`) + this.playbackSpeed = settings.playbackRate if (this.$refs.audioPlayer && this.$refs.audioPlayer.currentPlaybackRate !== settings.playbackRate) { - this.playbackSpeed = settings.playbackRate - this.$refs.audioPlayer.updatePlaybackRate() + console.log(`[AudioPlayerContainer] PlaybackRate Updated: ${this.playbackSpeed}`) + this.$refs.audioPlayer.setPlaybackSpeed(this.playbackSpeed) } }, streamUpdated(type, data) { @@ -441,9 +470,11 @@ export default { this.onSleepTimerEndedListener = MyNativeAudio.addListener('onSleepTimerEnded', this.onSleepTimerEnded) this.playbackSpeed = this.$store.getters['user/getUserSetting']('playbackRate') + console.log(`[AudioPlayerContainer] Init Playback Speed: ${this.playbackSpeed}`) this.setListeners() this.$store.commit('user/addSettingsListener', { id: 'streamContainer', meth: this.settingsUpdated }) + // this.$store.commit('user/addUserAudiobookListener', { id: 'streamContainer', meth: this.userAudiobooksUpdated }) this.$store.commit('setStreamListener', this.streamUpdated) }, beforeDestroy() { @@ -458,6 +489,7 @@ export default { } this.$store.commit('user/removeSettingsListener', 'streamContainer') + // this.$store.commit('user/removeUserAudiobookListener', 'streamContainer') this.$store.commit('removeStreamListener') } } diff --git a/components/app/BookshelfListRow.vue b/components/app/BookshelfListRow.vue index 84b76f27..e33bad61 100644 --- a/components/app/BookshelfListRow.vue +++ b/components/app/BookshelfListRow.vue @@ -70,7 +70,7 @@ export default { return this.audiobook.id }, mostRecentUserProgress() { - return this.$store.getters['user/getMostRecentAudiobookProgress'](this.audiobookId) + return this.$store.getters['user/getMostRecentUserAudiobookData'](this.audiobookId) }, userProgressPercent() { return this.mostRecentUserProgress ? this.mostRecentUserProgress.progress || 0 : 0 diff --git a/components/app/StreamContainer.vue b/components/app/StreamContainer.vue index b6129a80..cf572bb5 100644 --- a/components/app/StreamContainer.vue +++ b/components/app/StreamContainer.vue @@ -263,7 +263,7 @@ export default { if (this.$server.connected) { this.$server.socket.emit('progress_update', progressUpdate) } - this.$localStore.updateUserAudiobookProgress(progressUpdate).then(() => { + this.$localStore.updateUserAudiobookData(progressUpdate).then(() => { console.log('Updated user audiobook progress', currentTime) }) } diff --git a/components/cards/BookCard.vue b/components/cards/BookCard.vue index 675aecc0..b6bd8dc5 100644 --- a/components/cards/BookCard.vue +++ b/components/cards/BookCard.vue @@ -82,7 +82,7 @@ export default { return this.$store.getters['user/getUserSetting']('mobileOrderBy') }, mostRecentUserProgress() { - return this.$store.getters['user/getMostRecentAudiobookProgress'](this.audiobookId) + return this.$store.getters['user/getMostRecentUserAudiobookData'](this.audiobookId) }, userProgressPercent() { return this.mostRecentUserProgress ? this.mostRecentUserProgress.progress || 0 : 0 diff --git a/components/modals/BookmarksModal.vue b/components/modals/BookmarksModal.vue new file mode 100644 index 00000000..48db74d4 --- /dev/null +++ b/components/modals/BookmarksModal.vue @@ -0,0 +1,137 @@ + + + diff --git a/components/modals/ChaptersModal.vue b/components/modals/ChaptersModal.vue index 04560aa3..18ec54d6 100644 --- a/components/modals/ChaptersModal.vue +++ b/components/modals/ChaptersModal.vue @@ -1,7 +1,7 @@