Add:Check for new episodes manual check and update last check time, Update:Adding new podcasts and downloading podcast episodes restricted to admin users

This commit is contained in:
advplyr 2022-04-29 16:42:40 -05:00
parent 8abda14e0f
commit 4185807da4
8 changed files with 76 additions and 13 deletions

View File

@ -52,7 +52,7 @@
<div v-show="isAuthorsPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" /> <div v-show="isAuthorsPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link> </nuxt-link>
<nuxt-link v-if="isPodcastLibrary" :to="`/library/${currentLibraryId}/podcast/search`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isPodcastSearchPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'"> <nuxt-link v-if="isPodcastLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/podcast/search`" class="w-full h-20 flex flex-col items-center justify-center text-white text-opacity-80 border-b border-primary border-opacity-70 hover:bg-primary cursor-pointer relative" :class="isPodcastSearchPage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
<icons-podcast-svg class="w-6 h-6" /> <icons-podcast-svg class="w-6 h-6" />
<p class="font-book pt-1.5" style="font-size: 0.9rem">Search</p> <p class="font-book pt-1.5" style="font-size: 0.9rem">Search</p>
@ -82,6 +82,9 @@ export default {
showExperimentalFeatures() { showExperimentalFeatures() {
return this.$store.state.showExperimentalFeatures return this.$store.state.showExperimentalFeatures
}, },
userIsAdminOrUp() {
return this.$store.getters['user/getIsAdminOrUp']
},
paramId() { paramId() {
return this.$route.params ? this.$route.params.id || '' : '' return this.$route.params ? this.$route.params.id || '' : ''
}, },

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="w-full h-full overflow-y-auto overflow-x-hidden px-4 py-6"> <div class="w-full h-full overflow-y-auto overflow-x-hidden px-4 py-6">
<div class="w-full mb-4"> <div class="w-full mb-4">
<!-- <div class="flex items-center mb-4"> <div v-if="userIsAdminOrUp" class="flex items-end justify-end mb-4">
<p v-if="autoDownloadEpisodes">Last new episode check {{ $formatDate(lastEpisodeCheck) }}</p> <!-- <p v-if="autoDownloadEpisodes">Last new episode check {{ $formatDate(lastEpisodeCheck) }}</p> -->
<div class="flex-grow" /> <ui-text-input-with-label ref="lastCheckInput" v-model="lastEpisodeCheckInput" :disabled="checkingNewEpisodes" type="datetime-local" label="Look for new episodes after this date" class="max-w-xs mr-2" />
<ui-btn :loading="checkingNewEpisodes" @click="checkForNewEpisodes">Check for new episodes</ui-btn> <ui-btn :loading="checkingNewEpisodes" @click="checkForNewEpisodes">Check & Download New Episodes</ui-btn>
</div> --> </div>
<div v-if="episodes.length" class="w-full p-4 bg-primary"> <div v-if="episodes.length" class="w-full p-4 bg-primary">
<p>Podcast Episodes</p> <p>Podcast Episodes</p>
@ -51,10 +51,23 @@ export default {
}, },
data() { data() {
return { return {
checkingNewEpisodes: false checkingNewEpisodes: false,
lastEpisodeCheckInput: null
}
},
watch: {
lastEpisodeCheck: {
handler(newVal) {
if (newVal) {
this.setLastEpisodeCheckInput()
}
}
} }
}, },
computed: { computed: {
userIsAdminOrUp() {
return this.$store.getters['user/getIsAdminOrUp']
},
autoDownloadEpisodes() { autoDownloadEpisodes() {
return !!this.media.autoDownloadEpisodes return !!this.media.autoDownloadEpisodes
}, },
@ -72,8 +85,22 @@ export default {
} }
}, },
methods: { methods: {
checkForNewEpisodes() { async checkForNewEpisodes() {
if (this.$refs.lastCheckInput) {
this.$refs.lastCheckInput.blur()
}
this.checkingNewEpisodes = true this.checkingNewEpisodes = true
const lastEpisodeCheck = new Date(this.lastEpisodeCheckInput).valueOf()
// If last episode check changed then update it first
if (lastEpisodeCheck && lastEpisodeCheck !== this.lastEpisodeCheck) {
var updateResult = await this.$axios.$patch(`/api/items/${this.libraryItemId}/media`, { lastEpisodeCheck }).catch((error) => {
console.error('Failed to update', error)
return false
})
console.log('updateResult', updateResult)
}
this.$axios this.$axios
.$get(`/api/podcasts/${this.libraryItemId}/checknew`) .$get(`/api/podcasts/${this.libraryItemId}/checknew`)
.then((response) => { .then((response) => {
@ -91,7 +118,13 @@ export default {
this.$toast.error(errorMsg) this.$toast.error(errorMsg)
this.checkingNewEpisodes = false this.checkingNewEpisodes = false
}) })
},
setLastEpisodeCheckInput() {
this.lastEpisodeCheckInput = this.lastEpisodeCheck ? this.$formatDate(this.lastEpisodeCheck, "yyyy-MM-dd'T'HH:mm") : null
} }
},
mounted() {
this.setLastEpisodeCheckInput()
} }
} }
</script> </script>

