diff --git a/client/components/cards/BookMatchCard.vue b/client/components/cards/BookMatchCard.vue
index 6d82e4b5..dba64a7d 100644
--- a/client/components/cards/BookMatchCard.vue
+++ b/client/components/cards/BookMatchCard.vue
@@ -4,7 +4,7 @@
-
+
{{ book.title }}
@@ -15,6 +15,12 @@
{{ book.description }}
+
+
{{ book.title }}
+
by {{ book.author }}
+
{{ book.genres.join(', ') }}
+
{{ book.trackCount }} Episodes
+
@@ -33,6 +39,7 @@ export default {
type: Object,
default: () => {}
},
+ isPodcast: Boolean,
bookCoverAspectRatio: Number
},
data() {
diff --git a/client/components/modals/item/EditModal.vue b/client/components/modals/item/EditModal.vue
index af3ab8a0..2840898e 100644
--- a/client/components/modals/item/EditModal.vue
+++ b/client/components/modals/item/EditModal.vue
@@ -116,19 +116,16 @@ export default {
userCanDownload() {
return this.$store.getters['user/getUserCanDownload']
},
- showExperimentalFeatures() {
- return this.$store.state.showExperimentalFeatures
- },
availableTabs() {
if (!this.userCanUpdate && !this.userCanDownload) return []
return this.tabs.filter((tab) => {
if (tab.id === 'download' && this.isMissing) return false
- if (this.mediaType == 'podcast' && (tab.id == 'match' || tab.id == 'chapters')) return false
+ if (this.mediaType == 'podcast' && tab.id == 'chapters') return false
if (this.mediaType == 'book' && tab.id == 'episodes') return false
if ((tab.id === 'download' || tab.id === 'files') && this.userCanDownload) return true
if (tab.id !== 'download' && tab.id !== 'files' && this.userCanUpdate) return true
- if (tab.id === 'match' && this.userCanUpdate && this.showExperimentalFeatures) return true
+ if (tab.id === 'match' && this.userCanUpdate) return true
return false
})
},
diff --git a/client/components/modals/item/tabs/Cover.vue b/client/components/modals/item/tabs/Cover.vue
index b0bd7b4b..64887db9 100644
--- a/client/components/modals/item/tabs/Cover.vue
+++ b/client/components/modals/item/tabs/Cover.vue
@@ -121,6 +121,7 @@ export default {
}
},
providers() {
+ if (this.isPodcast) return this.$store.state.scanners.podcastProviders
return this.$store.state.scanners.providers
},
searchTitleLabel() {
@@ -137,6 +138,12 @@ export default {
libraryItemId() {
return this.libraryItem ? this.libraryItem.id : null
},
+ mediaType() {
+ return this.libraryItem ? this.libraryItem.mediaType : null
+ },
+ isPodcast() {
+ return this.mediaType == 'podcast'
+ },
media() {
return this.libraryItem ? this.libraryItem.media || {} : {}
},
@@ -212,7 +219,8 @@ export default {
this.imageUrl = this.media.coverPath || ''
this.searchTitle = this.mediaMetadata.title || ''
this.searchAuthor = this.mediaMetadata.authorName || ''
- this.provider = localStorage.getItem('book-provider') || 'openlibrary'
+ if (this.isPodcast) this.provider = 'itunes'
+ else this.provider = localStorage.getItem('book-provider') || 'google'
},
removeCover() {
if (!this.media.coverPath) {
@@ -278,6 +286,7 @@ export default {
getSearchQuery() {
var searchQuery = `provider=${this.provider}&title=${this.searchTitle}`
if (this.searchAuthor) searchQuery += `&author=${this.searchAuthor}`
+ if (this.isPodcast) searchQuery += '&podcast=1'
return searchQuery
},
persistProvider() {
diff --git a/client/components/modals/item/tabs/Episodes.vue b/client/components/modals/item/tabs/Episodes.vue
index a67f1738..38f7a532 100644
--- a/client/components/modals/item/tabs/Episodes.vue
+++ b/client/components/modals/item/tabs/Episodes.vue
@@ -7,14 +7,14 @@
Check for new episodes
-->
-
+
No Episodes
Sort # |
- Episode # |
+ Episode # |
Title |
Duration |
Size |
diff --git a/client/components/modals/item/tabs/Match.vue b/client/components/modals/item/tabs/Match.vue
index 6a150e90..22b68cee 100644
--- a/client/components/modals/item/tabs/Match.vue
+++ b/client/components/modals/item/tabs/Match.vue
@@ -22,7 +22,7 @@
-
+
@@ -109,6 +109,36 @@
Currently: {{ mediaMetadata.asin || '' }}
+
+
+
+
+
+
Currently: {{ mediaMetadata.itunesId || '' }}
+
+
+
+
+
+
+
Currently: {{ mediaMetadata.feedUrl || '' }}
+
+
+
+
+
+
+
Currently: {{ mediaMetadata.itunesPageUrl || '' }}
+
+
+
+
+
+
+
Currently: {{ mediaMetadata.releaseDate || '' }}
+
+
+
Update
@@ -148,7 +178,12 @@ export default {
series: true,
volumeNumber: true,
asin: true,
- isbn: true
+ isbn: true,
+ // Podcast specific
+ itunesPageUrl: true,
+ itunesId: true,
+ feedUrl: true,
+ releaseDate: true
}
}
},
@@ -173,6 +208,7 @@ export default {
return this.$store.getters['getBookCoverAspectRatio']
},
providers() {
+ if (this.isPodcast) return this.$store.state.scanners.podcastProviders
return this.$store.state.scanners.providers
},
searchTitleLabel() {
@@ -185,6 +221,12 @@ export default {
},
mediaMetadata() {
return this.media.metadata || {}
+ },
+ mediaType() {
+ return this.libraryItem ? this.libraryItem.mediaType : null
+ },
+ isPodcast() {
+ return this.mediaType == 'podcast'
}
},
methods: {
@@ -196,6 +238,7 @@ export default {
}
},
getSearchQuery() {
+ if (this.isPodcast) return `term=${this.searchTitle}`
var searchQuery = `provider=${this.provider}&fallbackTitleOnly=1&title=${this.searchTitle}`
if (this.searchAuthor) searchQuery += `&author=${this.searchAuthor}`
return searchQuery
@@ -214,14 +257,27 @@ export default {
this.searchResults = []
this.isProcessing = true
this.lastSearch = searchQuery
- var results = await this.$axios.$get(`/api/search/books?${searchQuery}`).catch((error) => {
+ var searchEntity = this.isPodcast ? 'podcast' : 'books'
+ var results = await this.$axios.$get(`/api/search/${searchEntity}?${searchQuery}`).catch((error) => {
console.error('Failed', error)
return []
})
- results = results.filter((res) => {
+ // console.log('Got search results', results)
+ results = (results || []).filter((res) => {
return !!res.title
})
- this.searchResults = results
+
+ if (this.isPodcast) {
+ // Map to match PodcastMetadata keys
+ results = results.map((res) => {
+ res.itunesPageUrl = res.pageUrl || null
+ res.itunesId = res.id || null
+ res.author = res.artistName || null
+ return res
+ })
+ }
+
+ this.searchResults = results || []
this.isProcessing = false
this.hasSearched = true
},
@@ -239,7 +295,12 @@ export default {
series: true,
volumeNumber: true,
asin: true,
- isbn: true
+ isbn: true,
+ // Podcast specific
+ itunesPageUrl: true,
+ itunesId: true,
+ feedUrl: true,
+ releaseDate: true
}
if (this.libraryItem.id !== this.libraryItemId) {
@@ -255,7 +316,8 @@ export default {
}
this.searchTitle = this.libraryItem.media.metadata.title
this.searchAuthor = this.libraryItem.media.metadata.authorName || ''
- this.provider = localStorage.getItem('book-provider') || 'google'
+ if (this.isPodcast) this.provider = 'itunes'
+ else this.provider = localStorage.getItem('book-provider') || 'google'
},
selectMatch(match) {
this.selectedMatch = match
@@ -273,7 +335,7 @@ export default {
sequence: volumeNumber
}
updatePayload.series = [seriesItem]
- } else if (key === 'author') {
+ } else if (key === 'author' && !this.isPodcast) {
var authorItem = {
id: `new-${Math.floor(Math.random() * 10000)}`,
name: this.selectedMatch[key]
@@ -281,6 +343,8 @@ export default {
updatePayload.authors = [authorItem]
} else if (key === 'narrator') {
updatePayload.narrators = [this.selectedMatch[key]]
+ } else if (key === 'itunesId') {
+ updatePayload.itunesId = Number(this.selectedMatch[key])
} else if (key !== 'volumeNumber') {
updatePayload[key] = this.selectedMatch[key]
}
diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js
index 75070df0..db8b0412 100644
--- a/server/controllers/LibraryItemController.js
+++ b/server/controllers/LibraryItemController.js
@@ -66,7 +66,9 @@ class LibraryItemController {
await this.cacheManager.purgeCoverCache(libraryItem.id)
}
- await this.createAuthorsAndSeriesForItemUpdate(mediaPayload)
+ if (libraryItem.isBook) {
+ await this.createAuthorsAndSeriesForItemUpdate(mediaPayload)
+ }
var hasUpdates = libraryItem.media.update(mediaPayload)
if (hasUpdates) {
diff --git a/server/controllers/MiscController.js b/server/controllers/MiscController.js
index 4f35ff43..15136dcc 100644
--- a/server/controllers/MiscController.js
+++ b/server/controllers/MiscController.js
@@ -147,7 +147,11 @@ class MiscController {
async findCovers(req, res) {
var query = req.query
- var result = await this.bookFinder.findCovers(query.provider, query.title, query.author || null)
+ var podcast = query.podcast == 1
+
+ var result = null
+ if (podcast) result = await this.podcastFinder.findCovers(query.title)
+ else result = await this.bookFinder.findCovers(query.provider, query.title, query.author || null)
res.json(result)
}
diff --git a/server/finders/PodcastFinder.js b/server/finders/PodcastFinder.js
index a3e0c994..e0a204bd 100644
--- a/server/finders/PodcastFinder.js
+++ b/server/finders/PodcastFinder.js
@@ -13,5 +13,13 @@ class PodcastFinder {
Logger.debug(`[iTunes] Podcast search for "${term}" returned ${results.length} results`)
return results
}
+
+ async findCovers(term) {
+ if (!term) return null
+ Logger.debug(`[iTunes] Searching for podcast covers with term "${term}"`)
+ var results = await this.iTunesApi.searchPodcasts(term)
+ if (!results) return []
+ return results.map(r => r.cover).filter(r => r)
+ }
}
module.exports = PodcastFinder
\ No newline at end of file
diff --git a/server/objects/LibraryItem.js b/server/objects/LibraryItem.js
index 6b0de298..cd68ad2b 100644
--- a/server/objects/LibraryItem.js
+++ b/server/objects/LibraryItem.js
@@ -144,6 +144,8 @@ class LibraryItem {
}
}
+ get isPodcast() { return this.mediaType === 'podcast' }
+ get isBook() { return this.mediaType === 'book' }
get size() {
var total = 0
this.libraryFiles.forEach((lf) => total += lf.metadata.size)
diff --git a/server/providers/iTunes.js b/server/providers/iTunes.js
index 906c90db..7f43d819 100644
--- a/server/providers/iTunes.js
+++ b/server/providers/iTunes.js
@@ -80,7 +80,7 @@ class iTunes {
cleanPodcast(data) {
return {
id: data.collectionId,
- artistId: data.artistId,
+ artistId: data.artistId || null,
title: data.collectionName,
artistName: data.artistName,
description: stripHtml(data.description || '').result,