From 51b3f0d4654e2d51ccc4699d4b5cd6377267738e Mon Sep 17 00:00:00 2001 From: Rhys Tyers <5313660+rhyst@users.noreply.github.com> Date: Mon, 9 Jan 2023 21:48:56 +0000 Subject: [PATCH] WIP --- .../components/modals/podcast/EpisodeFeed.vue | 174 ------------------ .../tables/podcast/EpisodeTableRow.vue | 56 +++++- .../tables/podcast/EpisodesTable.vue | 75 +++++++- client/pages/item/_id/index.vue | 8 +- server/controllers/PodcastController.js | 11 +- server/managers/PodcastManager.js | 7 + server/objects/mediaTypes/Podcast.js | 24 ++- server/routers/ApiRouter.js | 2 +- 8 files changed, 157 insertions(+), 200 deletions(-) delete mode 100644 client/components/modals/podcast/EpisodeFeed.vue diff --git a/client/components/modals/podcast/EpisodeFeed.vue b/client/components/modals/podcast/EpisodeFeed.vue deleted file mode 100644 index 642a6e0ea..000000000 --- a/client/components/modals/podcast/EpisodeFeed.vue +++ /dev/null @@ -1,174 +0,0 @@ - - - - - diff --git a/client/components/tables/podcast/EpisodeTableRow.vue b/client/components/tables/podcast/EpisodeTableRow.vue index a184b3c89..363ea9dbf 100644 --- a/client/components/tables/podcast/EpisodeTableRow.vue +++ b/client/components/tables/podcast/EpisodeTableRow.vue @@ -15,29 +15,36 @@
- + + - + - + - + - - + + +

Downloading...

+

Queued...

{{ $strings.MessageNoEpisodes }}

