mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Merge pull request #2026 from shawnphoffman/shawn/rss-feeds
Add config page for all RSS feeds
This commit is contained in:
		
						commit
						ec15978e26
					
				| @ -99,6 +99,11 @@ export default { | ||||
|           id: 'config-item-metadata-utils', | ||||
|           title: this.$strings.HeaderItemMetadataUtils, | ||||
|           path: '/config/item-metadata-utils' | ||||
|         }, | ||||
|         { | ||||
|           id: 'config-rss-feeds', | ||||
|           title: this.$strings.HeaderRSSFeeds, | ||||
|           path: '/config/rss-feeds' | ||||
|         } | ||||
|       ] | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										124
									
								
								client/components/modals/rssfeed/ViewFeedModal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								client/components/modals/rssfeed/ViewFeedModal.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,124 @@ | ||||
| <template> | ||||
|   <modals-modal v-model="show" name="rss-feed-view-modal" :processing="processing" :width="700" :height="'unset'"> | ||||
|     <div ref="wrapper" class="px-8 py-6 w-full text-sm rounded-lg bg-bg shadow-lg border border-black-300 relative overflow-hidden"> | ||||
|       <div v-if="feed" class="w-full"> | ||||
|         <p class="text-lg font-semibold mb-4">{{ $strings.HeaderRSSFeedGeneral }}</p> | ||||
| 
 | ||||
|         <div class="w-full relative"> | ||||
|           <ui-text-input v-model="feed.feedUrl" readonly /> | ||||
|           <span class="material-icons absolute right-2 bottom-2 p-0.5 text-base transition-transform duration-100 text-gray-300 hover:text-white transform hover:scale-125 cursor-pointer" @click="copyToClipboard(feed.feedUrl)">content_copy</span> | ||||
|         </div> | ||||
| 
 | ||||
