diff --git a/client/assets/fonts.css b/client/assets/fonts.css index 61d5f572..874570ac 100644 --- a/client/assets/fonts.css +++ b/client/assets/fonts.css @@ -20,7 +20,7 @@ -webkit-font-feature-settings: 'liga'; -webkit-font-smoothing: antialiased; } -.material-icons:not(.text-sm):not(.text-md):not(.text-base):not(.text-lg):not(.text-xl):not(.text-2xl):not(.text-3xl):not(.text-4xl):not(.text-5xl):not(.text-6xl) { +.material-icons:not(.text-xs):not(.text-sm):not(.text-md):not(.text-base):not(.text-lg):not(.text-xl):not(.text-2xl):not(.text-3xl):not(.text-4xl):not(.text-5xl):not(.text-6xl) { font-size: 1.5rem; } diff --git a/client/components/AudioPlayer.vue b/client/components/AudioPlayer.vue index 32692a55..6988fe33 100644 --- a/client/components/AudioPlayer.vue +++ b/client/components/AudioPlayer.vue @@ -15,7 +15,7 @@
- {{ bookmarks.length ? 'bookmarks' : 'bookmark_border' }} + {{ bookmarks.length ? 'bookmarks' : 'bookmark_border' }}
diff --git a/client/components/app/StreamContainer.vue b/client/components/app/StreamContainer.vue index 8c2c58ff..b63fa36d 100644 --- a/client/components/app/StreamContainer.vue +++ b/client/components/app/StreamContainer.vue @@ -24,7 +24,7 @@ - +
@@ -38,8 +38,7 @@ export default { totalDuration: 0, showBookmarksModal: false, bookmarkCurrentTime: 0, - bookmarkAudiobookId: null, - bookmarkTimeCreating: 0 + bookmarkAudiobookId: null } }, computed: { @@ -103,24 +102,38 @@ export default { this.bookmarkCurrentTime = currentTime this.showBookmarksModal = true }, - bookmarkCreated(time) { - if (time === this.bookmarkTimeCreating) { - this.bookmarkTimeCreating = 0 - this.$toast.success(`${this.$secondsToTimestamp(time)} Bookmarked`) - } - }, + // bookmarkCreated(time) { + // if (time === this.bookmarkTimeProcessing) { + // this.bookmarkTimeProcessing = 0 + // this.$toast.success(`${this.$secondsToTimestamp(time)} Bookmarked`) + // } + // }, createBookmark(bookmark) { - this.bookmarkTimeCreating = bookmark.time - this.$root.socket.once('bookmark_created', this.bookmarkCreated) + // this.bookmarkTimeProcessing = bookmark.time this.$root.socket.emit('create_bookmark', bookmark) this.showBookmarksModal = false }, + // bookmarkUpdated(time) { + // if (time === this.bookmarkTimeProcessing) { + // this.bookmarkTimeProcessing = 0 + // this.$toast.success(`Bookmark @${this.$secondsToTimestamp(time)} Updated`) + // } + // }, + updateBookmark(bookmark) { + // this.bookmarkTimeProcessing = bookmark.time + this.$root.socket.emit('update_bookmark', bookmark) + this.showBookmarksModal = false + }, selectBookmark(bookmark) { if (this.$refs.audioPlayer) { this.$refs.audioPlayer.selectBookmark(bookmark) } this.showBookmarksModal = false }, + deleteBookmark(bookmark) { + this.$root.socket.emit('delete_bookmark', bookmark) + this.showBookmarksModal = false + }, filterByAuthor() { if (this.$route.name !== 'index') { this.$router.push(`/library/${this.libraryId || this.$store.state.libraries.currentLibraryId}/bookshelf`) diff --git a/client/components/modals/BookmarksModal.vue b/client/components/modals/BookmarksModal.vue index 89d9d586..b934281f 100644 --- a/client/components/modals/BookmarksModal.vue +++ b/client/components/modals/BookmarksModal.vue @@ -6,7 +6,7 @@
arrow_back
-

New Bookmark

+

{{ selectedBookmark ? 'Edit Bookmark' : 'New Bookmark' }}

{{ this.$secondsToTimestamp(currentTime) }} @@ -15,25 +15,18 @@

- Create Bookmark + {{ selectedBookmark ? 'Update' : 'Create' }} Bookmark

No Bookmarks

-
+
add

Create Bookmark

@@ -61,6 +54,7 @@ export default { }, data() { return { + selectedBookmark: null, showBookmarkTitleInput: false, newBookmarkTitle: '' } @@ -81,22 +75,45 @@ export default { set(val) { this.$emit('input', val) } + }, + canCreateBookmark() { + return !this.bookmarks.find((bm) => bm.time === this.currentTime) } }, methods: { + editBookmark(bm) { + this.selectedBookmark = bm + this.newBookmarkTitle = bm.title + this.showBookmarkTitleInput = true + }, + deleteBookmark(bm) { + var bookmark = { ...bm, audiobookId: this.audiobookId } + this.$emit('delete', bookmark) + }, clickBookmark(bm) { this.$emit('select', bm) }, createBookmark() { + this.selectedBookmark = null + this.newBookmarkTitle = this.$formatDate(Date.now(), 'MMM dd, yyyy HH:mm') this.showBookmarkTitleInput = true }, submitBookmark() { - var bookmark = { - audiobookId: this.audiobookId, - title: this.newBookmarkTitle, - time: this.currentTime + if (this.selectedBookmark) { + if (this.selectedBookmark.title !== this.newBookmarkTitle) { + var bookmark = { ...this.selectedBookmark } + bookmark.audiobookId = this.audiobookId + bookmark.title = this.newBookmarkTitle + this.$emit('update', bookmark) + } + } else { + var bookmark = { + audiobookId: this.audiobookId, + title: this.newBookmarkTitle, + time: this.currentTime + } + this.$emit('create', bookmark) } - this.$emit('create', bookmark) this.newBookmarkTitle = '' this.showBookmarkTitleInput = false } diff --git a/client/components/modals/bookmarks/BookmarkItem.vue b/client/components/modals/bookmarks/BookmarkItem.vue new file mode 100644 index 00000000..032ed7f7 --- /dev/null +++ b/client/components/modals/bookmarks/BookmarkItem.vue @@ -0,0 +1,45 @@ + + + \ No newline at end of file diff --git a/client/layouts/default.vue b/client/layouts/default.vue index 15f30ffa..3d1ebddb 100644 --- a/client/layouts/default.vue +++ b/client/layouts/default.vue @@ -239,6 +239,12 @@ export default { download.status = this.$constants.DownloadStatus.EXPIRED this.$store.commit('downloads/addUpdateDownload', download) }, + showErrorToast(message) { + this.$toast.error(message) + }, + showSuccessToast(message) { + this.$toast.success(message) + }, logEvtReceived(payload) { this.$store.commit('logs/logEvt', payload) }, @@ -303,6 +309,10 @@ export default { this.socket.on('download_killed', this.downloadKilled) this.socket.on('download_expired', this.downloadExpired) + // Toast Listeners + this.socket.on('show_error_toast', this.showErrorToast) + this.socket.on('show_success_toast', this.showSuccessToast) + this.socket.on('log', this.logEvtReceived) this.socket.on('backup_applied', this.backupApplied) diff --git a/server/Server.js b/server/Server.js index 59518e55..b983f0b3 100644 --- a/server/Server.js +++ b/server/Server.js @@ -11,6 +11,7 @@ const { version } = require('../package.json') // Utils const { ScanResult } = require('./utils/constants') const filePerms = require('./utils/filePerms') +const { secondsToTimestamp } = require('./utils/fileUtils') const Logger = require('./Logger') // Classes @@ -261,6 +262,8 @@ class Server { // Bookmarks socket.on('create_bookmark', (payload) => this.createBookmark(socket, payload)) + socket.on('update_bookmark', (payload) => this.updateBookmark(socket, payload)) + socket.on('delete_bookmark', (payload) => this.deleteBookmark(socket, payload)) socket.on('test', () => { socket.emit('test_received', socket.id) @@ -481,15 +484,66 @@ class Server { return } var userAudiobook = client.user.createBookmark(payload) - if (userAudiobook) { - await this.db.updateEntity('user', client.user) - - this.clientEmitter(client.user.id, 'bookmark_created', payload.time) - this.clientEmitter(client.user.id, 'current_user_audiobook_update', { - id: userAudiobook.audiobookId, - data: userAudiobook || null - }) + if (!userAudiobook || userAudiobook.error) { + var failMessage = (userAudiobook ? userAudiobook.error : null) || 'Unknown Error' + socket.emit('show_error_toast', `Failed to create Bookmark: ${failMessage}`) + return } + + await this.db.updateEntity('user', client.user) + + socket.emit('show_success_toast', `${secondsToTimestamp(payload.time)} Bookmarked`) + + this.clientEmitter(client.user.id, 'current_user_audiobook_update', { + id: userAudiobook.audiobookId, + data: userAudiobook || null + }) + } + + async updateBookmark(socket, payload) { + var client = socket.sheepClient + if (!client || !client.user) { + Logger.error('[Server] updateBookmark invalid socket client') + return + } + var userAudiobook = client.user.updateBookmark(payload) + if (!userAudiobook || userAudiobook.error) { + var failMessage = (userAudiobook ? userAudiobook.error : null) || 'Unknown Error' + socket.emit('show_error_toast', `Failed to update Bookmark: ${failMessage}`) + return + } + + await this.db.updateEntity('user', client.user) + + socket.emit('show_success_toast', `Bookmark ${secondsToTimestamp(payload.time)} Updated`) + + this.clientEmitter(client.user.id, 'current_user_audiobook_update', { + id: userAudiobook.audiobookId, + data: userAudiobook || null + }) + } + + async deleteBookmark(socket, payload) { + var client = socket.sheepClient + if (!client || !client.user) { + Logger.error('[Server] deleteBookmark invalid socket client') + return + } + var userAudiobook = client.user.deleteBookmark(payload) + if (!userAudiobook || userAudiobook.error) { + var failMessage = (userAudiobook ? userAudiobook.error : null) || 'Unknown Error' + socket.emit('show_error_toast', `Failed to delete Bookmark: ${failMessage}`) + return + } + + await this.db.updateEntity('user', client.user) + + socket.emit('show_success_toast', `Bookmark ${secondsToTimestamp(payload.time)} Removed`) + + this.clientEmitter(client.user.id, 'current_user_audiobook_update', { + id: userAudiobook.audiobookId, + data: userAudiobook || null + }) } async authenticateSocket(socket, token) { diff --git a/server/objects/AudiobookProgress.js b/server/objects/AudiobookProgress.js index 23f24814..a9ed85f0 100644 --- a/server/objects/AudiobookProgress.js +++ b/server/objects/AudiobookProgress.js @@ -107,11 +107,26 @@ class AudiobookProgress { return hasUpdates } + checkBookmarkExists(time) { + return this.bookmarks.find(bm => bm.time === time) + } + createBookmark(time, title) { var newBookmark = new AudioBookmark() newBookmark.setData(time, title) this.bookmarks.push(newBookmark) return newBookmark } + + updateBookmark(time, title) { + var bookmark = this.bookmarks.find(bm => bm.time === time) + if (!bookmark) return false + bookmark.title = title + return bookmark + } + + deleteBookmark(time) { + this.bookmarks = this.bookmarks.filter(bm => bm.time !== time) + } } module.exports = AudiobookProgress \ No newline at end of file diff --git a/server/objects/User.js b/server/objects/User.js index 0e8b459d..deb56d4a 100644 --- a/server/objects/User.js +++ b/server/objects/User.js @@ -281,11 +281,52 @@ class User { createBookmark({ audiobookId, time, title }) { if (!this.audiobooks || !this.audiobooks[audiobookId]) { - return false + return { + error: 'Invalid Audiobook' + } } + if (this.audiobooks[audiobookId].checkBookmarkExists(time)) { + return { + error: 'Bookmark already exists' + } + } + var success = this.audiobooks[audiobookId].createBookmark(time, title) if (success) return this.audiobooks[audiobookId] return null } + + updateBookmark({ audiobookId, time, title }) { + if (!this.audiobooks || !this.audiobooks[audiobookId]) { + return { + error: 'Invalid Audiobook' + } + } + if (!this.audiobooks[audiobookId].checkBookmarkExists(time)) { + return { + error: 'Bookmark does not exist' + } + } + + var success = this.audiobooks[audiobookId].updateBookmark(time, title) + if (success) return this.audiobooks[audiobookId] + return null + } + + deleteBookmark({ audiobookId, time }) { + if (!this.audiobooks || !this.audiobooks[audiobookId]) { + return { + error: 'Invalid Audiobook' + } + } + if (!this.audiobooks[audiobookId].checkBookmarkExists(time)) { + return { + error: 'Bookmark does not exist' + } + } + + this.audiobooks[audiobookId].deleteBookmark(time) + return this.audiobooks[audiobookId] + } } module.exports = User \ No newline at end of file diff --git a/server/utils/fileUtils.js b/server/utils/fileUtils.js index 4d4750d7..098c4992 100644 --- a/server/utils/fileUtils.js +++ b/server/utils/fileUtils.js @@ -69,7 +69,7 @@ function secondsToTimestamp(seconds) { _seconds -= _minutes * 60 var _hours = Math.floor(_minutes / 60) _minutes -= _hours * 60 - _seconds = Math.round(_seconds) + _seconds = Math.floor(_seconds) if (!_hours) { return `${_minutes}:${_seconds.toString().padStart(2, '0')}` }