Merge pull request #3117 from mikiher/show-subtitles

New feature: Show Subtitles
This commit is contained in:
advplyr 2024-07-09 16:08:36 -05:00 committed by GitHub
commit 10cb8ebf3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 117 additions and 4682 deletions

View File

@ -170,13 +170,13 @@ export default {
if (!this.isPodcastLibrary && this.selectedMediaItemsArePlayable) { if (!this.isPodcastLibrary && this.selectedMediaItemsArePlayable) {
options.push({ options.push({
text: 'Quick Embed Metadata', text: this.$strings.ButtonQuickEmbedMetadata,
action: 'quick-embed' action: 'quick-embed'
}) })
} }
options.push({ options.push({
text: 'Re-Scan', text: this.$strings.ButtonReScan,
action: 'rescan' action: 'rescan'
}) })

View File

@ -53,7 +53,6 @@
<span class="font-mono">{{ numShowing }}</span> <span class="font-mono">{{ numShowing }}</span>
</div> </div>
<div class="flex-grow" /> <div class="flex-grow" />
<ui-checkbox v-if="!isBatchSelecting" v-model="settings.collapseBookSeries" :label="$strings.LabelCollapseSeries" checkbox-bg="bg" check-color="white" small class="mr-2" @input="updateCollapseBookSeries" />
<!-- RSS feed --> <!-- RSS feed -->
<ui-tooltip v-if="seriesRssFeed" :text="$strings.LabelOpenRSSFeed" direction="top"> <ui-tooltip v-if="seriesRssFeed" :text="$strings.LabelOpenRSSFeed" direction="top">
@ -68,9 +67,6 @@
<div class="flex-grow hidden sm:inline-block" /> <div class="flex-grow hidden sm:inline-block" />
<!-- collapse series checkbox -->
<ui-checkbox v-if="isLibraryPage && isBookLibrary && !isBatchSelecting" v-model="settings.collapseSeries" :label="$strings.LabelCollapseSeries" checkbox-bg="bg" check-color="white" small class="mr-2" @input="updateCollapseSeries" />
<!-- library filter select --> <!-- library filter select -->
<controls-library-filter-select v-if="isLibraryPage && !isBatchSelecting" v-model="settings.filterBy" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateFilter" /> <controls-library-filter-select v-if="isLibraryPage && !isBatchSelecting" v-model="settings.filterBy" class="w-36 sm:w-44 md:w-48 h-7.5 ml-1 sm:ml-4" @change="updateFilter" />
@ -88,11 +84,17 @@
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" :menu-width="110" class="ml-2" @action="contextMenuAction" /> <ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" :menu-width="110" class="ml-2" @action="contextMenuAction" />
</template> </template>
<!-- home page -->
<template v-else-if="isHome">
<div class="flex-grow" />
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" :menu-width="110" class="ml-2" @action="contextMenuAction" />
</template>
<!-- search page --> <!-- search page -->
<template v-else-if="page === 'search'"> <template v-else-if="page === 'search'">
<div class="flex-grow" /> <div class="flex-grow" />
<p>{{ $strings.MessageSearchResultsFor }} "{{ searchQuery }}"</p> <p>{{ $strings.MessageSearchResultsFor }} "{{ searchQuery }}"</p>
<div class="flex-grow" /> <div class="flex-grow" />
<ui-context-menu-dropdown v-if="contextMenuItems.length" :items="contextMenuItems" :menu-width="110" class="ml-2" @action="contextMenuAction" />
</template> </template>
<!-- authors page --> <!-- authors page -->
<template v-else-if="page === 'authors'"> <template v-else-if="page === 'authors'">
@ -151,11 +153,13 @@ export default {
if (this.isSeriesRemovedFromContinueListening) { if (this.isSeriesRemovedFromContinueListening) {
items.push({ items.push({
text: 'Re-Add Series to Continue Listening', text: this.$strings.LabelReAddSeriesToContinueListening,
action: 're-add-to-continue-listening' action: 're-add-to-continue-listening'
}) })
} }
this.addSubtitlesMenuItem(items)
return items return items
}, },
seriesSortItems() { seriesSortItems() {
@ -318,11 +322,14 @@ export default {
if (this.isPodcastLibrary && this.isLibraryPage && this.userCanDownload) { if (this.isPodcastLibrary && this.isLibraryPage && this.userCanDownload) {
items.push({ items.push({
text: 'Export OPML', text: this.$strings.LabelExportOPML,
action: 'export-opml' action: 'export-opml'
}) })
} }
this.addSubtitlesMenuItem(items)
this.addCollapseSeriesMenuItem(items)
return items return items
}, },
showPlaylists() { showPlaylists() {
@ -330,9 +337,70 @@ export default {
} }
}, },
methods: { methods: {
addSubtitlesMenuItem(items) {
if (this.isBookLibrary && (!this.page || this.page === 'search')) {
if (this.settings.showSubtitles) {
items.push({
text: this.$strings.LabelHideSubtitles,
action: 'hide-subtitles'
})
} else {
items.push({
text: this.$strings.LabelShowSubtitles,
action: 'show-subtitles'
})
}
}
},
addCollapseSeriesMenuItem(items) {
if (this.isLibraryPage && this.isBookLibrary && !this.isBatchSelecting) {
if (this.settings.collapseSeries) {
items.push({
text: this.$strings.LabelExpandSeries,
action: 'expand-series'
})
} else {
items.push({
text: this.$strings.LabelCollapseSeries,
action: 'collapse-series'
})
}
}
},
handleSubtitlesAction(action) {
if (action === 'show-subtitles') {
this.settings.showSubtitles = true
this.updateShowSubtitles()
return true
}
if (action === 'hide-subtitles') {
this.settings.showSubtitles = false
this.updateShowSubtitles()
return true
}
return false
},
handleCollapseSeriesAction(action) {
if (action === 'collapse-series') {
this.settings.collapseSeries = true
this.updateCollapseSeries()
return true
}
if (action === 'expand-series') {
this.settings.collapseSeries = false
this.updateCollapseSeries()
return true
}
return false
},
contextMenuAction({ action }) { contextMenuAction({ action }) {
if (action === 'export-opml') { if (action === 'export-opml') {
this.exportOPML() this.exportOPML()
return
} else if (this.handleSubtitlesAction(action)) {
return
} else if (this.handleCollapseSeriesAction(action)) {
return
} }
}, },
exportOPML() { exportOPML() {
@ -353,6 +421,8 @@ export default {
return return
} }
this.markSeriesFinished() this.markSeriesFinished()
} else if (this.handleSubtitlesAction(action)) {
return
} }
}, },
showOpenSeriesRSSFeed() { showOpenSeriesRSSFeed() {
@ -482,6 +552,9 @@ export default {
updateCollapseBookSeries() { updateCollapseBookSeries() {
this.saveSettings() this.saveSettings()
}, },
updateShowSubtitles() {
this.saveSettings()
},
updateAuthorSort() { updateAuthorSort() {
this.saveSettings() this.saveSettings()
}, },

View File

@ -132,6 +132,9 @@
<widgets-explicit-indicator cy-id="explicitIndicator" v-if="isExplicit" /> <widgets-explicit-indicator cy-id="explicitIndicator" v-if="isExplicit" />
</ui-tooltip> </ui-tooltip>
</div> </div>
<ui-tooltip v-if="showSubtitles" :text="displaySubtitle" :disabled="!displaySubtitleTruncated" direction="bottom" :delayOnShow="500" class="flex items-center">
<p cy-id="subtitle" class="truncate" ref="displaySubtitle" :style="{ fontSize: 0.6 + 'em' }">{{ displaySubtitle }}</p>
</ui-tooltip>
<p cy-id="line2" class="truncate text-gray-400" :style="{ fontSize: 0.8 + 'em' }">{{ displayLineTwo || '&nbsp;' }}</p> <p cy-id="line2" class="truncate text-gray-400" :style="{ fontSize: 0.8 + 'em' }">{{ displayLineTwo || '&nbsp;' }}</p>
<p cy-id="line3" v-if="displaySortLine" class="truncate text-gray-400" :style="{ fontSize: 0.8 + 'em' }">{{ displaySortLine }}</p> <p cy-id="line3" v-if="displaySortLine" class="truncate text-gray-400" :style="{ fontSize: 0.8 + 'em' }">{{ displaySortLine }}</p>
</div> </div>
@ -171,6 +174,7 @@ export default {
selected: false, selected: false,
isSelectionMode: false, isSelectionMode: false,
displayTitleTruncated: false, displayTitleTruncated: false,
displaySubtitleTruncated: false,
showCoverBg: false showCoverBg: false
} }
}, },
@ -237,7 +241,7 @@ export default {
return this._libraryItem.mediaType return this._libraryItem.mediaType
}, },
isPodcast() { isPodcast() {
return this.mediaType === 'podcast' return this.mediaType === 'podcast' || this.store.getters['libraries/getCurrentLibraryMediaType'] === 'podcast'
}, },
isMusic() { isMusic() {
return this.mediaType === 'music' return this.mediaType === 'music'
@ -339,6 +343,13 @@ export default {
if (this.collapsedSeries) return ignorePrefix ? this.collapsedSeries.nameIgnorePrefix : this.collapsedSeries.name if (this.collapsedSeries) return ignorePrefix ? this.collapsedSeries.nameIgnorePrefix : this.collapsedSeries.name
return ignorePrefix ? this.mediaMetadata.titleIgnorePrefix || '\u00A0' : this.title || '\u00A0' return ignorePrefix ? this.mediaMetadata.titleIgnorePrefix || '\u00A0' : this.title || '\u00A0'
}, },
displaySubtitle() {
if (!this.libraryItem) return '\u00A0'
if (this.collapsedSeries) return this.collapsedSeries.numBooks === 1 ? '1 book' : `${this.collapsedSeries.numBooks} books`
if (this.mediaMetadata.subtitle) return this.mediaMetadata.subtitle
if (this.mediaMetadata.seriesName) return this.mediaMetadata.seriesName
return ''
},
displayLineTwo() { displayLineTwo() {
if (this.recentEpisode) return this.title if (this.recentEpisode) return this.title
if (this.isPodcast) return this.author if (this.isPodcast) return this.author
@ -644,6 +655,9 @@ export default {
}, },
mediaItemShare() { mediaItemShare() {
return this._libraryItem.mediaItemShare || null return this._libraryItem.mediaItemShare || null
},
showSubtitles() {
return !this.isPodcast && this.store.getters['user/getUserSetting']('showSubtitles')
} }
}, },
methods: { methods: {
@ -685,6 +699,9 @@ export default {
if (this.$refs.displayTitle) { if (this.$refs.displayTitle) {
this.displayTitleTruncated = this.$refs.displayTitle.scrollWidth > this.$refs.displayTitle.clientWidth this.displayTitleTruncated = this.$refs.displayTitle.scrollWidth > this.$refs.displayTitle.clientWidth
} }
if (this.$refs.displaySubtitle) {
this.displaySubtitleTruncated = this.$refs.displaySubtitle.scrollWidth > this.$refs.displaySubtitle.clientWidth
}
}) })
}, },
clickCard(e) { clickCard(e) {

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ function createMountOptions() {
mediaType: 'book', mediaType: 'book',
media: { media: {
id: 'book1', id: 'book1',
metadata: { title: 'The Fellowship of the Ring', titleIgnorePrefix: 'Fellowship of the Ring', authorName: 'J. R. R. Tolkien' }, metadata: { title: 'The Fellowship of the Ring', titleIgnorePrefix: 'Fellowship of the Ring', authorName: 'J. R. R. Tolkien', subtitle: 'The Lord of the Rings, Book 1' },
numTracks: 1 numTracks: 1
} }
} }
@ -138,6 +138,16 @@ describe('LazyBookCard', () => {
}) })
}) })
it('shows subtitle when showSubtitles settings is true', () => {
mountOptions.mocks.$store.getters['user/getUserSetting'] = (settingName) => {
if (settingName === 'showSubtitles') return true
}
cy.mount(LazyBookCard, mountOptions)
cy.get('&titleImageNotReady').should('be.hidden')
cy.get('&subtitle').should('be.visible').and('have.text', 'The Lord of the Rings, Book 1')
})
it('shows overlay on mouseover', () => { it('shows overlay on mouseover', () => {
cy.mount(LazyBookCard, mountOptions) cy.mount(LazyBookCard, mountOptions)
cy.get('#book-card-0').trigger('mouseover') cy.get('#book-card-0').trigger('mouseover')

View File

@ -8,6 +8,7 @@ export const state = () => ({
bookshelfCoverSize: 120, bookshelfCoverSize: 120,
collapseSeries: false, collapseSeries: false,
collapseBookSeries: false, collapseBookSeries: false,
showSubtitles: false,
useChapterTrack: false, useChapterTrack: false,
seriesSortBy: 'name', seriesSortBy: 'name',
seriesSortDesc: false, seriesSortDesc: false,

View File

@ -59,6 +59,7 @@
"ButtonPurgeItemsCache": "Purge Items Cache", "ButtonPurgeItemsCache": "Purge Items Cache",
"ButtonQueueAddItem": "Add to queue", "ButtonQueueAddItem": "Add to queue",
"ButtonQueueRemoveItem": "Remove from queue", "ButtonQueueRemoveItem": "Remove from queue",
"ButtonQuickEmbedMetadata": "Quick Embed Metadata",
"ButtonQuickMatch": "Quick Match", "ButtonQuickMatch": "Quick Match",
"ButtonReScan": "Re-Scan", "ButtonReScan": "Re-Scan",
"ButtonRead": "Read", "ButtonRead": "Read",
@ -293,9 +294,11 @@
"LabelEpisodeTitle": "Episode Title", "LabelEpisodeTitle": "Episode Title",
"LabelEpisodeType": "Episode Type", "LabelEpisodeType": "Episode Type",
"LabelExample": "Example", "LabelExample": "Example",
"LabelExpandSeries": "Expand Series",
"LabelExplicit": "Explicit", "LabelExplicit": "Explicit",
"LabelExplicitChecked": "Explicit (checked)", "LabelExplicitChecked": "Explicit (checked)",
"LabelExplicitUnchecked": "Not Explicit (unchecked)", "LabelExplicitUnchecked": "Not Explicit (unchecked)",
"LabelExportOPML": "Export OPML",
"LabelFeedURL": "Feed URL", "LabelFeedURL": "Feed URL",
"LabelFetchingMetadata": "Fetching Metadata", "LabelFetchingMetadata": "Fetching Metadata",
"LabelFile": "File", "LabelFile": "File",
@ -319,6 +322,7 @@
"LabelHardDeleteFile": "Hard delete file", "LabelHardDeleteFile": "Hard delete file",
"LabelHasEbook": "Has ebook", "LabelHasEbook": "Has ebook",
"LabelHasSupplementaryEbook": "Has supplementary ebook", "LabelHasSupplementaryEbook": "Has supplementary ebook",
"LabelHideSubtitles": "Hide Subtitles",
"LabelHighestPriority": "Highest priority", "LabelHighestPriority": "Highest priority",
"LabelHost": "Host", "LabelHost": "Host",
"LabelHour": "Hour", "LabelHour": "Hour",
@ -446,6 +450,7 @@
"LabelRSSFeedPreventIndexing": "Prevent Indexing", "LabelRSSFeedPreventIndexing": "Prevent Indexing",
"LabelRSSFeedSlug": "RSS Feed Slug", "LabelRSSFeedSlug": "RSS Feed Slug",
"LabelRSSFeedURL": "RSS Feed URL", "LabelRSSFeedURL": "RSS Feed URL",
"LabelReAddSeriesToContinueListening": "Re-add series to Continue Listening",
"LabelRead": "Read", "LabelRead": "Read",
"LabelReadAgain": "Read Again", "LabelReadAgain": "Read Again",
"LabelReadEbookWithoutProgress": "Read ebook without keeping progress", "LabelReadEbookWithoutProgress": "Read ebook without keeping progress",
@ -516,6 +521,7 @@
"LabelShareURL": "Share URL", "LabelShareURL": "Share URL",
"LabelShowAll": "Show All", "LabelShowAll": "Show All",
"LabelShowSeconds": "Show seconds", "LabelShowSeconds": "Show seconds",
"LabelShowSubtitles": "Show Subtitles",
"LabelSize": "Size", "LabelSize": "Size",
"LabelSleepTimer": "Sleep timer", "LabelSleepTimer": "Sleep timer",
"LabelSlug": "Slug", "LabelSlug": "Slug",