From d15264832dfc2a9ce418846eb2a4e4b8ecad1f50 Mon Sep 17 00:00:00 2001 From: Kaldigo <6437691+kaldigo@users.noreply.github.com> Date: Mon, 23 May 2022 03:56:51 +0100 Subject: [PATCH 1/4] Updated matching with latest changes, Added override toggle for quickmatch, added asin and isbn to quickmatch query, updated audible provider to use audnexus --- client/components/modals/item/tabs/Match.vue | 88 ++++++++++++---- client/pages/config/index.vue | 11 ++ server/finders/BookFinder.js | 8 +- server/objects/entities/Series.js | 2 +- server/objects/settings/ServerSettings.js | 5 +- server/providers/Audible.js | 100 +++++++++---------- server/scanner/ScanOptions.js | 5 +- server/scanner/Scanner.js | 71 +++++++++---- 8 files changed, 192 insertions(+), 98 deletions(-) diff --git a/client/components/modals/item/tabs/Match.vue b/client/components/modals/item/tabs/Match.vue index 22b68cee..5ce43ed3 100644 --- a/client/components/modals/item/tabs/Match.vue +++ b/client/components/modals/item/tabs/Match.vue @@ -87,7 +87,7 @@
- +

Currently: {{ mediaMetadata.seriesName || '' }}

@@ -95,6 +95,27 @@ +
+ +
+ +

Currently: {{ mediaMetadata.genresList || '' }}

+
+
+
+ +
+ +

Currently: {{ mediaMetadata.tagsList || '' }}

+
+
+
+ +
+ +

Currently: {{ mediaMetadata.language || '' }}