View File

@ -150,7 +150,8 @@
<ui-icon-btn icon="collections_bookmark" class="mx-0.5" outlined @click="collectionsClick" /> <ui-icon-btn icon="collections_bookmark" class="mx-0.5" outlined @click="collectionsClick" />
</ui-tooltip> </ui-tooltip>
<ui-tooltip v-if="isPodcast" text="Find Episodes" direction="top"> <!-- Only admin or root user can download new episodes -->
<ui-tooltip v-if="isPodcast && userIsAdminOrUp" text="Find Episodes" direction="top">
<ui-icon-btn icon="search" class="mx-0.5" :loading="fetchingRSSFeed" outlined @click="findEpisodesClick" /> <ui-icon-btn icon="search" class="mx-0.5" :loading="fetchingRSSFeed" outlined @click="findEpisodesClick" />
</ui-tooltip> </ui-tooltip>
</div> </div>
@ -210,6 +211,9 @@ export default {
} }
}, },
computed: { computed: {
userIsAdminOrUp() {
return this.$store.getters['user/getIsAdminOrUp']
},
isFile() { isFile() {
return this.libraryItem.isFile return this.libraryItem.isFile
}, },

View File

@ -16,6 +16,7 @@ export const state = () => ({
export const getters = { export const getters = {
getIsRoot: (state) => state.user && state.user.type === 'root', getIsRoot: (state) => state.user && state.user.type === 'root',
getIsAdminOrUp: (state) => state.user && (state.user.type === 'admin' || state.user.type === 'root'),
getToken: (state) => { getToken: (state) => {
return state.user ? state.user.token : null return state.user ? state.user.token : null
}, },

View File

@ -124,7 +124,7 @@ class PodcastController {
return res.status(500).send('Podcast has no rss feed url') return res.status(500).send('Podcast has no rss feed url')
} }
var newEpisodes = await this.podcastManager.checkPodcastForNewEpisodes(libraryItem) var newEpisodes = await this.podcastManager.checkAndDownloadNewEpisodes(libraryItem)
res.json({ res.json({
episodes: newEpisodes || [] episodes: newEpisodes || []
}) })

View File

@ -208,8 +208,27 @@ class PodcastManager {
} }
// Filter new and not already has // Filter new and not already has
var newEpisodes = feed.episodes.filter(ep => ep.publishedAt > podcastLibraryItem.media.lastEpisodeCheck && !podcastLibraryItem.media.checkHasEpisodeByFeedUrl(ep.enclosure.url)) var newEpisodes = feed.episodes.filter(ep => ep.publishedAt > podcastLibraryItem.media.lastEpisodeCheck && !podcastLibraryItem.media.checkHasEpisodeByFeedUrl(ep.enclosure.url))
// Max new episodes for safety = 2 // Max new episodes for safety = 3
newEpisodes = newEpisodes.slice(0, 2) newEpisodes = newEpisodes.slice(0, 3)
return newEpisodes
}
async checkAndDownloadNewEpisodes(libraryItem) {
const lastEpisodeCheckDate = new Date(libraryItem.media.lastEpisodeCheck || 0)
Logger.info(`[PodcastManager] checkAndDownloadNewEpisodes for "${libraryItem.media.metadata.title}" - Last episode check: ${lastEpisodeCheckDate}`)
var newEpisodes = await this.checkPodcastForNewEpisodes(libraryItem)
if (newEpisodes.length) {
Logger.info(`[PodcastManager] Found ${newEpisodes.length} new episodes for podcast "${libraryItem.media.metadata.title}" - starting download`)
this.downloadPodcastEpisodes(libraryItem, newEpisodes)
} else {
Logger.info(`[PodcastManager] No new episodes found for podcast "${libraryItem.media.metadata.title}"`)
}
libraryItem.media.lastEpisodeCheck = Date.now()
libraryItem.updatedAt = Date.now()
await this.db.updateLibraryItem(libraryItem)
this.emitter('item_updated', libraryItem.toJSONExpanded())
return newEpisodes return newEpisodes
} }

View File

@ -30,6 +30,9 @@ class User {
get isRoot() { get isRoot() {
return this.type === 'root' return this.type === 'root'
} }
get isAdmin() {
return this.type === 'admin'
}
get canDelete() { get canDelete() {
return !!this.permissions.delete && this.isActive return !!this.permissions.delete && this.isActive
} }