mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-03 00:06:46 +01:00
Add download queue
This commit is contained in:
parent
f0edea5d52
commit
34ac972130
@ -86,6 +86,14 @@
|
||||
<div v-show="isPlaylistsPage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
|
||||
</nuxt-link>
|
||||
|
||||
<nuxt-link v-if="isPodcastLibrary && userIsAdminOrUp" :to="`/library/${currentLibraryId}/podcast/download-queue`" 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="isPodcastDownloadQueuePage ? 'bg-primary bg-opacity-80' : 'bg-bg bg-opacity-60'">
|
||||
<span class="material-icons text-2xl">file_download</span>
|
||||
|
||||
<p class="pt-1.5 text-center leading-4" style="font-size: 0.9rem">{{ $strings.ButtonDownloadQueue }}</p>
|
||||
|
||||
<div v-show="isPodcastDownloadQueuePage" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
|
||||
</nuxt-link>
|
||||
|
||||
<nuxt-link v-if="numIssues" :to="`/library/${currentLibraryId}/bookshelf?filter=issues`" 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-opacity-40 cursor-pointer relative" :class="showingIssues ? 'bg-error bg-opacity-40' : ' bg-error bg-opacity-20'">
|
||||
<span class="material-icons text-2xl">warning</span>
|
||||
|
||||
@ -149,6 +157,9 @@ export default {
|
||||
isMusicLibrary() {
|
||||
return this.currentLibraryMediaType === 'music'
|
||||
},
|
||||
isPodcastDownloadQueuePage() {
|
||||
return this.$route.name === 'library-library-podcast-download-queue'
|
||||
},
|
||||
isPodcastSearchPage() {
|
||||
return this.$route.name === 'library-library-podcast-search'
|
||||
},
|
||||
@ -212,4 +223,4 @@ export default {
|
||||
},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
@ -11,12 +11,15 @@
|
||||
</nuxt-link>
|
||||
<div v-if="!playerHandler.isVideo" class="text-gray-400 flex items-center">
|
||||
<span class="material-icons text-sm">person</span>
|
||||
<p v-if="podcastAuthor" class="pl-1 sm:pl-1.5 text-xs sm:text-base">{{ podcastAuthor }}</p>
|
||||
<p v-else-if="musicArtists" class="pl-1 sm:pl-1.5 text-xs sm:text-base">{{ musicArtists }}</p>
|
||||
<p v-else-if="authors.length" class="pl-1 sm:pl-1.5 text-xs sm:text-base">
|
||||
<nuxt-link v-for="(author, index) in authors" :key="index" :to="`/author/${author.id}`" class="hover:underline">{{ author.name }}<span v-if="index < authors.length - 1">, </span></nuxt-link>
|
||||
</p>
|
||||
<p v-else class="text-xs sm:text-base cursor-pointer pl-1 sm:pl-1.5">{{ $strings.LabelUnknown }}</p>
|
||||
<div class="flex items-center">
|
||||
<div v-if="podcastAuthor" class="pl-1 sm:pl-1.5 text-xs sm:text-base">{{ podcastAuthor }}</div>
|
||||
<div v-else-if="musicArtists" class="pl-1 sm:pl-1.5 text-xs sm:text-base">{{ musicArtists }}</div>
|
||||
<div v-else-if="authors.length" class="pl-1 sm:pl-1.5 text-xs sm:text-base">
|
||||
<nuxt-link v-for="(author, index) in authors" :key="index" :to="`/author/${author.id}`" class="hover:underline">{{ author.name }}<span v-if="index < authors.length - 1">, </span></nuxt-link>
|
||||
</div>
|
||||
<div v-else class="text-xs sm:text-base cursor-pointer pl-1 sm:pl-1.5">{{ $strings.LabelUnknown }}</div>
|
||||
<widgets-explicit-indicator :explicit="isExplicit"></widgets-explicit-indicator>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-gray-400 flex items-center">
|
||||
@ -129,6 +132,9 @@ export default {
|
||||
isMusic() {
|
||||
return this.streamLibraryItem ? this.streamLibraryItem.mediaType === 'music' : false
|
||||
},
|
||||
isExplicit() {
|
||||
return this.mediaMetadata.explicit || false
|
||||
},
|
||||
mediaMetadata() {
|
||||
return this.media.metadata || {}
|
||||
},
|
||||
@ -474,4 +480,4 @@ export default {
|
||||
#streamContainer {
|
||||
box-shadow: 0px -6px 8px #1111113f;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -19,8 +19,15 @@
|
||||
<ui-checkbox v-else v-model="selectedEpisodes[String(index)]" small checkbox-bg="primary" border-color="gray-600" />
|
||||
</div>
|
||||
<div class="px-8 py-2">
|
||||
<p v-if="episode.episode" class="font-semibold text-gray-200">#{{ episode.episode }}</p>
|
||||
<p class="break-words mb-1">{{ episode.title }}</p>
|
||||
<div class="flex items-center font-semibold text-gray-200">
|
||||
<div v-if="episode.season || episode.episode">#</div>
|
||||
<div v-if="episode.season">{{ episode.season }}x</div>
|
||||
<div v-if="episode.episode">{{ episode.episode }}</div>
|
||||
</div>
|
||||
<div class="flex items-center mb-1">
|
||||
<div class="break-words">{{ episode.title }}</div>
|
||||
<widgets-podcast-type-indicator :type="episode.episodeType" />
|
||||
</div>
|
||||
<p v-if="episode.subtitle" class="break-words mb-1 text-sm text-gray-300 episode-subtitle">{{ episode.subtitle }}</p>
|
||||
<p class="text-xs text-gray-300">Published {{ episode.publishedAt ? $dateDistanceFromNow(episode.publishedAt) : 'Unknown' }}</p>
|
||||
</div>
|
||||
|
68
client/components/tables/podcast/DownloadQueueTable.vue
Normal file
68
client/components/tables/podcast/DownloadQueueTable.vue
Normal file
@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="w-full my-2">
|
||||
<div class="w-full bg-primary px-4 md:px-6 py-2 flex items-center">
|
||||
<p class="pr-2 md:pr-4">{{ $strings.HeaderDownloadQueue }}</p>
|
||||
<div class="h-5 md:h-7 w-5 md:w-7 rounded-full bg-white bg-opacity-10 flex items-center justify-center">
|
||||
<span class="text-sm font-mono">{{ queue.length }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="slide">
|
||||
<div class="w-full">
|
||||
<table class="text-sm tracksTable">
|
||||
<tr>
|
||||
<th class="text-left px-4">{{ $strings.LabelPodcast }}</th>
|
||||
<th class="text-left w-32 min-w-32">{{ $strings.LabelEpisode }}</th>
|
||||
<th class="text-left px-4">{{ $strings.LabelEpisodeTitle }}</th>
|
||||
<th class="text-left px-4 w-48">{{ $strings.LabelPubDate }}</th>
|
||||
</tr>
|
||||
<template v-for="downloadQueued in queue">
|
||||
<tr :key="downloadQueued.id">
|
||||
<td class="px-4">
|
||||
<div class="flex items-center">
|
||||
<nuxt-link :to="`/item/${downloadQueued.libraryItemId}`" class="text-sm text-gray-200 hover:underline">{{ downloadQueued.podcastTitle }}</nuxt-link>
|
||||
<widgets-explicit-indicator :explicit="downloadQueued.podcastExplicit" />
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center">
|
||||
<div v-if="downloadQueued.season">{{ downloadQueued.season }}x</div>
|
||||
<div v-if="downloadQueued.episode">{{ downloadQueued.episode }}</div>
|
||||
<widgets-podcast-type-indicator :type="downloadQueued.episodeType" />
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4">
|
||||
{{ downloadQueued.episodeDisplayTitle }}
|
||||
</td>
|
||||
<td class="text-xs">
|
||||
<div class="flex items-center">
|
||||
<p>{{ $dateDistanceFromNow(downloadQueued.publishedAt) }}</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</table>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
queue: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
libraryItemId: String
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
mounted() {
|
||||
}
|
||||
}
|
||||
</script>
|
@ -2,9 +2,10 @@
|
||||
<div class="w-full px-2 py-3 overflow-hidden relative border-b border-white border-opacity-10" @mouseover="mouseover" @mouseleave="mouseleave">
|
||||
<div v-if="episode" class="flex items-center cursor-pointer" :class="{ 'opacity-70': isSelected || selectionMode }" @click="clickedEpisode">
|
||||
<div class="flex-grow px-2">
|
||||
<p class="text-sm font-semibold">
|
||||
{{ title }}
|
||||
</p>
|
||||
<div class="flex items-center">
|
||||
<span class="text-sm font-semibold">{{ title }}</span>
|
||||
<widgets-podcast-type-indicator :type="episode.episodeType" />
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-gray-200 episode-subtitle mt-1.5 mb-0.5">{{ subtitle }}</p>
|
||||
|
||||
@ -205,4 +206,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
31
client/components/widgets/PodcastTypeIndicator.vue
Normal file
31
client/components/widgets/PodcastTypeIndicator.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="type == 'bonus'">
|
||||
<ui-tooltip text="Bonus" direction="top">
|
||||
<span class="material-icons ml-1" style="font-size: 0.8rem">local_play</span>
|
||||
</ui-tooltip>
|
||||
</template>
|
||||
<template v-if="type == 'trailer'">
|
||||
<ui-tooltip text="Trailer" direction="top">
|
||||
<span class="material-icons ml-1" style="font-size: 0.8rem">local_movies</span>
|
||||
</ui-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'full'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {},
|
||||
methods: {},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
125
client/pages/library/_library/podcast/download-queue.vue
Normal file
125
client/pages/library/_library/podcast/download-queue.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div class="page" :class="streamLibraryItem ? 'streaming' : ''">
|
||||
<app-book-shelf-toolbar page="podcast-search" />
|
||||
|
||||
<div id="bookshelf" class="w-full overflow-y-auto px-2 py-6 sm:px-4 md:p-12 relative">
|
||||
|
||||
<div class="w-full max-w-3xl mx-auto py-4">
|
||||
<p class="text-xl mb-2 font-semibold px-4 md:px-0">{{ $strings.HeaderCurrentDownloads }}</p>
|
||||
<p v-if="!episodesDownloading.length" class="text-center text-xl">{{ $strings.MessageNoDownloadsInProgress }}</p>
|
||||
<template v-for="episode in episodesDownloading">
|
||||
<div :key="episode.id" class="flex py-5 relative">
|
||||
<covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](episode.libraryItemId)" :width="96" :book-cover-aspect-ratio="bookCoverAspectRatio" :show-resolution="false" class="hidden md:block" />
|
||||
<div class="flex-grow pl-4 max-w-2xl">
|
||||
<!-- mobile -->
|
||||
<div class="flex md:hidden mb-2">
|
||||
<covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](episode.libraryItemId)" :width="48" :book-cover-aspect-ratio="bookCoverAspectRatio" :show-resolution="false" class="md:hidden" />
|
||||
<div class="flex-grow px-2">
|
||||
<div class="flex items-center">
|
||||
<nuxt-link :to="`/item/${episode.libraryItemId}`" class="text-sm text-gray-200 hover:underline">{{ episode.podcastTitle }}</nuxt-link>
|
||||
<widgets-explicit-indicator :explicit="episode.podcastExplicit" />
|
||||
</div>
|
||||
<p class="text-xs text-gray-300 mb-1">{{ $dateDistanceFromNow(episode.publishedAt) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- desktop -->
|
||||
<div class="hidden md:block">
|
||||
<div class="flex items-center">
|
||||
<nuxt-link :to="`/item/${episode.libraryItemId}`" class="text-sm text-gray-200 hover:underline">{{ episode.podcastTitle }}</nuxt-link>
|
||||
<widgets-explicit-indicator :explicit="episode.podcastExplicit" />
|
||||
</div>
|
||||
<p class="text-xs text-gray-300 mb-1">{{ $dateDistanceFromNow(episode.publishedAt) }}</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center font-semibold text-gray-200">
|
||||
<div v-if="episode.season || episode.episode">#</div>
|
||||
<div v-if="episode.season">{{ episode.season }}x</div>
|
||||
<div v-if="episode.episode">{{ episode.episode }}</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center mb-2">
|
||||
<span class="font-semibold text-sm md:text-base">{{ episode.episodeDisplayTitle }}</span>
|
||||
<widgets-podcast-type-indicator :type="episode.episodeType" />
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-gray-200 mb-4">{{ episode.subtitle }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<tables-podcast-download-queue-table :queue="episodeDownloadsQueued"></tables-podcast-download-queue-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import DownloadQueueTable from "~/components/tables/podcast/DownloadQueueTable.vue";
|
||||
|
||||
export default {
|
||||
components: {DownloadQueueTable},
|
||||
data() {
|
||||
return {
|
||||
episodesDownloading: [],
|
||||
episodeDownloadsQueued: [],
|
||||
processing: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
bookCoverAspectRatio() {
|
||||
return this.$store.getters['libraries/getBookCoverAspectRatio']
|
||||
},
|
||||
streamLibraryItem() {
|
||||
return this.$store.state.streamLibraryItem
|
||||
},
|
||||
currentLibraryId() {
|
||||
return this.$store.state.libraries.currentLibraryId
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
episodeDownloadQueued(episodeDownload) {
|
||||
if (episodeDownload.libraryId === this.currentLibraryId) {
|
||||
this.episodeDownloadsQueued.push(episodeDownload)
|
||||
}
|
||||
},
|
||||
episodeDownloadStarted(episodeDownload) {
|
||||
if (episodeDownload.libraryId === this.currentLibraryId) {
|
||||
this.episodeDownloadsQueued = this.episodeDownloadsQueued.filter((d) => d.id !== episodeDownload.id)
|
||||
this.episodesDownloading.push(episodeDownload)
|
||||
}
|
||||
},
|
||||
episodeDownloadFinished(episodeDownload) {
|
||||
if (episodeDownload.libraryId === this.currentLibraryId) {
|
||||
this.episodeDownloadsQueued = this.episodeDownloadsQueued.filter((d) => d.id !== episodeDownload.id)
|
||||
this.episodesDownloading = this.episodesDownloading.filter((d) => d.id !== episodeDownload.id)
|
||||
}
|
||||
},
|
||||
downloadQueueUpdated(downloadQueue) {
|
||||
this.episodeDownloadsQueued = downloadQueue.filter((q) => q.libraryId == this.currentLibraryId)
|
||||
},
|
||||
async loadInitialDownloadQueue() {
|
||||
this.processing = true
|
||||
const queuePayload = await this.$axios.$get(`/api/libraries/${this.currentLibraryId}/downloads`).catch((error) => {
|
||||
console.error('Failed to get download queue', error)
|
||||
this.$toast.error('Failed to get download queue')
|
||||
return null
|
||||
})
|
||||
this.processing = false
|
||||
console.log('Episodes', queuePayload)
|
||||
this.episodeDownloadsQueued = queuePayload || []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadInitialDownloadQueue()
|
||||
this.$root.socket.on('episode_download_queued', this.episodeDownloadQueued)
|
||||
this.$root.socket.on('episode_download_started', this.episodeDownloadStarted)
|
||||
this.$root.socket.on('episode_download_finished', this.episodeDownloadFinished)
|
||||
this.$root.socket.on('download_queue_updated', this.downloadQueueUpdated)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$root.socket.off('episode_download_queued', this.episodeDownloadQueued)
|
||||
this.$root.socket.off('episode_download_started', this.episodeDownloadStarted)
|
||||
this.$root.socket.off('episode_download_finished', this.episodeDownloadFinished)
|
||||
this.$root.socket.off('download_queue_updated', this.downloadQueueUpdated)
|
||||
}
|
||||
}
|
||||
</script>
|
@ -30,7 +30,16 @@
|
||||
<p class="text-xs text-gray-300 mb-1">{{ $dateDistanceFromNow(episode.publishedAt) }}</p>
|
||||
</div>
|
||||
|
||||
<p class="font-semibold mb-2 text-sm md:text-base">{{ episode.title }}</p>
|
||||
<div class="flex items-center font-semibold text-gray-200">
|
||||
<div v-if="episode.season || episode.episode">#</div>
|
||||
<div v-if="episode.season">{{ episode.season }}x</div>
|
||||
<div v-if="episode.episode">{{ episode.episode }}</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center mb-2">
|
||||
<div class="font-semibold text-sm md:text-base">{{ episode.title }}</div>
|
||||
<widgets-podcast-type-indicator :type="episode.episodeType" />
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-gray-200 mb-4">{{ episode.subtitle }}</p>
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
"ButtonCreate": "Erstellen",
|
||||
"ButtonCreateBackup": "Sicherung erstellen",
|
||||
"ButtonDelete": "Löschen",
|
||||
"ButtonDownloadQueue": "Queue",
|
||||
"ButtonEdit": "Bearbeiten",
|
||||
"ButtonEditChapters": "Kapitel bearbeiten",
|
||||
"ButtonEditPodcast": "Podcast bearbeiten",
|
||||
@ -92,7 +93,9 @@
|
||||
"HeaderCollection": "Sammlungen",
|
||||
"HeaderCollectionItems": "Sammlungseinträge",
|
||||
"HeaderCover": "Titelbild",
|
||||
"HeaderCurrentDownloads": "Current Downloads",
|
||||
"HeaderDetails": "Details",
|
||||
"HeaderDownloadQueue": "Download Queue",
|
||||
"HeaderEpisodes": "Episoden",
|
||||
"HeaderFiles": "Dateien",
|
||||
"HeaderFindChapters": "Kapitel suchen",
|
||||
@ -493,6 +496,8 @@
|
||||
"MessageNoCollections": "Keine Sammlungen",
|
||||
"MessageNoCoversFound": "Keine Titelbilder gefunden",
|
||||
"MessageNoDescription": "Keine Beschreibung",
|
||||
"MessageNoDownloadsQueued": "No downloads queued",
|
||||
"MessageNoDownloadsInProgress": "No downloads currently in progress",
|
||||
"MessageNoEpisodeMatchesFound": "Keine Episodenübereinstimmungen gefunden",
|
||||
"MessageNoEpisodes": "Keine Episoden",
|
||||
"MessageNoFoldersAvailable": "Keine Ordner verfügbar",
|
||||
@ -623,4 +628,4 @@
|
||||
"ToastSocketFailedToConnect": "Verbindung zum WebSocket fehlgeschlagen",
|
||||
"ToastUserDeleteFailed": "Benutzer konnte nicht gelöscht werden",
|
||||
"ToastUserDeleteSuccess": "Benutzer gelöscht"
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
"ButtonCreate": "Create",
|
||||
"ButtonCreateBackup": "Create Backup",
|
||||
"ButtonDelete": "Delete",
|
||||
"ButtonDownloadQueue": "Queue",
|
||||
"ButtonEdit": "Edit",
|
||||
"ButtonEditChapters": "Edit Chapters",
|
||||
"ButtonEditPodcast": "Edit Podcast",
|
||||
@ -92,7 +93,9 @@
|
||||
"HeaderCollection": "Collection",
|
||||
"HeaderCollectionItems": "Collection Items",
|
||||
"HeaderCover": "Cover",
|
||||
"HeaderCurrentDownloads": "Current Downloads",
|
||||
"HeaderDetails": "Details",
|
||||
"HeaderDownloadQueue": "Download Queue",
|
||||
"HeaderEpisodes": "Episodes",
|
||||
"HeaderFiles": "Files",
|
||||
"HeaderFindChapters": "Find Chapters",
|
||||
@ -493,6 +496,8 @@
|
||||
"MessageNoCollections": "No Collections",
|
||||
"MessageNoCoversFound": "No Covers Found",
|
||||
"MessageNoDescription": "No description",
|
||||
"MessageNoDownloadsQueued": "No downloads queued",
|
||||
"MessageNoDownloadsInProgress": "No downloads currently in progress",
|
||||
"MessageNoEpisodeMatchesFound": "No episode matches found",
|
||||
"MessageNoEpisodes": "No Episodes",
|
||||
"MessageNoFoldersAvailable": "No Folders Available",
|
||||
|
@ -20,6 +20,7 @@
|
||||
"ButtonCreate": "Create",
|
||||
"ButtonCreateBackup": "Create Backup",
|
||||
"ButtonDelete": "Delete",
|
||||
"ButtonDownloadQueue": "Queue",
|
||||
"ButtonEdit": "Edit",
|
||||
"ButtonEditChapters": "Edit Chapters",
|
||||
"ButtonEditPodcast": "Edit Podcast",
|
||||
@ -92,7 +93,9 @@
|
||||
"HeaderCollection": "Collection",
|
||||
"HeaderCollectionItems": "Collection Items",
|
||||
"HeaderCover": "Cover",
|
||||
"HeaderCurrentDownloads": "Current Downloads",
|
||||
"HeaderDetails": "Details",
|
||||
"HeaderDownloadQueue": "Download Queue",
|
||||
"HeaderEpisodes": "Episodes",
|
||||
"HeaderFiles": "Files",
|
||||
"HeaderFindChapters": "Find Chapters",
|
||||
@ -493,6 +496,8 @@
|
||||
"MessageNoCollections": "No Collections",
|
||||
"MessageNoCoversFound": "No Covers Found",
|
||||
"MessageNoDescription": "No description",
|
||||
"MessageNoDownloadsQueued": "No downloads queued",
|
||||
"MessageNoDownloadsInProgress": "No downloads currently in progress",
|
||||
"MessageNoEpisodeMatchesFound": "No episode matches found",
|
||||
"MessageNoEpisodes": "No Episodes",
|
||||
"MessageNoFoldersAvailable": "No Folders Available",
|
||||
|
@ -20,6 +20,7 @@
|
||||
"ButtonCreate": "Créer",
|
||||
"ButtonCreateBackup": "Créer une sauvegarde",
|
||||
"ButtonDelete": "Effacer",
|
||||
"ButtonDownloadQueue": "Queue",
|
||||
"ButtonEdit": "Modifier",
|
||||
"ButtonEditChapters": "Modifier les chapitres",
|
||||
"ButtonEditPodcast": "Modifier les podcasts",
|
||||
@ -92,7 +93,9 @@
|
||||
"HeaderCollection": "Collection",
|
||||
"HeaderCollectionItems": "Entrées de la Collection",
|
||||
"HeaderCover": "Couverture",
|
||||
"HeaderCurrentDownloads": "Current Downloads",
|
||||
"HeaderDetails": "Détails",
|
||||
"HeaderDownloadQueue": "Download Queue",
|
||||
"HeaderEpisodes": "Épisodes",
|
||||
"HeaderFiles": "Fichiers",
|
||||
"HeaderFindChapters": "Trouver les chapitres",
|
||||
@ -493,6 +496,8 @@
|
||||
"MessageNoCollections": "Pas de collections",
|
||||
"MessageNoCoversFound": "Aucune couverture trouvée",
|
||||
"MessageNoDescription": "Pas de description",
|
||||
"MessageNoDownloadsQueued": "No downloads queued",
|
||||
"MessageNoDownloadsInProgress": "No downloads currently in progress",
|
||||
"MessageNoEpisodeMatchesFound": "Pas de correspondance d'épisode trouvée",
|
||||
"MessageNoEpisodes": "Aucun épisode",
|
||||
"MessageNoFoldersAvailable": "Aucun dossier disponible",
|
||||
|
@ -20,6 +20,7 @@
|
||||
"ButtonCreate": "Napravi",
|
||||
"ButtonCreateBackup": "Napravi backup",
|
||||
"ButtonDelete": "Obriši",
|
||||
"ButtonDownloadQueue": "Queue",
|
||||
"ButtonEdit": "Edit",
|
||||
"ButtonEditChapters": "Uredi poglavlja",
|
||||
"ButtonEditPodcast": "Uredi podcast",
|
||||
@ -92,7 +93,9 @@
|
||||
"HeaderCollection": "Kolekcija",
|
||||
"HeaderCollectionItems": "Stvari u kolekciji",
|
||||
"HeaderCover": "Cover",
|
||||
"HeaderCurrentDownloads": "Current Downloads",
|
||||
"HeaderDetails": "Detalji",
|
||||
"HeaderDownloadQueue": "Download Queue",
|
||||
"HeaderEpisodes": "Epizode",
|
||||
"HeaderFiles": "Datoteke",
|
||||
"HeaderFindChapters": "Pronađi poglavlja",
|
||||
@ -493,6 +496,8 @@
|
||||
"MessageNoCollections": "Nema kolekcija",
|
||||
"MessageNoCoversFound": "Covers nisu pronađeni",
|
||||
"MessageNoDescription": "Nema opisa",
|
||||
"MessageNoDownloadsQueued": "No downloads queued",
|
||||
"MessageNoDownloadsInProgress": "No downloads currently in progress",
|
||||
"MessageNoEpisodeMatchesFound": "Nijedna epizoda pronađena",
|
||||
"MessageNoEpisodes": "Nema epizoda",
|
||||
"MessageNoFoldersAvailable": "Nema dostupnih foldera",
|
||||
|
@ -20,6 +20,7 @@
|
||||
"ButtonCreate": "Crea",
|
||||
"ButtonCreateBackup": "Crea un Backup",
|
||||
"ButtonDelete": "Elimina",
|
||||
"ButtonDownloadQueue": "Queue",
|
||||
"ButtonEdit": "Edit",
|
||||
"ButtonEditChapters": "Modifica Capitoli",
|
||||
"ButtonEditPodcast": "Modifica Podcast",
|
||||
@ -92,7 +93,9 @@
|
||||
"HeaderCollection": "Raccolta",
|
||||
"HeaderCollectionItems": "Elementi della Raccolta",
|
||||
"HeaderCover": "Cover",
|
||||
"HeaderCurrentDownloads": "Current Downloads",
|
||||
"HeaderDetails": "Dettagli",
|
||||
"HeaderDownloadQueue": "Download Queue",
|
||||
"HeaderEpisodes": "Episodi",
|
||||
"HeaderFiles": "File",
|
||||
"HeaderFindChapters": "Trova Capitoli",
|
||||
@ -493,6 +496,8 @@
|
||||
"MessageNoCollections": "Nessuna Raccolta",
|
||||
"MessageNoCoversFound": "Nessuna Cover Trovata",
|
||||
"MessageNoDescription": "Nessuna descrizione",
|
||||
"MessageNoDownloadsQueued": "No downloads queued",
|
||||
"MessageNoDownloadsInProgress": "No downloads currently in progress",
|
||||
"MessageNoEpisodeMatchesFound": "Nessun episodio corrispondente trovato",
|
||||
"MessageNoEpisodes": "Nessun Episodio",
|
||||
"MessageNoFoldersAvailable": "Nessuna Cartella disponibile",
|
||||
|
@ -20,6 +20,7 @@
|
||||
"ButtonCreate": "Utwórz",
|
||||
"ButtonCreateBackup": "Utwórz kopię zapasową",
|
||||
"ButtonDelete": "Usuń",
|
||||
"ButtonDownloadQueue": "Queue",
|
||||
"ButtonEdit": "Edit",
|
||||
"ButtonEditChapters": "Edytuj rozdziały",
|
||||
"ButtonEditPodcast": "Edytuj podcast",
|
||||
@ -92,7 +93,9 @@
|
||||
"HeaderCollection": "Kolekcja",
|
||||
"HeaderCollectionItems": "Elementy kolekcji",
|
||||
"HeaderCover": "Okładka",
|
||||
"HeaderCurrentDownloads": "Current Downloads",
|
||||
"HeaderDetails": "Szczegóły",
|
||||
"HeaderDownloadQueue": "Download Queue",
|
||||
"HeaderEpisodes": "Rozdziały",
|
||||
"HeaderFiles": "Pliki",
|
||||
"HeaderFindChapters": "Wyszukaj rozdziały",
|
||||
@ -493,6 +496,8 @@
|
||||
"MessageNoCollections": "Brak kolekcji",
|
||||
"MessageNoCoversFound": "Okładki nieznalezione",
|
||||
"MessageNoDescription": "Brak opisu",
|
||||
"MessageNoDownloadsQueued": "No downloads queued",
|
||||
"MessageNoDownloadsInProgress": "No downloads currently in progress",
|
||||
"MessageNoEpisodeMatchesFound": "Nie znaleziono pasujących odcinków",
|
||||
"MessageNoEpisodes": "Brak odcinków",
|
||||
"MessageNoFoldersAvailable": "Brak dostępnych folderów",
|
||||
|
@ -20,6 +20,7 @@
|
||||
"ButtonCreate": "Создать",
|
||||
"ButtonCreateBackup": "Создать бэкап",
|
||||
"ButtonDelete": "Удалить",
|
||||
"ButtonDownloadQueue": "Queue",
|
||||
"ButtonEdit": "Редактировать",
|
||||
"ButtonEditChapters": "Редактировать Главы",
|
||||
"ButtonEditPodcast": "Редактировать Подкаст",
|
||||
@ -92,7 +93,9 @@
|
||||
"HeaderCollection": "Коллекция",
|
||||
"HeaderCollectionItems": "Элементы Коллекции",
|
||||
"HeaderCover": "Обложка",
|
||||
"HeaderCurrentDownloads": "Current Downloads",
|
||||
"HeaderDetails": "Подробности",
|
||||
"HeaderDownloadQueue": "Download Queue",
|
||||
"HeaderEpisodes": "Эпизоды",
|
||||
"HeaderFiles": "Файлы",
|
||||
"HeaderFindChapters": "Найти Главы",
|
||||
@ -493,6 +496,8 @@
|
||||
"MessageNoCollections": "Нет Коллекций",
|
||||
"MessageNoCoversFound": "Обложек не найдено",
|
||||
"MessageNoDescription": "Нет описания",
|
||||
"MessageNoDownloadsQueued": "No downloads queued",
|
||||
"MessageNoDownloadsInProgress": "No downloads currently in progress",
|
||||
"MessageNoEpisodeMatchesFound": "Совпадения эпизодов не найдены",
|
||||
"MessageNoEpisodes": "Нет Эпизодов",
|
||||
"MessageNoFoldersAvailable": "Нет доступных папок",
|
||||
|
@ -20,6 +20,7 @@
|
||||
"ButtonCreate": "创建",
|
||||
"ButtonCreateBackup": "创建备份",
|
||||
"ButtonDelete": "删除",
|
||||
"ButtonDownloadQueue": "Queue",
|
||||
"ButtonEdit": "编辑",
|
||||
"ButtonEditChapters": "编辑章节",
|
||||
"ButtonEditPodcast": "编辑播客",
|
||||
@ -92,7 +93,9 @@
|
||||
"HeaderCollection": "收藏",
|
||||
"HeaderCollectionItems": "收藏项目",
|
||||
"HeaderCover": "封面",
|
||||
"HeaderCurrentDownloads": "Current Downloads",
|
||||
"HeaderDetails": "详情",
|
||||
"HeaderDownloadQueue": "Download Queue",
|
||||
"HeaderEpisodes": "剧集",
|
||||
"HeaderFiles": "文件",
|
||||
"HeaderFindChapters": "查找章节",
|
||||
@ -493,6 +496,8 @@
|
||||
"MessageNoCollections": "没有收藏",
|
||||
"MessageNoCoversFound": "没有找到封面",
|
||||
"MessageNoDescription": "没有描述",
|
||||
"MessageNoDownloadsQueued": "No downloads queued",
|
||||
"MessageNoDownloadsInProgress": "No downloads currently in progress",
|
||||
"MessageNoEpisodeMatchesFound": "没有找到任何剧集匹配项",
|
||||
"MessageNoEpisodes": "没有剧集",
|
||||
"MessageNoFoldersAvailable": "没有可用文件夹",
|
||||
|
@ -82,6 +82,13 @@ class LibraryController {
|
||||
return res.json(req.library)
|
||||
}
|
||||
|
||||
async getDownloadQueue(req, res) {
|
||||
const library = req.library
|
||||
|
||||
let queue = this.podcastManager.getDownloadQueueDetails().filter(q => q.libraryId === library.id)
|
||||
return res.json(queue)
|
||||
}
|
||||
|
||||
async update(req, res) {
|
||||
const library = req.library
|
||||
|
||||
|
@ -56,12 +56,13 @@ class PodcastManager {
|
||||
newPe.setData(ep, index++)
|
||||
newPe.libraryItemId = libraryItem.id
|
||||
var newPeDl = new PodcastEpisodeDownload()
|
||||
newPeDl.setData(newPe, libraryItem, isAutoDownload)
|
||||
newPeDl.setData(newPe, libraryItem, isAutoDownload, libraryItem.libraryId)
|
||||
this.startPodcastEpisodeDownload(newPeDl)
|
||||
})
|
||||
}
|
||||
|
||||
async startPodcastEpisodeDownload(podcastEpisodeDownload) {
|
||||
SocketAuthority.emitter('download_queue_updated', this.getDownloadQueueDetails())
|
||||
if (this.currentDownload) {
|
||||
this.downloadQueue.push(podcastEpisodeDownload)
|
||||
SocketAuthority.emitter('episode_download_queued', podcastEpisodeDownload.toJSONForClient())
|
||||
@ -99,6 +100,7 @@ class PodcastManager {
|
||||
}
|
||||
|
||||
SocketAuthority.emitter('episode_download_finished', this.currentDownload.toJSONForClient())
|
||||
SocketAuthority.emitter('download_queue_updated', this.getDownloadQueueDetails())
|
||||
|
||||
this.watcher.removeIgnoreDir(this.currentDownload.libraryItem.path)
|
||||
this.currentDownload = null
|
||||
@ -329,5 +331,22 @@ class PodcastManager {
|
||||
feeds: rssFeedData
|
||||
}
|
||||
}
|
||||
|
||||
getDownloadQueueDetails() {
|
||||
return this.downloadQueue.map(item => {
|
||||
return {
|
||||
id: item.id,
|
||||
libraryId: item.libraryId || null,
|
||||
libraryItemId: item.libraryItemId || null,
|
||||
podcastTitle: item.libraryItem.media.metadata.title || null,
|
||||
podcastExplicit: item.libraryItem.media.metadata.explicit || false,
|
||||
episodeDisplayTitle: item.podcastEpisode.title || null,
|
||||
season: item.podcastEpisode.season || null,
|
||||
episode: item.podcastEpisode.episode || null,
|
||||
episodeType: item.podcastEpisode.episodeType || 'full',
|
||||
publishedAt: item.podcastEpisode.publishedAt || null
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
module.exports = PodcastManager
|
||||
module.exports = PodcastManager
|
||||
|
@ -8,6 +8,7 @@ class PodcastEpisodeDownload {
|
||||
this.podcastEpisode = null
|
||||
this.url = null
|
||||
this.libraryItem = null
|
||||
this.libraryId = null
|
||||
|
||||
this.isAutoDownload = false
|
||||
this.isDownloading = false
|
||||
@ -25,12 +26,17 @@ class PodcastEpisodeDownload {
|
||||
episodeDisplayTitle: this.podcastEpisode ? this.podcastEpisode.title : null,
|
||||
url: this.url,
|
||||
libraryItemId: this.libraryItem ? this.libraryItem.id : null,
|
||||
libraryId: this.libraryId || null,
|
||||
isDownloading: this.isDownloading,
|
||||
isFinished: this.isFinished,
|
||||
failed: this.failed,
|
||||
startedAt: this.startedAt,
|
||||
createdAt: this.createdAt,
|
||||
finishedAt: this.finishedAt
|
||||
finishedAt: this.finishedAt,
|
||||
season: this.podcastEpisode ? this.podcastEpisode.season : null,
|
||||
episode: this.podcastEpisode ? this.podcastEpisode.episode : null,
|
||||
episodeType: this.podcastEpisode ? this.podcastEpisode.episodeType : 'full',
|
||||
publishedAt: this.podcastEpisode ? this.podcastEpisode.publishedAt : null
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,13 +53,14 @@ class PodcastEpisodeDownload {
|
||||
return this.libraryItem ? this.libraryItem.id : null
|
||||
}
|
||||
|
||||
setData(podcastEpisode, libraryItem, isAutoDownload) {
|
||||
setData(podcastEpisode, libraryItem, isAutoDownload, libraryId) {
|
||||
this.id = getId('epdl')
|
||||
this.podcastEpisode = podcastEpisode
|
||||
this.url = encodeURI(podcastEpisode.enclosure.url)
|
||||
this.libraryItem = libraryItem
|
||||
this.isAutoDownload = isAutoDownload
|
||||
this.createdAt = Date.now()
|
||||
this.libraryId = libraryId
|
||||
}
|
||||
|
||||
setFinished(success) {
|
||||
@ -62,4 +69,4 @@ class PodcastEpisodeDownload {
|
||||
this.failed = !success
|
||||
}
|
||||
}
|
||||
module.exports = PodcastEpisodeDownload
|
||||
module.exports = PodcastEpisodeDownload
|
||||
|
@ -76,6 +76,7 @@ class ApiRouter {
|
||||
|
||||
this.router.get('/libraries/:id/items', LibraryController.middleware.bind(this), LibraryController.getLibraryItems.bind(this))
|
||||
this.router.delete('/libraries/:id/issues', LibraryController.middleware.bind(this), LibraryController.removeLibraryItemsWithIssues.bind(this))
|
||||
this.router.get('/libraries/:id/downloads', LibraryController.middleware.bind(this), LibraryController.getDownloadQueue.bind(this))
|
||||
this.router.get('/libraries/:id/series', LibraryController.middleware.bind(this), LibraryController.getAllSeriesForLibrary.bind(this))
|
||||
this.router.get('/libraries/:id/collections', LibraryController.middleware.bind(this), LibraryController.getCollectionsForLibrary.bind(this))
|
||||
this.router.get('/libraries/:id/playlists', LibraryController.middleware.bind(this), LibraryController.getUserPlaylistsForLibrary.bind(this))
|
||||
@ -553,4 +554,4 @@ class ApiRouter {
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = ApiRouter
|
||||
module.exports = ApiRouter
|
||||
|
Loading…
Reference in New Issue
Block a user