|         <div v-if="feed.meta" class="mt-5"> | ||||
|           <div class="flex py-0.5"> | ||||
|             <div class="w-48"> | ||||
|               <span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelRSSFeedPreventIndexing }}</span> | ||||
|             </div> | ||||
|             <div>{{ feed.meta.preventIndexing ? 'Yes' : 'No' }}</div> | ||||
|           </div> | ||||
|           <div v-if="feed.meta.ownerName" class="flex py-0.5"> | ||||
|             <div class="w-48"> | ||||
|               <span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelRSSFeedCustomOwnerName }}</span> | ||||
|             </div> | ||||
|             <div>{{ feed.meta.ownerName }}</div> | ||||
|           </div> | ||||
|           <div v-if="feed.meta.ownerEmail" class="flex py-0.5"> | ||||
|             <div class="w-48"> | ||||
|               <span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelRSSFeedCustomOwnerEmail }}</span> | ||||
|             </div> | ||||
|             <div>{{ feed.meta.ownerEmail }}</div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <!--  --> | ||||
|         <div class="episodesTable mt-2"> | ||||
|           <div class="bg-primary bg-opacity-40 h-12 header"> | ||||
|             {{ $strings.LabelEpisodeTitle }} | ||||
|           </div> | ||||
|           <div class="scroller"> | ||||
|             <div v-for="episode in feed.episodes" :key="episode.id" class="h-8 text-xs truncate"> | ||||
|               {{ episode.title }} | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </modals-modal> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|   props: { | ||||
|     value: Boolean, | ||||
|     feed: { | ||||
|       type: Object, | ||||
|       default: () => {} | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       processing: false | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     show: { | ||||
|       get() { | ||||
|         return this.value | ||||
|       }, | ||||
|       set(val) { | ||||
|         this.$emit('input', val) | ||||
|       } | ||||
|     }, | ||||
|     _feed() { | ||||
|       return this.feed || {} | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     copyToClipboard(str) { | ||||
|       this.$copyToClipboard(str, this) | ||||
|     } | ||||
|   }, | ||||
|   mounted() {} | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| .episodesTable { | ||||
|   width: 100%; | ||||
|   max-width: 100%; | ||||
|   border: 1px solid #474747; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
| } | ||||
| 
 | ||||
| .episodesTable div.header { | ||||
|   background-color: #272727; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   justify-content: center; | ||||
|   align-items: flex-start; | ||||
|   padding: 4px 8px; | ||||
| } | ||||
| 
 | ||||
| .episodesTable .scroller { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   max-height: 250px; | ||||
|   overflow-x: hidden; | ||||
|   overflow-y: scroll; | ||||
| } | ||||
| 
 | ||||
| .episodesTable .scroller div { | ||||
|   background-color: #373838; | ||||
|   padding: 4px 8px; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: flex-start; | ||||
|   height: 32px; | ||||
|   flex: 0 0 32px; | ||||
| } | ||||
| 
 | ||||
| .episodesTable .scroller div:nth-child(even) { | ||||
|   background-color: #2f2f2f; | ||||
| } | ||||
| </style> | ||||
| 
 | ||||
| @ -55,6 +55,7 @@ export default { | ||||
|         else if (pageName === 'library-stats') return this.$strings.HeaderLibraryStats | ||||
|         else if (pageName === 'users') return this.$strings.HeaderUsers | ||||
|         else if (pageName === 'item-metadata-utils') return this.$strings.HeaderItemMetadataUtils | ||||
|         else if (pageName === 'rss-feeds') return this.$strings.HeaderRSSFeeds | ||||
|         else if (pageName === 'email') return this.$strings.HeaderEmail | ||||
|       } | ||||
|       return this.$strings.HeaderSettings | ||||
|  | ||||
							
								
								
									
										176
									
								
								client/pages/config/rss-feeds.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								client/pages/config/rss-feeds.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <app-settings-content :header-text="$strings.HeaderRSSFeeds"> | ||||
|       <div v-if="feeds.length" class="block max-w-full"> | ||||
|         <table class="rssFeedsTable text-xs"> | ||||
|           <tr class="bg-primary bg-opacity-40 h-12"> | ||||
|             <th class="w-16 min-w-16"></th> | ||||
|             <th class="w-48 max-w-64 min-w-24 text-left truncate">{{ $strings.LabelTitle }}</th> | ||||
|             <th class="w-48 min-w-24 text-left hidden xl:table-cell">{{ $strings.LabelSlug }}</th> | ||||
|             <th class="w-24 min-w-16 text-left hidden md:table-cell">{{ $strings.LabelType }}</th> | ||||
|             <th class="w-16 min-w-16 text-center">{{ $strings.HeaderEpisodes }}</th> | ||||
|             <th class="w-16 min-w-16 text-center hidden lg:table-cell">{{ $strings.LabelRSSFeedPreventIndexing }}</th> | ||||
|             <th class="w-48 min-w-24 flex-grow hidden md:table-cell">{{ $strings.LabelLastUpdate }}</th> | ||||
|             <th class="w-16 text-left"></th> | ||||
|           </tr> | ||||
| 
 | ||||
|           <tr v-for="feed in feeds" :key="feed.id" class="cursor-pointer h-12" @click="showFeed(feed)"> | ||||
|             <!--  --> | ||||
|             <td> | ||||
|               <img :src="coverUrl(feed)" class="h-full w-full" /> | ||||
|             </td> | ||||
|             <!--  --> | ||||
|             <td class="w-48 max-w-64 min-w-24 text-left truncate"> | ||||
|               <p class="truncate">{{ feed.meta.title }}</p> | ||||
|             </td> | ||||
|             <!--  --> | ||||
|             <td class="hidden xl:table-cell"> | ||||
|               <p class="truncate">{{ feed.slug }}</p> | ||||
|             </td> | ||||
|             <!--  --> | ||||
|             <td class="hidden md:table-cell"> | ||||
|               <p class="">{{ getEntityType(feed.entityType) }}</p> | ||||
|             </td> | ||||
|             <!--  --> | ||||
|             <td class="text-center"> | ||||
|               <p class="">{{ feed.episodes.length }}</p> | ||||
|             </td> | ||||
|             <!--  --> | ||||
|             <td class="text-center leading-none hidden lg:table-cell"> | ||||
|               <p v-if="feed.meta.preventIndexing" class=""> | ||||
|                 <span class="material-icons text-2xl">check</span> | ||||
|               </p> | ||||
|             </td> | ||||
|             <!--  --> | ||||
|             <td class="text-center hidden md:table-cell"> | ||||
|               <ui-tooltip v-if="feed.updatedAt" direction="top" :text="$formatDatetime(feed.updatedAt, dateFormat, timeFormat)"> | ||||
|                 <p class="text-gray-200">{{ $dateDistanceFromNow(feed.updatedAt) }}</p> | ||||
|               </ui-tooltip> | ||||
|             </td> | ||||
|             <!--  --> | ||||
|             <td class="text-center"> | ||||
|               <ui-icon-btn icon="delete" class="mx-0.5" :size="7" bg-color="error" outlined @click.stop="deleteFeedClick(feed)" /> | ||||
|             </td> | ||||
|           </tr> | ||||
|         </table> | ||||
|       </div> | ||||
|     </app-settings-content> | ||||
|     <modals-rssfeed-view-feed-modal v-model="showFeedModal" :feed="selectedFeed" /> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|   data() { | ||||
|     return { | ||||
|       showFeedModal: false, | ||||
|       selectedFeed: null, | ||||
|       feeds: [] | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     dateFormat() { | ||||
|       return this.$store.state.serverSettings.dateFormat | ||||
|     }, | ||||
|     timeFormat() { | ||||
|       return this.$store.state.serverSettings.timeFormat | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     showFeed(feed) { | ||||
|       this.selectedFeed = feed | ||||
|       this.showFeedModal = true | ||||
|     }, | ||||
|     deleteFeedClick(feed) { | ||||
|       const payload = { | ||||
|         message: this.$strings.MessageConfirmCloseFeed, | ||||
|         callback: (confirmed) => { | ||||
|           if (confirmed) { | ||||
|             this.deleteFeed(feed) | ||||
|           } | ||||
|         }, | ||||
|         type: 'yesNo' | ||||
|       } | ||||
|       this.$store.commit('globals/setConfirmPrompt', payload) | ||||
|     }, | ||||
|     deleteFeed(feed) { | ||||
|       this.processing = true | ||||
|       this.$axios | ||||
|         .$post(`/api/feeds/${feed.id}/close`) | ||||
|         .then(() => { | ||||
|           this.$toast.success(this.$strings.ToastRSSFeedCloseSuccess) | ||||
|           this.show = false | ||||
|           this.loadFeeds() | ||||
|         }) | ||||
|         .catch((error) => { | ||||
|           console.error('Failed to close RSS feed', error) | ||||
|           this.$toast.error(this.$strings.ToastRSSFeedCloseFailed) | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           this.processing = false | ||||
|         }) | ||||
|     }, | ||||
|     getEntityType(entityType) { | ||||
|       if (entityType === 'libraryItem') return this.$strings.LabelItem | ||||
|       else if (entityType === 'series') return this.$strings.LabelSeries | ||||
|       else if (entityType === 'collection') return this.$strings.LabelCollection | ||||
|       return this.$strings.LabelUnknown | ||||
|     }, | ||||
|     coverUrl(feed) { | ||||
|       if (!feed.coverPath) return `${this.$config.routerBasePath}/Logo.png` | ||||
|       return `${feed.feedUrl}/cover` | ||||
|     }, | ||||
|     async loadFeeds() { | ||||
|       const data = await this.$axios.$get(`/api/feeds`).catch((err) => { | ||||
|         console.error('Failed to load RSS feeds', err) | ||||
|         return null | ||||
|       }) | ||||
|       if (!data) { | ||||
|         this.$toast.error('Failed to load RSS feeds') | ||||
|         return | ||||
|       } | ||||
|       this.feeds = data.feeds | ||||
|     }, | ||||
|     init() { | ||||
|       this.loadFeeds() | ||||
|     } | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.init() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style scoped> | ||||
| .rssFeedsTable { | ||||
|   border-collapse: collapse; | ||||
|   width: 100%; | ||||
|   max-width: 100%; | ||||
|   border: 1px solid #474747; | ||||
| } | ||||
| 
 | ||||
| .rssFeedsTable tr:first-child { | ||||
|   background-color: #272727; | ||||
| } | ||||
| 
 | ||||
| .rssFeedsTable tr:not(:first-child) { | ||||
|   background-color: #373838; | ||||
| } | ||||
| 
 | ||||
| .rssFeedsTable tr:not(:first-child):nth-child(odd) { | ||||
|   background-color: #2f2f2f; | ||||
| } | ||||
| 
 | ||||
| .rssFeedsTable tr:hover:not(:first-child) { | ||||
|   background-color: #474747; | ||||
| } | ||||
| 
 | ||||
| .rssFeedsTable td { | ||||
|   padding: 4px 8px; | ||||
| } | ||||
| 
 | ||||
| .rssFeedsTable th { | ||||
|   padding: 4px 8px; | ||||
|   font-size: 0.75rem; | ||||
| } | ||||
| </style> | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Lösche {0} Episoden", | ||||
|   "HeaderRSSFeedGeneral": "RSS Details", | ||||
|   "HeaderRSSFeedIsOpen": "RSS-Feed ist geöffnet", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Gespeicherte Hörfortschritte", | ||||
|   "HeaderSchedule": "Zeitplan", | ||||
|   "HeaderScheduleLibraryScans": "Automatische Bibliotheksscans", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Player schließen", | ||||
|   "LabelCodec": "Codec", | ||||
|   "LabelCollapseSeries": "Serien zusammenfassen", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Sammlungen", | ||||
|   "LabelComplete": "Vollständig", | ||||
|   "LabelConfirmPassword": "Passwort bestätigen", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Alles anzeigen", | ||||
|   "LabelSize": "Größe", | ||||
|   "LabelSleepTimer": "Einschlaf-Timer", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Start", | ||||
|   "LabelStarted": "Gestartet", | ||||
|   "LabelStartedAt": "Gestartet am", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "Ungültige Kapitelstartzeit: Kapitelanfang < Kapitelanfang vorheriges Kapitel (Kapitelanfang liegt zeitlich vor dem Beginn des vorherigen Kapitels -> Lösung: Kapitelanfang >= Startzeit des vorherigen Kapitels)", | ||||
|   "MessageChapterStartIsAfter": "Ungültige Kapitelstartzeit: Kapitelanfang > Mediumende (Kapitelanfang liegt nach dem Ende des Mediums)", | ||||
|   "MessageCheckingCron": "Überprüfe Cron...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Sind Sie sicher, dass Sie die Sicherung für {0} löschen wollen?", | ||||
|   "MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?", | ||||
|   "MessageConfirmDeleteLibrary": "Sind Sie sicher, dass Sie die Bibliothek \"{0}\" dauerhaft löschen wollen?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Remove {0} Episodes", | ||||
|   "HeaderRSSFeedGeneral": "RSS Details", | ||||
|   "HeaderRSSFeedIsOpen": "RSS Feed is Open", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Saved Media Progress", | ||||
|   "HeaderSchedule": "Schedule", | ||||
|   "HeaderScheduleLibraryScans": "Schedule Automatic Library Scans", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Close player", | ||||
|   "LabelCodec": "Codec", | ||||
|   "LabelCollapseSeries": "Collapse Series", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Collections", | ||||
|   "LabelComplete": "Complete", | ||||
|   "LabelConfirmPassword": "Confirm Password", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Show All", | ||||
|   "LabelSize": "Size", | ||||
|   "LabelSleepTimer": "Sleep timer", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Start", | ||||
|   "LabelStarted": "Started", | ||||
|   "LabelStartedAt": "Started At", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "Invalid start time must be greater than or equal to previous chapter start time", | ||||
|   "MessageChapterStartIsAfter": "Chapter start is after the end of your audiobook", | ||||
|   "MessageCheckingCron": "Checking cron...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Are you sure you want to delete backup for {0}?", | ||||
|   "MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?", | ||||
|   "MessageConfirmDeleteLibrary": "Are you sure you want to permanently delete library \"{0}\"?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Remover {0} Episodios", | ||||
|   "HeaderRSSFeedGeneral": "Detalles RSS", | ||||
|   "HeaderRSSFeedIsOpen": "Fuente RSS esta abierta", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Guardar Progreso de multimedia", | ||||
|   "HeaderSchedule": "Horario", | ||||
|   "HeaderScheduleLibraryScans": "Programar Escaneo Automático de Biblioteca", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Close player", | ||||
|   "LabelCodec": "Codec", | ||||
|   "LabelCollapseSeries": "Colapsar Series", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Colecciones", | ||||
|   "LabelComplete": "Completo", | ||||
|   "LabelConfirmPassword": "Confirmar Contraseña", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Mostrar Todos", | ||||
|   "LabelSize": "Tamaño", | ||||
|   "LabelSleepTimer": "Temporizador para Dormir", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Iniciar", | ||||
|   "LabelStarted": "Indiciado", | ||||
|   "LabelStartedAt": "Iniciado En", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "El tiempo de inicio no es válida debe ser mayor o igual que la hora de inicio del capítulo anterior", | ||||
|   "MessageChapterStartIsAfter": "El comienzo del capítulo es después del final de su audiolibro", | ||||
|   "MessageCheckingCron": "Checking cron...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Esta seguro que desea eliminar el respaldo {0}?", | ||||
|   "MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?", | ||||
|   "MessageConfirmDeleteLibrary": "Esta seguro que desea eliminar permanentemente la biblioteca \"{0}\"?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Suppression de {0} épisodes", | ||||
|   "HeaderRSSFeedGeneral": "Détails de flux RSS", | ||||
|   "HeaderRSSFeedIsOpen": "Le Flux RSS est actif", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Progression de la sauvegarde des médias", | ||||
|   "HeaderSchedule": "Programmation", | ||||
|   "HeaderScheduleLibraryScans": "Analyse automatique de la bibliothèque", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Fermer le lecteur", | ||||
|   "LabelCodec": "Codec", | ||||
|   "LabelCollapseSeries": "Réduire les séries", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Collections", | ||||
|   "LabelComplete": "Complet", | ||||
|   "LabelConfirmPassword": "Confirmer le mot de passe", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Afficher Tout", | ||||
|   "LabelSize": "Taille", | ||||
|   "LabelSleepTimer": "Minuterie", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Démarrer", | ||||
|   "LabelStarted": "Démarré", | ||||
|   "LabelStartedAt": "Démarré à", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "Horodatage invalide car il doit débuter au moins après le précédent chapitre", | ||||
|   "MessageChapterStartIsAfter": "Le premier chapitre est situé au début de votre livre audio", | ||||
|   "MessageCheckingCron": "Vérification du cron…", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Êtes-vous sûr de vouloir supprimer la Sauvegarde de {0} ?", | ||||
|   "MessageConfirmDeleteFile": "Cela Le fichier sera supprimer de votre système. Êtes-vous sûr ?", | ||||
|   "MessageConfirmDeleteLibrary": "Êtes-vous sûr de vouloir supprimer définitivement la bibliothèque « {0} » ?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Remove {0} Episodes", | ||||
|   "HeaderRSSFeedGeneral": "RSS Details", | ||||
|   "HeaderRSSFeedIsOpen": "RSS Feed is Open", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Saved Media Progress", | ||||
|   "HeaderSchedule": "Schedule", | ||||
|   "HeaderScheduleLibraryScans": "Schedule Automatic Library Scans", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Close player", | ||||
|   "LabelCodec": "Codec", | ||||
|   "LabelCollapseSeries": "Collapse Series", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Collections", | ||||
|   "LabelComplete": "Complete", | ||||
|   "LabelConfirmPassword": "Confirm Password", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Show All", | ||||
|   "LabelSize": "Size", | ||||
|   "LabelSleepTimer": "Sleep timer", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Start", | ||||
|   "LabelStarted": "Started", | ||||
|   "LabelStartedAt": "Started At", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "Invalid start time must be greater than or equal to previous chapter start time", | ||||
|   "MessageChapterStartIsAfter": "Chapter start is after the end of your audiobook", | ||||
|   "MessageCheckingCron": "Checking cron...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Are you sure you want to delete backup for {0}?", | ||||
|   "MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?", | ||||
|   "MessageConfirmDeleteLibrary": "Are you sure you want to permanently delete library \"{0}\"?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Remove {0} Episodes", | ||||
|   "HeaderRSSFeedGeneral": "RSS Details", | ||||
|   "HeaderRSSFeedIsOpen": "RSS Feed is Open", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Saved Media Progress", | ||||
|   "HeaderSchedule": "Schedule", | ||||
|   "HeaderScheduleLibraryScans": "Schedule Automatic Library Scans", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Close player", | ||||
|   "LabelCodec": "Codec", | ||||
|   "LabelCollapseSeries": "Collapse Series", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Collections", | ||||
|   "LabelComplete": "Complete", | ||||
|   "LabelConfirmPassword": "Confirm Password", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Show All", | ||||
|   "LabelSize": "Size", | ||||
|   "LabelSleepTimer": "Sleep timer", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Start", | ||||
|   "LabelStarted": "Started", | ||||
|   "LabelStartedAt": "Started At", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "Invalid start time must be greater than or equal to previous chapter start time", | ||||
|   "MessageChapterStartIsAfter": "Chapter start is after the end of your audiobook", | ||||
|   "MessageCheckingCron": "Checking cron...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Are you sure you want to delete backup for {0}?", | ||||
|   "MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?", | ||||
|   "MessageConfirmDeleteLibrary": "Are you sure you want to permanently delete library \"{0}\"?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Ukloni {0} epizoda/-e", | ||||
|   "HeaderRSSFeedGeneral": "RSS Details", | ||||
|   "HeaderRSSFeedIsOpen": "RSS Feed je otvoren", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Spremljen Media Progress", | ||||
|   "HeaderSchedule": "Schedule", | ||||
|   "HeaderScheduleLibraryScans": "Zakaži automatsko skeniranje biblioteke", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Close player", | ||||
|   "LabelCodec": "Codec", | ||||
|   "LabelCollapseSeries": "Collapse Series", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Kolekcije", | ||||
|   "LabelComplete": "Complete", | ||||
|   "LabelConfirmPassword": "Potvrdi lozinku", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Prikaži sve", | ||||
|   "LabelSize": "Veličina", | ||||
|   "LabelSleepTimer": "Sleep timer", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Pokreni", | ||||
|   "LabelStarted": "Pokrenuto", | ||||
|   "LabelStartedAt": "Pokrenuto", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "Invalid start time must be greater than or equal to previous chapter start time", | ||||
|   "MessageChapterStartIsAfter": "Početak poglavlja je nakon kraja audioknjige.", | ||||
|   "MessageCheckingCron": "Provjeravam cron...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Jeste li sigurni da želite obrisati backup za {0}?", | ||||
|   "MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?", | ||||
|   "MessageConfirmDeleteLibrary": "Jeste li sigurni da želite trajno obrisati biblioteku \"{0}\"?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Rimuovi {0} Episodi", | ||||
|   "HeaderRSSFeedGeneral": "RSS Details", | ||||
|   "HeaderRSSFeedIsOpen": "RSS Feed è aperto", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Progressi salvati", | ||||
|   "HeaderSchedule": "Schedula", | ||||
|   "HeaderScheduleLibraryScans": "Schedula la scansione della libreria", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Chiudi player", | ||||
|   "LabelCodec": "Codec", | ||||
|   "LabelCollapseSeries": "Comprimi Serie", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Raccolte", | ||||
|   "LabelComplete": "Completo", | ||||
|   "LabelConfirmPassword": "Conferma Password", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Mostra Tutto", | ||||
|   "LabelSize": "Dimensione", | ||||
|   "LabelSleepTimer": "Sleep timer", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Inizo", | ||||
|   "LabelStarted": "Iniziato", | ||||
|   "LabelStartedAt": "Iniziato al", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "L'ora di inizio non valida deve essere maggiore o uguale all'ora di inizio del capitolo precedente", | ||||
|   "MessageChapterStartIsAfter": "L'inizio del capitolo è dopo la fine del tuo audiolibro", | ||||
|   "MessageCheckingCron": "Controllo cron...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Sei sicuro di voler eliminare il backup {0}?", | ||||
|   "MessageConfirmDeleteFile": "Questo eliminerà il file dal tuo file system. Sei sicuro?", | ||||
|   "MessageConfirmDeleteLibrary": "Sei sicuro di voler eliminare definitivamente la libreria \"{0}\"?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Pašalinti {0} epizodus", | ||||
|   "HeaderRSSFeedGeneral": "RSS informacija", | ||||
|   "HeaderRSSFeedIsOpen": "RSS srautas yra atidarytas", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Išsaugota medijos pažanga", | ||||
|   "HeaderSchedule": "Tvarkaraštis", | ||||
|   "HeaderScheduleLibraryScans": "Nustatyti bibliotekų nuskaitymo tvarkaraštį", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Uždaryti grotuvą", | ||||
|   "LabelCodec": "Kodekas", | ||||
|   "LabelCollapseSeries": "Suskleisti seriją", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Kolekcijos", | ||||
|   "LabelComplete": "Baigta", | ||||
|   "LabelConfirmPassword": "Patvirtinkite slaptažodį", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Rodyti viską", | ||||
|   "LabelSize": "Dydis", | ||||
|   "LabelSleepTimer": "Miego laikmatis", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Pradėti", | ||||
|   "LabelStarted": "Pradėta", | ||||
|   "LabelStartedAt": "Pradėta", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "Netinkamas pradžios laikas. Turi būti didesnis arba lygus ankstesnio skyriaus pradžios laikui", | ||||
|   "MessageChapterStartIsAfter": "Skyriaus pradžia yra po jūsų garso knygos pabaigos", | ||||
|   "MessageCheckingCron": "Tikrinamas cron...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Ar tikrai norite ištrinti atsarginę kopiją, skirtą {0}?", | ||||
|   "MessageConfirmDeleteFile": "Tai ištrins failą iš jūsų failų sistemos. Ar tikrai?", | ||||
|   "MessageConfirmDeleteLibrary": "Ar tikrai norite visam laikui ištrinti biblioteką \"{0}\"?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Verwijder {0} afleveringen", | ||||
|   "HeaderRSSFeedGeneral": "RSS-details", | ||||
|   "HeaderRSSFeedIsOpen": "RSS-feed is open", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Opgeslagen mediavoortgang", | ||||
|   "HeaderSchedule": "Schema", | ||||
|   "HeaderScheduleLibraryScans": "Schema automatische bibliotheekscans", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Sluit speler", | ||||
|   "LabelCodec": "Codec", | ||||
|   "LabelCollapseSeries": "Series inklappen", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Collecties", | ||||
|   "LabelComplete": "Compleet", | ||||
|   "LabelConfirmPassword": "Bevestig wachtwoord", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Toon alle", | ||||
|   "LabelSize": "Grootte", | ||||
|   "LabelSleepTimer": "Slaaptimer", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Start", | ||||
|   "LabelStarted": "Gestart", | ||||
|   "LabelStartedAt": "Gestart op", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "Ongeldig: starttijd moet be groter zijn dan of equal aan starttijd van vorig hoofdstuk", | ||||
|   "MessageChapterStartIsAfter": "Start van hoofdstuk is na het einde van je audioboek", | ||||
|   "MessageCheckingCron": "Cron aan het checken...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Weet je zeker dat je de backup voor {0} wil verwijderen?", | ||||
|   "MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?", | ||||
|   "MessageConfirmDeleteLibrary": "Weet je zeker dat je de bibliotheek \"{0}\" permanent wil verwijderen?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Usuń {0} odcinków", | ||||
|   "HeaderRSSFeedGeneral": "RSS Details", | ||||
|   "HeaderRSSFeedIsOpen": "Kanał RSS jest otwarty", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Zapisany postęp", | ||||
|   "HeaderSchedule": "Harmonogram", | ||||
|   "HeaderScheduleLibraryScans": "Zaplanuj automatyczne skanowanie biblioteki", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Zamknij odtwarzacz", | ||||
|   "LabelCodec": "Codec", | ||||
|   "LabelCollapseSeries": "Podsumuj serię", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Kolekcje", | ||||
|   "LabelComplete": "Ukończone", | ||||
|   "LabelConfirmPassword": "Potwierdź hasło", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Pokaż wszystko", | ||||
|   "LabelSize": "Rozmiar", | ||||
|   "LabelSleepTimer": "Wyłącznik czasowy", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Rozpocznij", | ||||
|   "LabelStarted": "Rozpoczęty", | ||||
|   "LabelStartedAt": "Rozpoczęto", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "Invalid start time must be greater than or equal to previous chapter start time", | ||||
|   "MessageChapterStartIsAfter": "Początek rozdziału następuje po zakończeniu audiobooka", | ||||
|   "MessageCheckingCron": "Sprawdzanie cron...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Czy na pewno chcesz usunąć kopię zapasową dla {0}?", | ||||
|   "MessageConfirmDeleteFile": "This will delete the file from your file system. Are you sure?", | ||||
|   "MessageConfirmDeleteLibrary": "Czy na pewno chcesz trwale usunąć bibliotekę \"{0}\"?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "Удалить {0} эпизодов", | ||||
|   "HeaderRSSFeedGeneral": "Сведения о RSS", | ||||
|   "HeaderRSSFeedIsOpen": "RSS-канал открыт", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "Прогресс медиа сохранен", | ||||
|   "HeaderSchedule": "Планировщик", | ||||
|   "HeaderScheduleLibraryScans": "Планировщик автоматического сканирования библиотеки", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "Закрыть проигрыватель", | ||||
|   "LabelCodec": "Кодек", | ||||
|   "LabelCollapseSeries": "Свернуть серии", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "Коллекции", | ||||
|   "LabelComplete": "Завершить", | ||||
|   "LabelConfirmPassword": "Подтвердить пароль", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "Показать все", | ||||
|   "LabelSize": "Размер", | ||||
|   "LabelSleepTimer": "Таймер сна", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "Начало", | ||||
|   "LabelStarted": "Начат", | ||||
|   "LabelStartedAt": "Начато В", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "Неверное время начала, должно быть больше или равно времени начала предыдущей главы", | ||||
|   "MessageChapterStartIsAfter": "Глава начинается после окончания аудиокниги", | ||||
|   "MessageCheckingCron": "Проверка cron...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "Вы уверены, что хотите удалить бэкап для {0}?", | ||||
|   "MessageConfirmDeleteFile": "Это удалит файл из Вашей файловой системы. Вы уверены?", | ||||
|   "MessageConfirmDeleteLibrary": "Вы уверены, что хотите навсегда удалить библиотеку \"{0}\"?", | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|   "HeaderRemoveEpisodes": "移除 {0} 剧集", | ||||
|   "HeaderRSSFeedGeneral": "RSS 详细信息", | ||||
|   "HeaderRSSFeedIsOpen": "RSS 源已打开", | ||||
|   "HeaderRSSFeeds": "RSS Feeds", | ||||
|   "HeaderSavedMediaProgress": "保存媒体进度", | ||||
|   "HeaderSchedule": "计划任务", | ||||
|   "HeaderScheduleLibraryScans": "自动扫描媒体库", | ||||
| @ -201,6 +202,7 @@ | ||||
|   "LabelClosePlayer": "关闭播放器", | ||||
|   "LabelCodec": "编解码", | ||||
|   "LabelCollapseSeries": "折叠系列", | ||||
|   "LabelCollection": "Collection", | ||||
|   "LabelCollections": "收藏", | ||||
|   "LabelComplete": "已完成", | ||||
|   "LabelConfirmPassword": "确认密码", | ||||
| @ -428,6 +430,7 @@ | ||||
|   "LabelShowAll": "全部显示", | ||||
|   "LabelSize": "文件大小", | ||||
|   "LabelSleepTimer": "睡眠定时", | ||||
|   "LabelSlug": "Slug", | ||||
|   "LabelStart": "开始", | ||||
|   "LabelStarted": "开始于", | ||||
|   "LabelStartedAt": "从这开始", | ||||
| @ -516,6 +519,7 @@ | ||||
|   "MessageChapterErrorStartLtPrev": "无效的开始时间, 必须大于或等于上一章节的开始时间", | ||||
|   "MessageChapterStartIsAfter": "章节开始是在有声读物结束之后", | ||||
|   "MessageCheckingCron": "检查计划任务...", | ||||
|   "MessageConfirmCloseFeed": "Are you sure you want to close this feed?", | ||||
|   "MessageConfirmDeleteBackup": "你确定要删除备份 {0}?", | ||||
|   "MessageConfirmDeleteFile": "这将从文件系统中删除该文件. 你确定吗?", | ||||
|   "MessageConfirmDeleteLibrary": "你确定要永久删除媒体库 \"{0}\"?", | ||||
|  | ||||
| @ -5,6 +5,14 @@ const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilter | ||||
| class RSSFeedController { | ||||
|   constructor() { } | ||||
| 
 | ||||
|   async getAll(req, res) { | ||||
|     const feeds = await this.rssFeedManager.getFeeds() | ||||
|     res.json({ | ||||
|       feeds: feeds.map(f => f.toJSON()), | ||||
|       minified:  feeds.map(f => f.toJSONMinified()) | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   // POST: api/feeds/item/:itemId/open
 | ||||
|   async openRSSFeedForItem(req, res) { | ||||
|     const options = req.body || {} | ||||
|  | ||||
| @ -262,5 +262,11 @@ class RssFeedManager { | ||||
|     if (!feed) return | ||||
|     return this.handleCloseFeed(feed) | ||||
|   } | ||||
| 
 | ||||
|   async getFeeds() { | ||||
|     const feeds = await Database.models.feed.getOldFeeds() | ||||
|     Logger.info(`[RssFeedManager] Fetched all feeds`) | ||||
|     return feeds | ||||
|   } | ||||
| } | ||||
| module.exports = RssFeedManager | ||||
|  | ||||
| @ -298,6 +298,7 @@ class ApiRouter { | ||||
|     //
 | ||||
|     // RSS Feed Routes (Admin and up)
 | ||||
|     //
 | ||||
|     this.router.get('/feeds', RSSFeedController.middleware.bind(this), RSSFeedController.getAll.bind(this)) | ||||
|     this.router.post('/feeds/item/:itemId/open', RSSFeedController.middleware.bind(this), RSSFeedController.openRSSFeedForItem.bind(this)) | ||||
|     this.router.post('/feeds/collection/:collectionId/open', RSSFeedController.middleware.bind(this), RSSFeedController.openRSSFeedForCollection.bind(this)) | ||||
|     this.router.post('/feeds/series/:seriesId/open', RSSFeedController.middleware.bind(this), RSSFeedController.openRSSFeedForSeries.bind(this)) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user