+
+
@@ -177,6 +198,10 @@ export default { publishedYear: true, series: true, volumeNumber: true, + genres: true, + tags: true, + language: true, + explicit: true, asin: true, isbn: true, // Podcast specific @@ -204,6 +229,19 @@ export default { this.$emit('update:processing', val) } }, + seriesItems: { + get() { + return this.selectedMatch.series.map((se) => { + return { + displayName: se.volumeNumber ? `${se.series} #${se.volumeNumber}` : se.series, + ...se + } + }) + }, + set(val) { + this.selectedMatch.series = val + } + }, bookCoverAspectRatio() { return this.$store.getters['getBookCoverAspectRatio'] }, @@ -294,6 +332,10 @@ export default { publishedYear: true, series: true, volumeNumber: true, + genres: true, + tags: true, + language: true, + explicit: true, asin: true, isbn: true, // Podcast specific @@ -324,32 +366,42 @@ export default { }, buildMatchUpdatePayload() { var updatePayload = {} + updatePayload.metadata = {} var volumeNumber = this.selectedMatchUsage.volumeNumber ? this.selectedMatch.volumeNumber || null : null for (const key in this.selectedMatchUsage) { if (this.selectedMatchUsage[key] && this.selectedMatch[key]) { if (key === 'series') { - var seriesItem = { + if(!Array.isArray(this.selectedMatch[key])) this.selectedMatch[key] = [{ series: this.selectedMatch[key], volumeNumber: volumeNumber }] + var seriesPayload = [] + this.selectedMatch[key].forEach(seriesItem => seriesPayload.push({ id: `new-${Math.floor(Math.random() * 10000)}`, - name: this.selectedMatch[key], - sequence: volumeNumber - } - updatePayload.series = [seriesItem] + name: seriesItem.series, + sequence: seriesItem.volumeNumber + })) + updatePayload.metadata.series = seriesPayload } else if (key === 'author' && !this.isPodcast) { - var authorItem = { + if(!Array.isArray(this.selectedMatch[key])) this.selectedMatch[key] = [this.selectedMatch[key]] + var authorPayload = [] + this.selectedMatch[key].forEach(authorName => authorPayload.push({ id: `new-${Math.floor(Math.random() * 10000)}`, - name: this.selectedMatch[key] - } - updatePayload.authors = [authorItem] + name: authorName + })) + updatePayload.metadata.authors = authorPayload } else if (key === 'narrator') { - updatePayload.narrators = [this.selectedMatch[key]] + updatePayload.metadata.narrators = [this.selectedMatch[key]] + } else if (key === 'genres') { + updatePayload.metadata.genres = this.selectedMatch[key].split(',') + } else if (key === 'tags') { + updatePayload.tags = this.selectedMatch[key].split(',') } else if (key === 'itunesId') { - updatePayload.itunesId = Number(this.selectedMatch[key]) - } else if (key !== 'volumeNumber') { - updatePayload[key] = this.selectedMatch[key] + updatePayload.metadata.itunesId = Number(this.selectedMatch[key]) + } else { + updatePayload.metadata[key] = this.selectedMatch[key] } } } + console.log(updatePayload) return updatePayload }, async submitMatchUpdate() { @@ -361,7 +413,7 @@ export default { if (updatePayload.cover) { var coverPayload = { - url: updatePayload.cover + url: updatePayload.metadata.cover } var success = await this.$axios.$post(`/api/items/${this.libraryItemId}/cover`, coverPayload).catch((error) => { console.error('Failed to update', error) @@ -373,13 +425,11 @@ export default { this.$toast.error('Item Cover Failed to Update') } console.log('Updated cover') - delete updatePayload.cover + delete updatePayload.metadata.cover } if (Object.keys(updatePayload).length) { - var mediaUpdatePayload = { - metadata: updatePayload - } + var mediaUpdatePayload = updatePayload var updateResult = await this.$axios.$patch(`/api/items/${this.libraryItemId}/media`, mediaUpdatePayload).catch((error) => { console.error('Failed to update', error) return false diff --git a/client/pages/config/index.vue b/client/pages/config/index.vue index 87a5d969..a037ac71 100644 --- a/client/pages/config/index.vue +++ b/client/pages/config/index.vue @@ -113,6 +113,16 @@
+
+ + +

+ Scanner prefer matched metadata + info_outlined +

+
+
+
@@ -226,6 +236,7 @@ export default { experimentalFeatures: 'Features in development that could use your feedback and help testing. Click to open github discussion.', scannerDisableWatcher: 'Disables the automatic adding/updating of items when file changes are detected. *Requires server restart', scannerPreferOpfMetadata: 'OPF file metadata will be used for book details over folder names', + scannerPreferMatchedMetadata: 'Matched data will overide book details when using Quick Match', scannerPreferAudioMetadata: 'Audio file ID3 meta tags will be used for book details over folder names', scannerParseSubtitle: 'Extract subtitles from audiobook folder names.
Subtitle must be seperated by " - "
i.e. "Book Title - A Subtitle Here" has the subtitle "A Subtitle Here"', sortingIgnorePrefix: 'i.e. for prefix "the" book title "The Book Title" would sort as "Book Title, The"', diff --git a/server/finders/BookFinder.js b/server/finders/BookFinder.js index 60645747..60b39838 100644 --- a/server/finders/BookFinder.js +++ b/server/finders/BookFinder.js @@ -166,14 +166,14 @@ class BookFinder { return this.iTunesApi.searchAudiobooks(title) } - async getAudibleResults(title, author) { - var books = await this.audible.search(title, author); + async getAudibleResults(title, author, asin) { + var books = await this.audible.search(title, author, asin); if (this.verbose) Logger.debug(`Audible Book Search Results: ${books.length || 0}`) if (!books) return [] return books } - async search(provider, title, author, options = {}) { + async search(provider, title, author, isbn, asin, options = {}) { var books = [] var maxTitleDistance = !isNaN(options.titleDistance) ? Number(options.titleDistance) : 4 var maxAuthorDistance = !isNaN(options.authorDistance) ? Number(options.authorDistance) : 4 @@ -182,7 +182,7 @@ class BookFinder { if (provider === 'google') { return this.getGoogleBooksResults(title, author) } else if (provider === 'audible') { - return this.getAudibleResults(title, author) + return this.getAudibleResults(title, author, asin) } else if (provider === 'itunes') { return this.getiTunesAudiobooksResults(title, author) } else if (provider === 'libgen') { diff --git a/server/objects/entities/Series.js b/server/objects/entities/Series.js index 18f3b4a3..9cb38a0b 100644 --- a/server/objects/entities/Series.js +++ b/server/objects/entities/Series.js @@ -48,7 +48,7 @@ class Series { } checkNameEquals(name) { - if (!name) return false + if (!name || !this.name) return false return this.name.toLowerCase() == name.toLowerCase().trim() } } diff --git a/server/objects/settings/ServerSettings.js b/server/objects/settings/ServerSettings.js index bea7b6ed..286e15c0 100644 --- a/server/objects/settings/ServerSettings.js +++ b/server/objects/settings/ServerSettings.js @@ -11,7 +11,8 @@ class ServerSettings { this.scannerCoverProvider = 'google' this.scannerPreferAudioMetadata = false this.scannerPreferOpfMetadata = false - this.scannerDisableWatcher = false + this.scannerPreferMatchedMetadata = false + this.scannerDisableWatcher = false // Metadata - choose to store inside users library item folder this.storeCoverWithItem = false @@ -62,6 +63,7 @@ class ServerSettings { this.scannerParseSubtitle = settings.scannerParseSubtitle this.scannerPreferAudioMetadata = !!settings.scannerPreferAudioMetadata this.scannerPreferOpfMetadata = !!settings.scannerPreferOpfMetadata + this.scannerPreferMatchedMetadata = !!settings.scannerPreferMatchedMetadata this.scannerDisableWatcher = !!settings.scannerDisableWatcher this.storeCoverWithItem = !!settings.storeCoverWithItem @@ -107,6 +109,7 @@ class ServerSettings { scannerParseSubtitle: this.scannerParseSubtitle, scannerPreferAudioMetadata: this.scannerPreferAudioMetadata, scannerPreferOpfMetadata: this.scannerPreferOpfMetadata, + scannerPreferMatchedMetadata: this.scannerPreferMatchedMetadata, scannerDisableWatcher: this.scannerDisableWatcher, storeCoverWithItem: this.storeCoverWithItem, storeMetadataWithItem: this.storeMetadataWithItem, diff --git a/server/providers/Audible.js b/server/providers/Audible.js index c6d6836e..21521623 100644 --- a/server/providers/Audible.js +++ b/server/providers/Audible.js @@ -6,83 +6,81 @@ class Audible { constructor() { } cleanResult(item) { - var { title, subtitle, asin, authors, narrators, publisher_name, publisher_summary, release_date, series, product_images, publication_name } = item; + var { title, subtitle, asin, authors, narrators, publisherName, summary, releaseDate, image, genres, seriesPrimary, seriesSecondary, language } = item; - var primarySeries = this.getPrimarySeries(series, publication_name); + var series = [] + if(seriesPrimary) series.push(seriesPrimary) + if(seriesSecondary) series.push(seriesSecondary) + + var genresFiltered = genres ? genres.filter(g => g.type == "genre") : [] + var tagsFiltered = genres ? genres.filter(g => g.type == "tag") : [] return { title, subtitle: subtitle || null, author: authors ? authors.map(({ name }) => name).join(', ') : null, narrator: narrators ? narrators.map(({ name }) => name).join(', ') : null, - publisher: publisher_name, - publishedYear: release_date ? release_date.split('-')[0] : null, - description: publisher_summary ? stripHtml(publisher_summary).result : null, - cover: this.getBestImageLink(product_images), + publisher: publisherName, + publishedYear: releaseDate ? releaseDate.split('-')[0] : null, + description: summary ? stripHtml(summary).result : null, + cover: image, asin, - series: primarySeries ? primarySeries.title : null, - volumeNumber: primarySeries ? primarySeries.sequence : null + genres: genresFiltered.length > 0 ? genresFiltered.map(({ name }) => name).join(', ') : null, + tags: tagsFiltered.length > 0 ? tagsFiltered.map(({ name }) => name).join(', ') : null, + series: series != [] ? series.map(({name, position}) => ({ series: name, volumeNumber: position })) : null, + language: language ? language.charAt(0).toUpperCase() + language.slice(1) : null } } - getBestImageLink(images) { - if (!images) return null - var keys = Object.keys(images) - if (!keys.length) return null - return images[keys[keys.length - 1]] - } - - getPrimarySeries(series, publication_name) { - return (series && series.length > 0) ? series.find((s) => s.title == publication_name) || series[0] : null - } - isProbablyAsin(title) { return /^[0-9A-Z]{10}$/.test(title) } asinSearch(asin) { - var queryObj = { - response_groups: 'rating,series,contributors,product_desc,media,product_extended_attrs', - image_sizes: '500,1024,2000' - }; - var queryString = (new URLSearchParams(queryObj)).toString(); asin = encodeURIComponent(asin); - var url = `https://api.audible.com/1.0/catalog/products/${asin}?${queryString}` + var url = `https://api.audnex.us/books/${asin}` Logger.debug(`[Audible] ASIN url: ${url}`) return axios.get(url).then((res) => { - if (!res || !res.data || !res.data.product || !res.data.product.authors) return [] - return [res.data.product] + if (!res || !res.data || !res.data.asin) return null + return res.data }).catch(error => { - Logger.error('[Audible] search error', error) + Logger.error('[Audible] ASIN search error', error) return [] }) } - async search(title, author) { - if (this.isProbablyAsin(title)) { - var items = await this.asinSearch(title) - if (items.length > 0) return items.map(item => this.cleanResult(item)) + async search(title, author, asin) { + var items + if(asin) { + items = [await this.asinSearch(asin)] + } + + if (!items && this.isProbablyAsin(title)) { + items = [await this.asinSearch(title)] } - var queryObj = { - response_groups: 'rating,series,contributors,product_desc,media,product_extended_attrs', - image_sizes: '500,1024,2000', - num_results: '25', - products_sort_by: 'Relevance', - title: title - }; - if (author) queryObj.author = author - var queryString = (new URLSearchParams(queryObj)).toString(); - var url = `https://api.audible.com/1.0/catalog/products?${queryString}` - Logger.debug(`[Audible] Search url: ${url}`) - var items = await axios.get(url).then((res) => { - if (!res || !res.data || !res.data.products) return [] - return res.data.products - }).catch(error => { - Logger.error('[Audible] search error', error) - return [] - }) - return items.map(item => this.cleanResult(item)) + if(!items) { + var queryObj = { + response_groups: 'rating,series,contributors,product_desc,media,product_extended_attrs', + image_sizes: '500,1024,2000', + num_results: '25', + products_sort_by: 'Relevance', + title: title + }; + if (author) queryObj.author = author + var queryString = (new URLSearchParams(queryObj)).toString(); + var url = `https://api.audible.com/1.0/catalog/products?${queryString}` + Logger.debug(`[Audible] Search url: ${url}`) + items = await axios.get(url).then((res) => { + if (!res || !res.data || !res.data.products) return null + return Promise.all(res.data.products.map(result => this.asinSearch(result.asin))) + }).catch(error => { + Logger.error('[Audible] query search error', error) + return [] + }) + } + + return items ? items.map(item => this.cleanResult(item)) : [] } } diff --git a/server/scanner/ScanOptions.js b/server/scanner/ScanOptions.js index ccc6ca2f..04adf2f9 100644 --- a/server/scanner/ScanOptions.js +++ b/server/scanner/ScanOptions.js @@ -8,6 +8,7 @@ class ScanOptions { this.storeCoverWithItem = false this.preferAudioMetadata = false this.preferOpfMetadata = false + this.preferMatchedMetadata = false if (options) { this.construct(options) @@ -32,7 +33,8 @@ class ScanOptions { findCovers: this.findCovers, storeCoverWithItem: this.storeCoverWithItem, preferAudioMetadata: this.preferAudioMetadata, - preferOpfMetadata: this.preferOpfMetadata + preferOpfMetadata: this.preferOpfMetadata, + preferOpfMetadata: this.preferMatchedMetadata } } @@ -44,6 +46,7 @@ class ScanOptions { this.storeCoverWithItem = serverSettings.storeCoverWithItem this.preferAudioMetadata = serverSettings.scannerPreferAudioMetadata this.preferOpfMetadata = serverSettings.scannerPreferOpfMetadata + this.preferOpfMetadata = serverSettings.scannerPreferMatchedMetadata } } module.exports = ScanOptions \ No newline at end of file diff --git a/server/scanner/Scanner.js b/server/scanner/Scanner.js index 7e9ffc2e..3ded0c10 100644 --- a/server/scanner/Scanner.js +++ b/server/scanner/Scanner.js @@ -632,8 +632,10 @@ class Scanner { var provider = options.provider || 'google' var searchTitle = options.title || libraryItem.media.metadata.title var searchAuthor = options.author || libraryItem.media.metadata.authorName + var searchISBN = options.isbn || libraryItem.media.metadata.isbn + var searchASIN = options.asin || libraryItem.media.metadata.asin - var results = await this.bookFinder.search(provider, searchTitle, searchAuthor) + var results = await this.bookFinder.search(provider, searchTitle, searchAuthor, searchISBN, searchASIN) if (!results.length) { return { warning: `No ${provider} match found` @@ -641,6 +643,12 @@ class Scanner { } var matchData = results[0] + // Set to override existing metadata if scannerPreferMatchedMetadata setting is true + if(this.db.serverSettings.scannerPreferMatchedMetadata) { + options.overrideCover = true + options.overrideDetails = true + } + // Update cover if not set OR overrideCover flag var hasUpdated = false if (matchData.cover && (!libraryItem.media.coverPath || options.overrideCover)) { @@ -654,47 +662,68 @@ class Scanner { } // Update media metadata if not set OR overrideDetails flag - const detailKeysToUpdate = ['title', 'subtitle', 'description', 'narrator', 'publisher', 'publishedYear', 'asin', 'isbn'] + const detailKeysToUpdate = ['title', 'subtitle', 'description', 'narrator', 'publisher', 'publishedYear', 'genres', 'tags', 'language', 'explicit', 'asin', 'isbn'] const updatePayload = {} + updatePayload.metadata = {} for (const key in matchData) { if (matchData[key] && detailKeysToUpdate.includes(key)) { if (key === 'narrator') { if ((!libraryItem.media.metadata.narratorName || options.overrideDetails)) { - updatePayload.narrators = [matchData[key]] + updatePayload.metadata.narrators = matchData[key].split(',') + } + } else if (key === 'genres') { + if ((!libraryItem.media.metadata.genres || options.overrideDetails)) { + updatePayload.metadata[key] = matchData[key].split(',') + } + } else if (key === 'tags') { + if ((!libraryItem.media.tags || options.overrideDetails)) { + updatePayload[key] = matchData[key].split(',') } } else if ((!libraryItem.media.metadata[key] || options.overrideDetails)) { - updatePayload[key] = matchData[key] + updatePayload.metadata[key] = matchData[key] } } } // Add or set author if not set - if (matchData.author && !libraryItem.media.metadata.authorName) { - var author = this.db.authors.find(au => au.checkNameEquals(matchData.author)) - if (!author) { - author = new Author() - author.setData({ name: matchData.author }) - await this.db.insertEntity('author', author) - this.emitter('author_added', author) + if (matchData.author && !libraryItem.media.metadata.authorName || options.overrideDetails) { + if(!Array.isArray(matchData.author)) matchData.author = [matchData.author] + const authorPayload = [] + for (let index = 0; index < matchData.author.length; index++) { + const authorName = matchData.author[index] + var author = this.db.authors.find(au => au.checkNameEquals(authorName)) + if (!author) { + author = new Author() + author.setData({ name: authorName }) + await this.db.insertEntity('author', author) + this.emitter('author_added', author) + } + authorPayload.push(author.toJSONMinimal()) } - updatePayload.authors = [author.toJSONMinimal()] + updatePayload.metadata.authors = authorPayload } // Add or set series if not set - if (matchData.series && !libraryItem.media.metadata.seriesName) { - var seriesItem = this.db.series.find(au => au.checkNameEquals(matchData.series)) - if (!seriesItem) { - seriesItem = new Series() - seriesItem.setData({ name: matchData.series }) - await this.db.insertEntity('series', seriesItem) - this.emitter('series_added', seriesItem) + if (matchData.series && !libraryItem.media.metadata.seriesName || options.overrideDetails) { + if(!Array.isArray(matchData.series)) matchData.series = [{ series: matchData.series, volumeNumber: matchData.volumeNumber }] + const seriesPayload = [] + for (let index = 0; index < matchData.series.length; index++) { + const seriesMatchItem = matchData.series[index] + var seriesItem = this.db.series.find(au => au.checkNameEquals(seriesMatchItem.series)) + if (!seriesItem) { + seriesItem = new Series() + seriesItem.setData({ name: seriesMatchItem.series }) + await this.db.insertEntity('series', seriesItem) + this.emitter('series_added', seriesItem) + } + seriesPayload.push(seriesItem.toJSONMinimal(seriesMatchItem.volumeNumber)) } - updatePayload.series = [seriesItem.toJSONMinimal(matchData.volumeNumber)] + updatePayload.metadata.series = seriesPayload } if (Object.keys(updatePayload).length) { Logger.debug('[Scanner] Updating details', updatePayload) - if (libraryItem.media.update({ metadata: updatePayload })) { + if (libraryItem.media.update(updatePayload)) { hasUpdated = true } } From ce88ebb55bf3dabeb90e9623823a28eee335f3b1 Mon Sep 17 00:00:00 2001 From: Kaldigo <6437691+kaldigo@users.noreply.github.com> Date: Mon, 23 May 2022 22:48:11 +0100 Subject: [PATCH 2/4] Removed response groups in Audible query and limited to 10 results --- server/providers/Audible.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/providers/Audible.js b/server/providers/Audible.js index 21521623..9c1e1a97 100644 --- a/server/providers/Audible.js +++ b/server/providers/Audible.js @@ -61,9 +61,7 @@ class Audible { if(!items) { var queryObj = { - response_groups: 'rating,series,contributors,product_desc,media,product_extended_attrs', - image_sizes: '500,1024,2000', - num_results: '25', + num_results: '10', products_sort_by: 'Relevance', title: title }; From aeb87c81a17aa2f4bc3b156ffc4a7dca589c40fb Mon Sep 17 00:00:00 2001 From: Kaldigo <6437691+kaldigo@users.noreply.github.com> Date: Tue, 24 May 2022 01:29:43 +0100 Subject: [PATCH 3/4] Fix missed preferMatchedMetadata rename --- server/scanner/ScanOptions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/scanner/ScanOptions.js b/server/scanner/ScanOptions.js index 04adf2f9..3d9d4556 100644 --- a/server/scanner/ScanOptions.js +++ b/server/scanner/ScanOptions.js @@ -34,7 +34,7 @@ class ScanOptions { storeCoverWithItem: this.storeCoverWithItem, preferAudioMetadata: this.preferAudioMetadata, preferOpfMetadata: this.preferOpfMetadata, - preferOpfMetadata: this.preferMatchedMetadata + preferMatchedMetadata: this.preferMatchedMetadata } } @@ -46,7 +46,7 @@ class ScanOptions { this.storeCoverWithItem = serverSettings.storeCoverWithItem this.preferAudioMetadata = serverSettings.scannerPreferAudioMetadata this.preferOpfMetadata = serverSettings.scannerPreferOpfMetadata - this.preferOpfMetadata = serverSettings.scannerPreferMatchedMetadata + this.scannerPreferMatchedMetadata = serverSettings.scannerPreferMatchedMetadata } } module.exports = ScanOptions \ No newline at end of file From f41d6d5c77060967eadfa01649cf7a6d4408b4fe Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 28 May 2022 15:54:04 -0500 Subject: [PATCH 4/4] Update multi-series edit for match and make into separate component with inner modal --- .../modals/EditSeriesInputInnerModal.vue | 120 ++++++++++++++++++ client/components/modals/Modal.vue | 1 + client/components/modals/item/tabs/Match.vue | 68 +++++++--- client/components/widgets/BookDetailsEdit.vue | 87 +------------ .../components/widgets/SeriesInputWidget.vue | 111 ++++++++++++++++ client/store/index.js | 4 + server/controllers/LibraryItemController.js | 1 - 7 files changed, 284 insertions(+), 108 deletions(-) create mode 100644 client/components/modals/EditSeriesInputInnerModal.vue create mode 100644 client/components/widgets/SeriesInputWidget.vue diff --git a/client/components/modals/EditSeriesInputInnerModal.vue b/client/components/modals/EditSeriesInputInnerModal.vue new file mode 100644 index 00000000..dd993841 --- /dev/null +++ b/client/components/modals/EditSeriesInputInnerModal.vue @@ -0,0 +1,120 @@ + + + \ No newline at end of file diff --git a/client/components/modals/Modal.vue b/client/components/modals/Modal.vue index 1dfd2d71..5f289141 100644 --- a/client/components/modals/Modal.vue +++ b/client/components/modals/Modal.vue @@ -104,6 +104,7 @@ export default { } }, hotkey(action) { + if (this.$store.state.innerModalOpen) return if (action === this.$hotkeys.Modal.CLOSE) { this.show = false } diff --git a/client/components/modals/item/tabs/Match.vue b/client/components/modals/item/tabs/Match.vue index 5ce43ed3..8d057a4b 100644 --- a/client/components/modals/item/tabs/Match.vue +++ b/client/components/modals/item/tabs/Match.vue @@ -1,5 +1,5 @@ @@ -97,8 +76,6 @@ export default { }, data() { return { - selectedSeries: {}, - showSeriesForm: false, details: { title: null, subtitle: null, @@ -146,24 +123,6 @@ export default { }, filterData() { return this.$store.state.libraries.filterData || {} - }, - existingSeriesNames() { - // Only show series names not already selected - var alreadySelectedSeriesIds = this.details.series.map((se) => se.id) - return this.series.filter((se) => !alreadySelectedSeriesIds.includes(se.id)).map((se) => se.name) - }, - seriesItems: { - get() { - return this.details.series.map((se) => { - return { - displayName: se.sequence ? `${se.name} #${se.sequence}` : se.name, - ...se - } - }) - }, - set(val) { - this.details.series = val - } } }, methods: { @@ -214,50 +173,6 @@ export default { this.$refs.tagsSelect.forceBlur() } }, - cancelSeriesForm() { - this.showSeriesForm = false - }, - editSeriesItem(series) { - var _series = this.details.series.find((se) => se.id === series.id) - if (!_series) return - this.selectedSeries = { - ..._series - } - this.showSeriesForm = true - }, - addNewSeries() { - this.selectedSeries = { - id: `new-${Date.now()}`, - name: '', - sequence: '' - } - this.showSeriesForm = true - }, - submitSeriesForm() { - if (!this.selectedSeries.name) { - this.$toast.error('Must enter a series') - return - } - if (this.$refs.newSeriesSelect) { - this.$refs.newSeriesSelect.blur() - } - var existingSeriesIndex = this.details.series.findIndex((se) => se.id === this.selectedSeries.id) - - var seriesSameName = this.series.find((se) => se.name.toLowerCase() === this.selectedSeries.name.toLowerCase()) - if (existingSeriesIndex < 0 && seriesSameName) { - this.selectedSeries.id = seriesSameName.id - } - - if (existingSeriesIndex >= 0) { - this.details.series.splice(existingSeriesIndex, 1, { ...this.selectedSeries }) - } else { - this.details.series.push({ - ...this.selectedSeries - }) - } - - this.showSeriesForm = false - }, stringArrayEqual(array1, array2) { // return false if different if (array1.length !== array2.length) return false diff --git a/client/components/widgets/SeriesInputWidget.vue b/client/components/widgets/SeriesInputWidget.vue new file mode 100644 index 00000000..d57b3272 --- /dev/null +++ b/client/components/widgets/SeriesInputWidget.vue @@ -0,0 +1,111 @@ + + + \ No newline at end of file diff --git a/client/store/index.js b/client/store/index.js index 2b9a70ea..00af664b 100644 --- a/client/store/index.js +++ b/client/store/index.js @@ -20,6 +20,7 @@ export const state = () => ({ backups: [], bookshelfBookIds: [], openModal: null, + innerModalOpen: false, selectedBookshelfTexture: '/textures/wood_default.jpg', lastBookshelfScrollData: {} }) @@ -177,6 +178,9 @@ export const mutations = { setOpenModal(state, val) { state.openModal = val }, + setInnerModalOpen(state, val) { + state.innerModalOpen = val + }, setBookshelfTexture(state, val) { state.selectedBookshelfTexture = val } diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js index 9bd34c75..0499e4c0 100644 --- a/server/controllers/LibraryItemController.js +++ b/server/controllers/LibraryItemController.js @@ -51,7 +51,6 @@ class LibraryItemController { var hasUpdates = libraryItem.update(req.body) if (hasUpdates) { - // Turn on podcast auto download cron if not already on if (libraryItem.mediaType == 'podcast' && req.body.media.autoDownloadEpisodes && !this.podcastManager.episodeScheduleTask) { this.podcastManager.schedulePodcastEpisodeCron()