From 00eeeb2a5add899441a94353b8f896bef1ada957 Mon Sep 17 00:00:00 2001 From: Marcos Carvalho Date: Sun, 19 May 2024 10:40:04 +0100 Subject: [PATCH 1/3] Add detectable timestamp --- pages/item/_id/_episode/index.vue | 63 ++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/pages/item/_id/_episode/index.vue b/pages/item/_id/_episode/index.vue index d178dab1..1e915bc3 100644 --- a/pages/item/_id/_episode/index.vue +++ b/pages/item/_id/_episode/index.vue @@ -41,7 +41,7 @@ -

+

@@ -116,6 +116,9 @@ export default { } }, computed: { + transformedDescription() { + return this.parseDescription(this.description) + }, bookCoverAspectRatio() { return this.$store.getters['libraries/getBookCoverAspectRatio'] }, @@ -310,6 +313,58 @@ export default { } }, methods: { + parseDescription(description) { + const timeMarkerRegex = /\d{1,2}:\d{1,2}(:\d{1,2})?/g + + return description.replace(timeMarkerRegex, (match) => { + const time = match.replace(/[()]/g, '') + const timeParts = time.split(':').map(Number) + let seekTimeInSeconds + + if (timeParts.length === 2) { + const [minutes, seconds] = timeParts + seekTimeInSeconds = minutes * 60 + seconds + } else { + const [hours, minutes, seconds] = timeParts + seekTimeInSeconds = hours * 3600 + minutes * 60 + seconds + } + + return `${match}` + }) + }, + clickPlaybackTime(event) { + const startTime = event.target.getAttribute('data-time') + if (this.playerIsStartingPlayback) return + + this.$hapticsImpact() + + this.$store.commit('setPlayerIsStartingPlayback', this.episode.id) + + const playbackData = { + libraryItemId: this.libraryItemId, + episodeId: this.episode.id, + serverLibraryItemId: this.serverLibraryItemId, + serverEpisodeId: this.serverEpisodeId, + startTime + } + + if (this.localEpisodeId && this.localLibraryItemId && !this.isLocal) { + playbackData.libraryItemId = this.localLibraryItemId + playbackData.episodeId = this.localEpisodeId + } + + this.$eventBus.$emit('play-item', playbackData) + }, + bindTimeMarkerEvents() { + const container = document.querySelector('.description-container') + if (container) { + container.addEventListener('click', (event) => { + if (event.target.classList.contains('time-marker')) { + this.clickPlaybackTime(event) + } + }) + } + }, async deleteLocalEpisode() { await this.$hapticsImpact() @@ -557,9 +612,13 @@ export default { }, mounted() { this.$eventBus.$on('new-local-library-item', this.newLocalLibraryItem) + this.bindTimeMarkerEvents() }, beforeDestroy() { this.$eventBus.$off('new-local-library-item', this.newLocalLibraryItem) + document.querySelectorAll('.time-marker').forEach(marker => { + marker.removeEventListener('click', this.clickPlaybackTime) + }) } } - \ No newline at end of file + From b833019a1417c6c902001e14e64af3b4b948e662 Mon Sep 17 00:00:00 2001 From: Marcos Carvalho Date: Sun, 19 May 2024 14:14:47 +0100 Subject: [PATCH 2/3] Change href link to span, to avoid change the page and improve code --- pages/item/_id/_episode/index.vue | 92 ++++++++++++++----------------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/pages/item/_id/_episode/index.vue b/pages/item/_id/_episode/index.vue index 1e915bc3..d0ccd69d 100644 --- a/pages/item/_id/_episode/index.vue +++ b/pages/item/_id/_episode/index.vue @@ -329,42 +329,9 @@ export default { seekTimeInSeconds = hours * 3600 + minutes * 60 + seconds } - return `${match}` + return `${match}` }) }, - clickPlaybackTime(event) { - const startTime = event.target.getAttribute('data-time') - if (this.playerIsStartingPlayback) return - - this.$hapticsImpact() - - this.$store.commit('setPlayerIsStartingPlayback', this.episode.id) - - const playbackData = { - libraryItemId: this.libraryItemId, - episodeId: this.episode.id, - serverLibraryItemId: this.serverLibraryItemId, - serverEpisodeId: this.serverEpisodeId, - startTime - } - - if (this.localEpisodeId && this.localLibraryItemId && !this.isLocal) { - playbackData.libraryItemId = this.localLibraryItemId - playbackData.episodeId = this.localEpisodeId - } - - this.$eventBus.$emit('play-item', playbackData) - }, - bindTimeMarkerEvents() { - const container = document.querySelector('.description-container') - if (container) { - container.addEventListener('click', (event) => { - if (event.target.classList.contains('time-marker')) { - this.clickPlaybackTime(event) - } - }) - } - }, async deleteLocalEpisode() { await this.$hapticsImpact() @@ -406,23 +373,48 @@ export default { } else { this.$store.commit('setPlayerIsStartingPlayback', this.episode.id) - if (this.localEpisodeId && this.localLibraryItemId && !this.isLocal) { - console.log('Play local episode', this.localEpisodeId, this.localLibraryItemId) + const playbackData = this.generatePlaybackData() + this.emitPlayItemEvent(playbackData) + } + }, + async clickPlaybackTime(event) { + const startTime = event.target.getAttribute('data-time') + if (this.playerIsStartingPlayback) return - this.$eventBus.$emit('play-item', { - libraryItemId: this.localLibraryItemId, - episodeId: this.localEpisodeId, - serverLibraryItemId: this.serverLibraryItemId, - serverEpisodeId: this.serverEpisodeId - }) - } else { - this.$eventBus.$emit('play-item', { - libraryItemId: this.libraryItemId, - episodeId: this.episode.id, - serverLibraryItemId: this.serverLibraryItemId, - serverEpisodeId: this.serverEpisodeId - }) - } + await this.$hapticsImpact() + + this.$store.commit('setPlayerIsStartingPlayback', this.episode.id) + + const playbackData = this.generatePlaybackData(startTime) + this.emitPlayItemEvent(playbackData) + }, + generatePlaybackData(startTime) { + const playbackData = { + libraryItemId: this.libraryItemId, + episodeId: this.episode.id, + serverLibraryItemId: this.serverLibraryItemId, + serverEpisodeId: this.serverEpisodeId, + startTime + } + + if (this.localEpisodeId && this.localLibraryItemId && !this.isLocal) { + playbackData.libraryItemId = this.localLibraryItemId + playbackData.episodeId = this.localEpisodeId + } + + return playbackData + }, + emitPlayItemEvent(playbackData) { + this.$eventBus.$emit('play-item', playbackData) + }, + bindTimeMarkerEvents() { + const container = document.querySelector('.description-container') + if (container) { + container.addEventListener('click', (event) => { + if (event.target.classList.contains('time-marker')) { + this.clickPlaybackTime(event) + } + }) } }, async downloadClick() { From 67d32164d902d98e12644ef9c24d1bf96c8b8871 Mon Sep 17 00:00:00 2001 From: Marcos Carvalho Date: Mon, 20 May 2024 08:33:02 +0100 Subject: [PATCH 3/3] Update the parse description to identify time marker in anchor links --- pages/item/_id/_episode/index.vue | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/pages/item/_id/_episode/index.vue b/pages/item/_id/_episode/index.vue index d0ccd69d..c33ee6f4 100644 --- a/pages/item/_id/_episode/index.vue +++ b/pages/item/_id/_episode/index.vue @@ -314,21 +314,20 @@ export default { }, methods: { parseDescription(description) { - const timeMarkerRegex = /\d{1,2}:\d{1,2}(:\d{1,2})?/g + const timeMarkerLinkRegex = /(.*?)<\/a>/g + const timeMarkerRegex = /\b\d{1,2}:\d{1,2}(?::\d{1,2})?\b/g - return description.replace(timeMarkerRegex, (match) => { - const time = match.replace(/[()]/g, '') + function convertToSeconds(time) { const timeParts = time.split(':').map(Number) - let seekTimeInSeconds - - if (timeParts.length === 2) { - const [minutes, seconds] = timeParts - seekTimeInSeconds = minutes * 60 + seconds - } else { - const [hours, minutes, seconds] = timeParts - seekTimeInSeconds = hours * 3600 + minutes * 60 + seconds - } + return timeParts.reduce((acc, part, index) => acc * 60 + part, 0) + } + return description.replace(timeMarkerLinkRegex, (match, href, displayTime) => { + const time = displayTime.match(timeMarkerRegex)[0] + const seekTimeInSeconds = convertToSeconds(time) + return `${displayTime}` + }).replace(timeMarkerRegex, (match) => { + const seekTimeInSeconds = convertToSeconds(match) return `${match}` }) },