diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt b/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt index cc2ba1f6..f7e3c89a 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt @@ -86,7 +86,8 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi } override fun onSeekTo(pos: Long) { - playerNotificationService.seekPlayer(pos) + val currentTrackStartOffset = playerNotificationService.getCurrentTrackStartOffsetMs() + playerNotificationService.seekPlayer(currentTrackStartOffset + pos) } private fun onChangeSpeed() { diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt index 07a2373a..16e0f19b 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt @@ -652,16 +652,20 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { } } - fun getCurrentTime() : Long { + fun getCurrentTrackStartOffsetMs() : Long { return if (currentPlayer.mediaItemCount > 1) { val windowIndex = currentPlayer.currentMediaItemIndex val currentTrackStartOffset = currentPlaybackSession?.getTrackStartOffsetMs(windowIndex) ?: 0L - currentPlayer.currentPosition + currentTrackStartOffset + currentTrackStartOffset } else { - currentPlayer.currentPosition + 0 } } + fun getCurrentTime() : Long { + return currentPlayer.currentPosition + getCurrentTrackStartOffsetMs() + } + fun getCurrentTimeSeconds() : Double { return getCurrentTime() / 1000.0 } diff --git a/components/bookshelf/LazyBookshelf.vue b/components/bookshelf/LazyBookshelf.vue index 5c2cd16a..4b082246 100644 --- a/components/bookshelf/LazyBookshelf.vue +++ b/components/bookshelf/LazyBookshelf.vue @@ -367,8 +367,14 @@ export default { this.handleScroll(scrollTop) }, buildSearchParams() { - if (this.page === 'search' || this.page === 'series' || this.page === 'collections') { + if (this.page === 'search' || this.page === 'collections') { return '' + } else if (this.page === 'series') { + // Sort by name ascending + let searchParams = new URLSearchParams() + searchParams.set('sort', 'name') + searchParams.set('desc', 0) + return searchParams.toString() } let searchParams = new URLSearchParams() diff --git a/components/connection/ServerConnectForm.vue b/components/connection/ServerConnectForm.vue index 85e55992..13599e9d 100644 --- a/components/connection/ServerConnectForm.vue +++ b/components/connection/ServerConnectForm.vue @@ -688,12 +688,13 @@ export default { this.setUserAndConnection(payload) } }, - async setUserAndConnection({ user, userDefaultLibraryId, serverSettings }) { + async setUserAndConnection({ user, userDefaultLibraryId, serverSettings, ereaderDevices }) { if (!user) return console.log('Successfully logged in', JSON.stringify(user)) this.$store.commit('setServerSettings', serverSettings) + this.$store.commit('libraries/setEReaderDevices', ereaderDevices) // Set library - Use last library if set and available fallback to default user library var lastLibraryId = await this.$localStore.getLastLibraryId() diff --git a/components/modals/BookmarksModal.vue b/components/modals/BookmarksModal.vue index 1c4e0615..e481c1e7 100644 --- a/components/modals/BookmarksModal.vue +++ b/components/modals/BookmarksModal.vue @@ -105,30 +105,28 @@ export default { this.$nativeHttp .delete(`/api/me/item/${this.libraryItemId}/bookmark/${bm.time}`) .then(() => { - this.$toast.success('Bookmark removed') + this.$store.commit('user/deleteBookmark', { libraryItemId: this.libraryItemId, time: bm.time }) }) .catch((error) => { this.$toast.error(`Failed to remove bookmark`) console.error(error) }) - this.show = false }, async clickBookmark(bm) { await this.$hapticsImpact() this.$emit('select', bm) }, submitUpdateBookmark(updatedBookmark) { - var bookmark = { ...updatedBookmark } this.$nativeHttp - .patch(`/api/me/item/${this.libraryItemId}/bookmark`, bookmark) - .then(() => { - this.$toast.success('Bookmark updated') + .patch(`/api/me/item/${this.libraryItemId}/bookmark`, updatedBookmark) + .then((bookmark) => { + this.$store.commit('user/updateBookmark', bookmark) + this.showBookmarkTitleInput = false }) .catch((error) => { this.$toast.error(`Failed to update bookmark`) console.error(error) }) - this.show = false }, submitCreateBookmark() { if (!this.newBookmarkTitle) { diff --git a/components/modals/Dialog.vue b/components/modals/Dialog.vue index 9fa46c18..9086e3a2 100644 --- a/components/modals/Dialog.vue +++ b/components/modals/Dialog.vue @@ -1,7 +1,7 @@ @@ -175,11 +177,9 @@ export default { }, data() { return { - resettingProgress: false, - isProcessingReadUpdate: false, + processing: false, showSelectLocalFolder: false, showMoreMenu: false, - showDetailsModal: false, showFullscreenCover: false, coverRgb: 'rgb(55, 56, 56)', coverBgIsLight: false, @@ -311,7 +311,7 @@ export default { return this.$store.getters['user/getUserMediaProgress'](this.serverLibraryItemId) }, userIsFinished() { - return this.userItemProgress ? !!this.userItemProgress.isFinished : false + return !!this.userItemProgress?.isFinished }, userTimeRemaining() { if (!this.userItemProgress) return 0 @@ -324,10 +324,10 @@ export default { }, progressPercent() { if (this.useEBookProgress) return Math.max(Math.min(1, this.userItemProgress.ebookProgress), 0) - return this.userItemProgress ? Math.max(Math.min(1, this.userItemProgress.progress), 0) : 0 + return Math.max(Math.min(1, this.userItemProgress?.progress || 0), 0) }, userProgressFinishedAt() { - return this.userItemProgress ? this.userItemProgress.finishedAt : 0 + return this.userItemProgress?.finishedAt || 0 }, isStreaming() { return this.isPlaying && !this.$store.getters['getIsCurrentSessionLocal'] @@ -387,83 +387,6 @@ export default { isCasting() { return this.$store.state.isCasting }, - showRSSFeedOption() { - if (!this.serverLibraryItemId) return false - if (!this.rssFeed && !this.episodes.length && !this.tracks.length) return false // Cannot open RSS feed with no episodes/tracks - - // If rss feed is open then show feed url to users otherwise just show to admins - return this.userIsAdminOrUp || this.rssFeed - }, - moreMenuItems() { - const items = [] - - if (!this.isPodcast) { - // TODO: Implement on iOS - if (!this.isIos) { - items.push({ - text: 'History', - value: 'history', - icon: 'history' - }) - } - - if (!this.userIsFinished) { - items.push({ - text: 'Mark as Finished', - value: 'markFinished', - icon: 'beenhere' - }) - } - - if (this.progressPercent > 0) { - items.push({ - text: 'Discard Progress', - value: 'discardProgress', - icon: 'backspace' - }) - } - } - - if (!this.isPodcast && this.serverLibraryItemId) { - items.push({ - text: 'Add to Playlist', - value: 'playlist', - icon: 'playlist_add' - }) - } - - if (this.showRSSFeedOption) { - items.push({ - text: this.rssFeed ? 'RSS Feed' : 'Open RSS Feed', - value: 'rssFeed', - icon: 'rss_feed' - }) - } - - if (this.localLibraryItemId) { - items.push({ - text: 'Manage Local Files', - value: 'manageLocal', - icon: 'folder' - }) - - if (!this.isPodcast) { - items.push({ - text: 'Delete Local Item', - value: 'deleteLocal', - icon: 'delete' - }) - } - } - - items.push({ - text: 'More Info', - value: 'details', - icon: 'info' - }) - - return items - }, coverWidth() { let width = this.windowWidth - 94 if (width > 325) return 325 @@ -471,42 +394,9 @@ export default { if (width * this.bookCoverAspectRatio > 325) width = 325 / this.bookCoverAspectRatio return width - }, - mediaId() { - if (this.isPodcast) return null - return this.serverLibraryItemId || this.localLibraryItemId } }, methods: { - async deleteLocalItem() { - await this.$hapticsImpact() - - let confirmMessage = 'Remove local files of this item from your device?' - if (this.serverLibraryItemId) { - confirmMessage += ' The files on the server and your progress will be unaffected.' - } - const { value } = await Dialog.confirm({ - title: 'Confirm', - message: confirmMessage - }) - if (value) { - const res = await AbsFileSystem.deleteItem(this.localLibraryItem) - if (res?.success) { - this.$toast.success('Deleted successfully') - if (this.isLocal) { - // If local then redirect to server version when available - if (this.serverLibraryItemId) { - this.$router.replace(`/item/${this.serverLibraryItemId}`) - } else { - this.$router.replace('/bookshelf') - } - } else { - // Remove localLibraryItem - this.$delete(this.libraryItem, 'localLibraryItem') - } - } else this.$toast.error('Failed to delete') - } - }, async coverImageLoaded(fullCoverUrl) { if (!fullCoverUrl) return @@ -521,39 +411,6 @@ export default { console.log(e) }) }, - moreMenuAction(action) { - this.showMoreMenu = false - if (action === 'manageLocal') { - this.$nextTick(() => { - this.$router.push(`/localMedia/item/${this.localLibraryItemId}`) - }) - } else if (action === 'details') { - this.showDetailsModal = true - } else if (action === 'playlist') { - this.$store.commit('globals/setSelectedPlaylistItems', [{ libraryItem: this.libraryItem, episode: null }]) - this.$store.commit('globals/setShowPlaylistsAddCreateModal', true) - } else if (action === 'markFinished') { - if (this.isProcessingReadUpdate) return - this.toggleFinished() - } else if (action === 'history') { - this.$router.push(`/media/${this.mediaId}/history?title=${this.title}`) - } else if (action === 'discardProgress') { - this.clearProgressClick() - } else if (action === 'deleteLocal') { - this.deleteLocalItem() - } else if (action === 'rssFeed') { - this.clickRSSFeed() - } - }, - clickRSSFeed() { - this.$store.commit('globals/setRSSFeedOpenCloseModal', { - id: this.serverLibraryItemId, - name: this.title, - type: 'item', - feed: this.rssFeed, - hasEpisodesWithoutPubDate: this.episodes.some((ep) => !ep.pubDate) - }) - }, moreButtonPress() { this.showMoreMenu = true }, @@ -635,37 +492,6 @@ export default { this.$eventBus.$emit('play-item', { libraryItemId, serverLibraryItemId: this.serverLibraryItemId, startTime }) } }, - async clearProgressClick() { - await this.$hapticsImpact() - - const { value } = await Dialog.confirm({ - title: 'Confirm', - message: 'Are you sure you want to reset your progress?' - }) - if (value) { - this.resettingProgress = true - const serverMediaProgressId = this.serverItemProgress?.id - if (this.localLibraryItemId) { - await this.$db.removeLocalMediaProgress(this.localLibraryItemId) - this.$store.commit('globals/removeLocalMediaProgressForItem', this.localLibraryItemId) - } - - if (this.serverLibraryItemId && serverMediaProgressId) { - await this.$nativeHttp - .delete(`/api/me/progress/${serverMediaProgressId}`) - .then(() => { - console.log('Progress reset complete') - this.$toast.success(`Your progress was reset`) - this.$store.commit('user/removeMediaProgress', serverMediaProgressId) - }) - .catch((error) => { - console.error('Progress reset failed', error) - }) - } - - this.resettingProgress = false - } - }, itemUpdated(libraryItem) { if (libraryItem.id === this.serverLibraryItemId) { console.log('Item Updated') @@ -767,48 +593,6 @@ export default { this.$set(this.libraryItem, 'localLibraryItem', item) } }, - async toggleFinished() { - await this.$hapticsImpact() - - // Show confirm if item has progress since it will reset - if (this.userItemProgress && this.userItemProgress.progress > 0 && !this.userIsFinished) { - const { value } = await Dialog.confirm({ - title: 'Confirm', - message: 'Are you sure you want to mark this item as Finished?' - }) - if (!value) return - } - - this.isProcessingReadUpdate = true - if (this.isLocal) { - const isFinished = !this.userIsFinished - const payload = await this.$db.updateLocalMediaProgressFinished({ localLibraryItemId: this.localLibraryItemId, isFinished }) - console.log('toggleFinished payload', JSON.stringify(payload)) - if (payload?.error) { - this.$toast.error(payload?.error || 'Unknown error') - } else { - const localMediaProgress = payload.localMediaProgress - console.log('toggleFinished localMediaProgress', JSON.stringify(localMediaProgress)) - if (localMediaProgress) { - this.$store.commit('globals/updateLocalMediaProgress', localMediaProgress) - } - } - this.isProcessingReadUpdate = false - } else { - const updatePayload = { - isFinished: !this.userIsFinished - } - this.$nativeHttp - .patch(`/api/me/progress/${this.libraryItemId}`, updatePayload) - .catch((error) => { - console.error('Failed', error) - this.$toast.error(`Failed to mark as ${updatePayload.isFinished ? 'Finished' : 'Not Finished'}`) - }) - .finally(() => { - this.isProcessingReadUpdate = false - }) - } - }, libraryChanged(libraryId) { if (this.libraryItem.libraryId !== libraryId) { this.$router.replace('/bookshelf') diff --git a/pages/playlist/_id.vue b/pages/playlist/_id.vue index ac076773..a2278dd6 100644 --- a/pages/playlist/_id.vue +++ b/pages/playlist/_id.vue @@ -1,11 +1,11 @@ @@ -67,7 +72,12 @@ export default { } }, data() { - return {} + return { + showMoreMenu: false, + processing: false, + selectedLibraryItem: null, + selectedEpisode: null + } }, computed: { bookCoverAspectRatio() { @@ -101,6 +111,11 @@ export default { } }, methods: { + showMore(playlistItem) { + this.selectedLibraryItem = playlistItem.libraryItem + this.selectedEpisode = playlistItem.episode + this.showMoreMenu = true + }, clickPlay() { const nextItem = this.playableItems.find((i) => { const prog = this.$store.getters['user/getUserMediaProgress'](i.libraryItemId, i.episodeId) diff --git a/store/libraries.js b/store/libraries.js index 9582653e..602c8e1c 100644 --- a/store/libraries.js +++ b/store/libraries.js @@ -7,7 +7,8 @@ export const state = () => ({ showModal: false, issues: 0, filterData: null, - numUserPlaylists: 0 + numUserPlaylists: 0, + ereaderDevices: [] }) export const getters = { @@ -177,5 +178,8 @@ export const mutations = { if (genre && !state.filterData.genres.includes(genre)) state.filterData.genres.push(genre) }) } + }, + setEReaderDevices(state, ereaderDevices) { + state.ereaderDevices = ereaderDevices } } \ No newline at end of file diff --git a/store/user.js b/store/user.js index a4837a0f..aa69588e 100644 --- a/store/user.js +++ b/store/user.js @@ -145,5 +145,21 @@ export const mutations = { setSettings(state, settings) { if (!settings) return state.settings = settings + }, + updateBookmark(state, bookmark) { + if (!state.user?.bookmarks) return + state.user.bookmarks = state.user.bookmarks.map((bm) => { + if (bm.libraryItemId === bookmark.libraryItemId && bm.time === bookmark.time) { + return bookmark + } + return bm + }) + }, + deleteBookmark(state, { libraryItemId, time }) { + if (!state.user?.bookmarks) return + state.user.bookmarks = state.user.bookmarks.filter(bm => { + if (bm.libraryItemId === libraryItemId && bm.time === time) return false + return true + }) } } \ No newline at end of file