- @@ -33,6 +35,14 @@ export default { libraryItem: { type: Object, default: () => {} + }, + episodesDownloading: { + type: Array, + default: () => [] + }, + episodesQueued: { + type: Array, + default: () => [] } }, data() { @@ -54,6 +64,11 @@ export default { handler() { this.init() } + }, + episodesDownloading: { + handler() { + console.log(this.episodesDownloading) + } } }, computed: { @@ -137,6 +152,17 @@ export default { return String(a[this.sortKey]).localeCompare(String(b[this.sortKey]), undefined, { numeric: true, sensitivity: 'base' }) }) }, + allEpisodes() { + const added = new Set() + const episodes = [] + for (const episode of [...this.episodes, ...this.media.podcastFeed.episodes]) { + const key = `${episode.pubDate}-${episode.title}` + if (added.has(key)) continue + episodes.push(episode) + added.add(key) + } + return episodes + }, selectedIsFinished() { // Find an item that is not finished, if none then all items finished return !this.selectedEpisodes.find((episode) => { @@ -290,6 +316,49 @@ export default { this.$store.commit('globals/setSelectedEpisode', episode) this.$store.commit('globals/setShowViewPodcastEpisodeModal', true) }, + refreshPodcastFeed() { + this.$axios + .get(`/api/podcasts/${this.libraryItem.id}/refresh-episodes-list`) + .then(() => { + this.$toast.success('Fetched episodes!') + }) + .catch((error) => { + this.$toast.error('Fetching episodes failed') + console.error('Fetching episodes failed', error) + }) + }, + downloadSelectedEpisodes() { + var episodesToDownload = this.selectedEpisodes + var payloadSize = JSON.stringify(episodesToDownload).length + var sizeInMb = payloadSize / 1024 / 1024 + var sizeInMbPretty = sizeInMb.toFixed(2) + 'MB' + console.log('Request size', sizeInMb) + if (sizeInMb > 4.99) { + return this.$toast.error(`Request is too large (${sizeInMbPretty}) should be < 5Mb`) + } + this.processing = true + this.$axios + .$post(`/api/podcasts/${this.libraryItem.id}/download-episodes`, episodesToDownload) + .then(() => { + this.processing = false + this.$toast.success('Started downloading episodes') + this.show = false + this.clearSelected() + }) + .catch((error) => { + var errorMsg = error.response && error.response.data ? error.response.data : 'Failed to download episodes' + console.error('Failed to download episodes', error) + this.processing = false + this.$toast.error(errorMsg) + this.clearSelected() + }) + }, + isDownloading(episode) { + return this.episodesDownloading.map(ep => ep.url).includes(episode?.url || episode?.enclosure?.url) + }, + isDownloadQueued(episode) { + return this.episodesQueued.map(ep => ep.url).includes(episode?.url || episode?.enclosure?.url) + }, init() { this.episodesCopy = this.episodes.map((ep) => ({ ...ep })) } diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue index 9f54d5bc1..c0b95938c 100644 --- a/client/pages/item/_id/index.vue +++ b/client/pages/item/_id/index.vue @@ -198,11 +198,6 @@ - - - - - @@ -225,7 +220,7 @@ - + @@ -234,7 +229,6 @@ - diff --git a/server/controllers/PodcastController.js b/server/controllers/PodcastController.js index 0eafd4e60..05e764f1f 100644 --- a/server/controllers/PodcastController.js +++ b/server/controllers/PodcastController.js @@ -45,6 +45,9 @@ class PodcastController { var libraryItemFolderStats = await getFileTimestampsWithIno(podcastPath) + var podcastFeed = await getPodcastFeed(payload.media.metadata.feedUrl) + var podcastFeedDownloadedAt = Date.now() + var relPath = payload.path.replace(folder.fullPath, '') if (relPath.startsWith('/')) relPath = relPath.slice(1) @@ -57,7 +60,7 @@ class PodcastController { mtimeMs: libraryItemFolderStats.mtimeMs || 0, ctimeMs: libraryItemFolderStats.ctimeMs || 0, birthtimeMs: libraryItemFolderStats.birthtimeMs || 0, - media: payload.media + media: { ...payload.media, podcastFeed, podcastFeedDownloadedAt } } var libraryItem = new LibraryItem() @@ -106,6 +109,12 @@ class PodcastController { res.json({ podcast }) } + async refreshPodcastFeed(req, res) { + var libraryItem = req.libraryItem; + await this.podcastManager.refreshPodcastFeed(libraryItem); + res.json({ episodes: libraryItem.media.episodes }); + } + async getOPMLFeeds(req, res) { if (!req.body.opmlText) { return res.sendStatus(400) diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js index 74751d458..253756bbe 100644 --- a/server/managers/PodcastManager.js +++ b/server/managers/PodcastManager.js @@ -49,6 +49,13 @@ class PodcastManager { } } + async refreshPodcastFeed(libraryItem) { + var feed = await getPodcastFeed(libraryItem.media.metadata.feedUrl); + libraryItem.updatedAt = Date.now(); + libraryItem.media.update({podcastFeed: feed, podcastFeedDownloadedAt: Date.now()}) + await this.db.updateLibraryItem(libraryItem); + } + async downloadPodcastEpisodes(libraryItem, episodesToDownload, isAutoDownload) { var index = libraryItem.media.episodes.length + 1 episodesToDownload.forEach((ep) => { diff --git a/server/objects/mediaTypes/Podcast.js b/server/objects/mediaTypes/Podcast.js index c94c8653f..26b005619 100644 --- a/server/objects/mediaTypes/Podcast.js +++ b/server/objects/mediaTypes/Podcast.js @@ -26,6 +26,9 @@ class Podcast { this.lastCoverSearch = null this.lastCoverSearchQuery = null + this.podcastFeed = null + this.podcastFeedDownloadedAt = null + if (podcast) { this.construct(podcast) } @@ -46,6 +49,8 @@ class Podcast { this.lastEpisodeCheck = podcast.lastEpisodeCheck || 0 this.maxEpisodesToKeep = podcast.maxEpisodesToKeep || 0 this.maxNewEpisodesToDownload = podcast.maxNewEpisodesToDownload || 3 + this.podcastFeed = podcast.podcastFeed + this.podcastFeedDownloadedAt = podcast.podcastFeedDownloadedAt } toJSON() { @@ -54,12 +59,14 @@ class Podcast { metadata: this.metadata.toJSON(), coverPath: this.coverPath, tags: [...this.tags], - episodes: this.episodes.map(e => e.toJSON()), + episodes: this.episodes.map((e) => e.toJSON()), autoDownloadEpisodes: this.autoDownloadEpisodes, autoDownloadSchedule: this.autoDownloadSchedule, lastEpisodeCheck: this.lastEpisodeCheck, maxEpisodesToKeep: this.maxEpisodesToKeep, - maxNewEpisodesToDownload: this.maxNewEpisodesToDownload + maxNewEpisodesToDownload: this.maxNewEpisodesToDownload, + podcastFeed: this.podcastFeed, + podcastFeedDownloadedAt: this.podcastFeedDownloadedAt, } } @@ -74,7 +81,9 @@ class Podcast { lastEpisodeCheck: this.lastEpisodeCheck, maxEpisodesToKeep: this.maxEpisodesToKeep, maxNewEpisodesToDownload: this.maxNewEpisodesToDownload, - size: this.size + size: this.size, + podcastFeed: this.podcastFeed, + podcastFeedDownloadedAt: this.podcastFeedDownloadedAt, } } @@ -84,13 +93,15 @@ class Podcast { metadata: this.metadata.toJSONExpanded(), coverPath: this.coverPath, tags: [...this.tags], - episodes: this.episodes.map(e => e.toJSONExpanded()), + episodes: this.episodes.map((e) => e.toJSONExpanded()), autoDownloadEpisodes: this.autoDownloadEpisodes, autoDownloadSchedule: this.autoDownloadSchedule, lastEpisodeCheck: this.lastEpisodeCheck, maxEpisodesToKeep: this.maxEpisodesToKeep, maxNewEpisodesToDownload: this.maxNewEpisodesToDownload, - size: this.size + size: this.size, + podcastFeed: this.podcastFeed, + podcastFeedDownloadedAt: this.podcastFeedDownloadedAt, } } @@ -185,6 +196,9 @@ class Podcast { this.autoDownloadEpisodes = !!mediaData.autoDownloadEpisodes this.autoDownloadSchedule = mediaData.autoDownloadSchedule || global.ServerSettings.podcastEpisodeSchedule this.lastEpisodeCheck = Date.now() // Makes sure new episodes are after this + + this.podcastFeed = mediaData.podcastFeed || null + this.podcastFeedDownloadedAt = mediaData.podcastFeedDownloadedAt || null } async syncMetadataFiles(textMetadataFiles, opfMetadataOverrideDetails) { diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index 4ca1aef61..ce7e723d0 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -234,7 +234,7 @@ class ApiRouter { this.router.post('/podcasts/:id/match-episodes', PodcastController.middleware.bind(this), PodcastController.quickMatchEpisodes.bind(this)) this.router.patch('/podcasts/:id/episode/:episodeId', PodcastController.middleware.bind(this), PodcastController.updateEpisode.bind(this)) this.router.delete('/podcasts/:id/episode/:episodeId', PodcastController.middleware.bind(this), PodcastController.removeEpisode.bind(this)) - + this.router.get('/podcasts/:id/refresh-episodes-list', PodcastController.middleware.bind(this), PodcastController.refreshPodcastFeed.bind(this)) // // Notification Routes (Admin and up) //