mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Remove serverAddress from Feeds and FeedEpisodes URLs
This commit is contained in:
		
							parent
							
								
									3b4a5b8785
								
							
						
					
					
						commit
						9b8e059efe
					
				| @ -10,9 +10,9 @@ | |||||||
|         <p class="text-lg font-semibold mb-4">{{ $strings.HeaderRSSFeedIsOpen }}</p> |         <p class="text-lg font-semibold mb-4">{{ $strings.HeaderRSSFeedIsOpen }}</p> | ||||||
| 
 | 
 | ||||||
|         <div class="w-full relative"> |         <div class="w-full relative"> | ||||||
|           <ui-text-input v-model="currentFeed.feedUrl" readonly /> |           <ui-text-input :value="feedUrl" readonly /> | ||||||
| 
 | 
 | ||||||
|           <span class="material-symbols 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(currentFeed.feedUrl)">content_copy</span> |           <span class="material-symbols 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(feedUrl)">content_copy</span> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <div v-if="currentFeed.meta" class="mt-5"> |         <div v-if="currentFeed.meta" class="mt-5"> | ||||||
| @ -111,8 +111,11 @@ export default { | |||||||
|     userIsAdminOrUp() { |     userIsAdminOrUp() { | ||||||
|       return this.$store.getters['user/getIsAdminOrUp'] |       return this.$store.getters['user/getIsAdminOrUp'] | ||||||
|     }, |     }, | ||||||
|  |     feedUrl() { | ||||||
|  |       return this.currentFeed ? `${window.origin}${this.$config.routerBasePath}${this.currentFeed.feedUrl}` : '' | ||||||
|  |     }, | ||||||
|     demoFeedUrl() { |     demoFeedUrl() { | ||||||
|       return `${window.origin}/feed/${this.newFeedSlug}` |       return `${window.origin}${this.$config.routerBasePath}/feed/${this.newFeedSlug}` | ||||||
|     }, |     }, | ||||||
|     isHttp() { |     isHttp() { | ||||||
|       return window.origin.startsWith('http://') |       return window.origin.startsWith('http://') | ||||||
|  | |||||||
| @ -5,8 +5,8 @@ | |||||||
|         <p class="text-lg font-semibold mb-4">{{ $strings.HeaderRSSFeedGeneral }}</p> |         <p class="text-lg font-semibold mb-4">{{ $strings.HeaderRSSFeedGeneral }}</p> | ||||||
| 
 | 
 | ||||||
|         <div class="w-full relative"> |         <div class="w-full relative"> | ||||||
|           <ui-text-input v-model="feed.feedUrl" readonly /> |           <ui-text-input :value="feedUrl" readonly /> | ||||||
|           <span class="material-symbols 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> |           <span class="material-symbols 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(feedUrl)">content_copy</span> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <div v-if="feed.meta" class="mt-5"> |         <div v-if="feed.meta" class="mt-5"> | ||||||
| @ -70,6 +70,9 @@ export default { | |||||||
|     }, |     }, | ||||||
|     _feed() { |     _feed() { | ||||||
|       return this.feed || {} |       return this.feed || {} | ||||||
|  |     }, | ||||||
|  |     feedUrl() { | ||||||
|  |       return this.feed ? `${window.origin}${this.$config.routerBasePath}${this.feed.feedUrl}` : '' | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|  | |||||||
| @ -126,7 +126,7 @@ export default { | |||||||
|     }, |     }, | ||||||
|     coverUrl(feed) { |     coverUrl(feed) { | ||||||
|       if (!feed.coverPath) return `${this.$config.routerBasePath}/Logo.png` |       if (!feed.coverPath) return `${this.$config.routerBasePath}/Logo.png` | ||||||
|       return `${feed.feedUrl}/cover` |       return `${this.$config.routerBasePath}${feed.feedUrl}/cover` | ||||||
|     }, |     }, | ||||||
|     async loadFeeds() { |     async loadFeeds() { | ||||||
|       const data = await this.$axios.$get(`/api/feeds`).catch((err) => { |       const data = await this.$axios.$get(`/api/feeds`).catch((err) => { | ||||||
|  | |||||||
| @ -253,6 +253,10 @@ class Server { | |||||||
|     // if RouterBasePath is set, modify all requests to include the base path
 |     // if RouterBasePath is set, modify all requests to include the base path
 | ||||||
|     if (global.RouterBasePath) { |     if (global.RouterBasePath) { | ||||||
|       app.use((req, res, next) => { |       app.use((req, res, next) => { | ||||||
|  |         const host = req.get('host') | ||||||
|  |         const protocol = req.secure || req.get('x-forwarded-proto') === 'https' ? 'https' : 'http' | ||||||
|  |         const prefix = req.url.startsWith(global.RouterBasePath) ? global.RouterBasePath : '' | ||||||
|  |         req.originalHostPrefix = `${protocol}://${host}${prefix}` | ||||||
|         if (!req.url.startsWith(global.RouterBasePath)) { |         if (!req.url.startsWith(global.RouterBasePath)) { | ||||||
|           req.url = `${global.RouterBasePath}${req.url}` |           req.url = `${global.RouterBasePath}${req.url}` | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -162,7 +162,7 @@ class RssFeedManager { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const xml = feed.buildXml() |     const xml = feed.buildXml(req.originalHostPrefix) | ||||||
|     res.set('Content-Type', 'text/xml') |     res.set('Content-Type', 'text/xml') | ||||||
|     res.send(xml) |     res.send(xml) | ||||||
|   } |   } | ||||||
|  | |||||||
							
								
								
									
										74
									
								
								server/migrations/v2.17.5-remove-host-from-feed-urls.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								server/migrations/v2.17.5-remove-host-from-feed-urls.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | |||||||
|  | /** | ||||||
|  |  * @typedef MigrationContext | ||||||
|  |  * @property {import('sequelize').QueryInterface} queryInterface - a suquelize QueryInterface object. | ||||||
|  |  * @property {import('../Logger')} logger - a Logger object. | ||||||
|  |  * | ||||||
|  |  * @typedef MigrationOptions | ||||||
|  |  * @property {MigrationContext} context - an object containing the migration context. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | const migrationVersion = '2.17.5' | ||||||
|  | const migrationName = `${migrationVersion}-remove-host-from-feed-urls` | ||||||
|  | const loggerPrefix = `[${migrationVersion} migration]` | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * This upward migration removes the host (serverAddress) from URL columns in the feeds and feedEpisodes tables. | ||||||
|  |  * | ||||||
|  |  * @param {MigrationOptions} options - an object containing the migration context. | ||||||
|  |  * @returns {Promise<void>} - A promise that resolves when the migration is complete. | ||||||
|  |  */ | ||||||
|  | async function up({ context: { queryInterface, logger } }) { | ||||||
|  |   // Upwards migration script
 | ||||||
|  |   logger.info(`${loggerPrefix} UPGRADE BEGIN: ${migrationName}`) | ||||||
|  | 
 | ||||||
|  |   logger.info(`${loggerPrefix} Removing serverAddress from Feeds table URLs`) | ||||||
|  |   await queryInterface.sequelize.query(` | ||||||
|  |     UPDATE Feeds | ||||||
|  |     SET feedUrl = REPLACE(feedUrl, COALESCE(serverAddress, ''), ''), | ||||||
|  |         imageUrl = REPLACE(imageUrl, COALESCE(serverAddress, ''), ''), | ||||||
|  |         siteUrl = REPLACE(siteUrl, COALESCE(serverAddress, ''), ''); | ||||||
|  |   `)
 | ||||||
|  |   logger.info(`${loggerPrefix} Removed serverAddress from Feeds table URLs`) | ||||||
|  | 
 | ||||||
|  |   logger.info(`${loggerPrefix} Removing serverAddress from FeedEpisodes table URLs`) | ||||||
|  |   await queryInterface.sequelize.query(` | ||||||
|  |     UPDATE FeedEpisodes | ||||||
|  |       SET siteUrl = REPLACE(siteUrl, (SELECT COALESCE(serverAddress, '') FROM Feeds WHERE Feeds.id = FeedEpisodes.feedId), ''), | ||||||
|  |           enclosureUrl = REPLACE(enclosureUrl, (SELECT COALESCE(serverAddress, '') FROM Feeds WHERE Feeds.id = FeedEpisodes.feedId), ''); | ||||||
|  |   `)
 | ||||||
|  |   logger.info(`${loggerPrefix} Removed serverAddress from FeedEpisodes table URLs`) | ||||||
|  | 
 | ||||||
|  |   logger.info(`${loggerPrefix} UPGRADE END: ${migrationName}`) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * This downward migration script adds the host (serverAddress) back to URL columns in the feeds and feedEpisodes tables. | ||||||
|  |  * | ||||||
|  |  * @param {MigrationOptions} options - an object containing the migration context. | ||||||
|  |  * @returns {Promise<void>} - A promise that resolves when the migration is complete. | ||||||
|  |  */ | ||||||
|  | async function down({ context: { queryInterface, logger } }) { | ||||||
|  |   // Downward migration script
 | ||||||
|  |   logger.info(`${loggerPrefix} DOWNGRADE BEGIN: ${migrationName}`) | ||||||
|  | 
 | ||||||
|  |   logger.info(`${loggerPrefix} Adding serverAddress back to Feeds table URLs`) | ||||||
|  |   await queryInterface.sequelize.query(` | ||||||
|  |     UPDATE Feeds | ||||||
|  |     SET feedUrl = COALESCE(serverAddress, '') || feedUrl, | ||||||
|  |         imageUrl = COALESCE(serverAddress, '') || imageUrl, | ||||||
|  |         siteUrl = COALESCE(serverAddress, '') || siteUrl; | ||||||
|  |   `)
 | ||||||
|  |   logger.info(`${loggerPrefix} Added serverAddress back to Feeds table URLs`) | ||||||
|  | 
 | ||||||
|  |   logger.info(`${loggerPrefix} Adding serverAddress back to FeedEpisodes table URLs`) | ||||||
|  |   await queryInterface.sequelize.query(` | ||||||
|  |     UPDATE FeedEpisodes | ||||||
|  |       SET siteUrl = (SELECT COALESCE(serverAddress, '') || FeedEpisodes.siteUrl FROM Feeds WHERE Feeds.id = FeedEpisodes.feedId), | ||||||
|  |           enclosureUrl = (SELECT COALESCE(serverAddress, '') || FeedEpisodes.enclosureUrl FROM Feeds WHERE Feeds.id = FeedEpisodes.feedId); | ||||||
|  |   `)
 | ||||||
|  |   logger.info(`${loggerPrefix} Added serverAddress back to FeedEpisodes table URLs`) | ||||||
|  | 
 | ||||||
|  |   logger.info(`${loggerPrefix} DOWNGRADE END: ${migrationName}`) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = { up, down } | ||||||
| @ -109,7 +109,7 @@ class Feed { | |||||||
|     const mediaMetadata = media.metadata |     const mediaMetadata = media.metadata | ||||||
|     const isPodcast = libraryItem.mediaType === 'podcast' |     const isPodcast = libraryItem.mediaType === 'podcast' | ||||||
| 
 | 
 | ||||||
|     const feedUrl = `${serverAddress}/feed/${slug}` |     const feedUrl = `/feed/${slug}` | ||||||
|     const author = isPodcast ? mediaMetadata.author : mediaMetadata.authorName |     const author = isPodcast ? mediaMetadata.author : mediaMetadata.authorName | ||||||
| 
 | 
 | ||||||
|     this.id = uuidv4() |     this.id = uuidv4() | ||||||
| @ -128,9 +128,9 @@ class Feed { | |||||||
|     this.meta.title = mediaMetadata.title |     this.meta.title = mediaMetadata.title | ||||||
|     this.meta.description = mediaMetadata.description |     this.meta.description = mediaMetadata.description | ||||||
|     this.meta.author = author |     this.meta.author = author | ||||||
|     this.meta.imageUrl = media.coverPath ? `${serverAddress}/feed/${slug}/cover${coverFileExtension}` : `${serverAddress}/Logo.png` |     this.meta.imageUrl = media.coverPath ? `/feed/${slug}/cover${coverFileExtension}` : `/Logo.png` | ||||||
|     this.meta.feedUrl = feedUrl |     this.meta.feedUrl = feedUrl | ||||||
|     this.meta.link = `${serverAddress}/item/${libraryItem.id}` |     this.meta.link = `/item/${libraryItem.id}` | ||||||
|     this.meta.explicit = !!mediaMetadata.explicit |     this.meta.explicit = !!mediaMetadata.explicit | ||||||
|     this.meta.type = mediaMetadata.type |     this.meta.type = mediaMetadata.type | ||||||
|     this.meta.language = mediaMetadata.language |     this.meta.language = mediaMetadata.language | ||||||
| @ -176,7 +176,7 @@ class Feed { | |||||||
|     this.meta.title = mediaMetadata.title |     this.meta.title = mediaMetadata.title | ||||||
|     this.meta.description = mediaMetadata.description |     this.meta.description = mediaMetadata.description | ||||||
|     this.meta.author = author |     this.meta.author = author | ||||||
|     this.meta.imageUrl = media.coverPath ? `${this.serverAddress}/feed/${this.slug}/cover${coverFileExtension}` : `${this.serverAddress}/Logo.png` |     this.meta.imageUrl = media.coverPath ? `/feed/${this.slug}/cover${coverFileExtension}` : `/Logo.png` | ||||||
|     this.meta.explicit = !!mediaMetadata.explicit |     this.meta.explicit = !!mediaMetadata.explicit | ||||||
|     this.meta.type = mediaMetadata.type |     this.meta.type = mediaMetadata.type | ||||||
|     this.meta.language = mediaMetadata.language |     this.meta.language = mediaMetadata.language | ||||||
| @ -206,7 +206,7 @@ class Feed { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   setFromCollection(userId, slug, collectionExpanded, serverAddress, preventIndexing = true, ownerName = null, ownerEmail = null) { |   setFromCollection(userId, slug, collectionExpanded, serverAddress, preventIndexing = true, ownerName = null, ownerEmail = null) { | ||||||
|     const feedUrl = `${serverAddress}/feed/${slug}` |     const feedUrl = `/feed/${slug}` | ||||||
| 
 | 
 | ||||||
|     const itemsWithTracks = collectionExpanded.books.filter((libraryItem) => libraryItem.media.tracks.length) |     const itemsWithTracks = collectionExpanded.books.filter((libraryItem) => libraryItem.media.tracks.length) | ||||||
|     const firstItemWithCover = itemsWithTracks.find((item) => item.media.coverPath) |     const firstItemWithCover = itemsWithTracks.find((item) => item.media.coverPath) | ||||||
| @ -227,9 +227,9 @@ class Feed { | |||||||
|     this.meta.title = collectionExpanded.name |     this.meta.title = collectionExpanded.name | ||||||
|     this.meta.description = collectionExpanded.description || '' |     this.meta.description = collectionExpanded.description || '' | ||||||
|     this.meta.author = this.getAuthorsStringFromLibraryItems(itemsWithTracks) |     this.meta.author = this.getAuthorsStringFromLibraryItems(itemsWithTracks) | ||||||
|     this.meta.imageUrl = this.coverPath ? `${serverAddress}/feed/${slug}/cover${coverFileExtension}` : `${serverAddress}/Logo.png` |     this.meta.imageUrl = this.coverPath ? `/feed/${slug}/cover${coverFileExtension}` : `/Logo.png` | ||||||
|     this.meta.feedUrl = feedUrl |     this.meta.feedUrl = feedUrl | ||||||
|     this.meta.link = `${serverAddress}/collection/${collectionExpanded.id}` |     this.meta.link = `/collection/${collectionExpanded.id}` | ||||||
|     this.meta.explicit = !!itemsWithTracks.some((li) => li.media.metadata.explicit) // explicit if any item is explicit
 |     this.meta.explicit = !!itemsWithTracks.some((li) => li.media.metadata.explicit) // explicit if any item is explicit
 | ||||||
|     this.meta.preventIndexing = preventIndexing |     this.meta.preventIndexing = preventIndexing | ||||||
|     this.meta.ownerName = ownerName |     this.meta.ownerName = ownerName | ||||||
| @ -272,7 +272,7 @@ class Feed { | |||||||
|     this.meta.title = collectionExpanded.name |     this.meta.title = collectionExpanded.name | ||||||
|     this.meta.description = collectionExpanded.description || '' |     this.meta.description = collectionExpanded.description || '' | ||||||
|     this.meta.author = this.getAuthorsStringFromLibraryItems(itemsWithTracks) |     this.meta.author = this.getAuthorsStringFromLibraryItems(itemsWithTracks) | ||||||
|     this.meta.imageUrl = this.coverPath ? `${this.serverAddress}/feed/${this.slug}/cover${coverFileExtension}` : `${this.serverAddress}/Logo.png` |     this.meta.imageUrl = this.coverPath ? `/feed/${this.slug}/cover${coverFileExtension}` : `/Logo.png` | ||||||
|     this.meta.explicit = !!itemsWithTracks.some((li) => li.media.metadata.explicit) // explicit if any item is explicit
 |     this.meta.explicit = !!itemsWithTracks.some((li) => li.media.metadata.explicit) // explicit if any item is explicit
 | ||||||
| 
 | 
 | ||||||
|     this.episodes = [] |     this.episodes = [] | ||||||
| @ -301,7 +301,7 @@ class Feed { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   setFromSeries(userId, slug, seriesExpanded, serverAddress, preventIndexing = true, ownerName = null, ownerEmail = null) { |   setFromSeries(userId, slug, seriesExpanded, serverAddress, preventIndexing = true, ownerName = null, ownerEmail = null) { | ||||||
|     const feedUrl = `${serverAddress}/feed/${slug}` |     const feedUrl = `/feed/${slug}` | ||||||
| 
 | 
 | ||||||
|     let itemsWithTracks = seriesExpanded.books.filter((libraryItem) => libraryItem.media.tracks.length) |     let itemsWithTracks = seriesExpanded.books.filter((libraryItem) => libraryItem.media.tracks.length) | ||||||
|     // Sort series items by series sequence
 |     // Sort series items by series sequence
 | ||||||
| @ -326,9 +326,9 @@ class Feed { | |||||||
|     this.meta.title = seriesExpanded.name |     this.meta.title = seriesExpanded.name | ||||||
|     this.meta.description = seriesExpanded.description || '' |     this.meta.description = seriesExpanded.description || '' | ||||||
|     this.meta.author = this.getAuthorsStringFromLibraryItems(itemsWithTracks) |     this.meta.author = this.getAuthorsStringFromLibraryItems(itemsWithTracks) | ||||||
|     this.meta.imageUrl = this.coverPath ? `${serverAddress}/feed/${slug}/cover${coverFileExtension}` : `${serverAddress}/Logo.png` |     this.meta.imageUrl = this.coverPath ? `/feed/${slug}/cover${coverFileExtension}` : `/Logo.png` | ||||||
|     this.meta.feedUrl = feedUrl |     this.meta.feedUrl = feedUrl | ||||||
|     this.meta.link = `${serverAddress}/library/${libraryId}/series/${seriesExpanded.id}` |     this.meta.link = `/library/${libraryId}/series/${seriesExpanded.id}` | ||||||
|     this.meta.explicit = !!itemsWithTracks.some((li) => li.media.metadata.explicit) // explicit if any item is explicit
 |     this.meta.explicit = !!itemsWithTracks.some((li) => li.media.metadata.explicit) // explicit if any item is explicit
 | ||||||
|     this.meta.preventIndexing = preventIndexing |     this.meta.preventIndexing = preventIndexing | ||||||
|     this.meta.ownerName = ownerName |     this.meta.ownerName = ownerName | ||||||
| @ -374,7 +374,7 @@ class Feed { | |||||||
|     this.meta.title = seriesExpanded.name |     this.meta.title = seriesExpanded.name | ||||||
|     this.meta.description = seriesExpanded.description || '' |     this.meta.description = seriesExpanded.description || '' | ||||||
|     this.meta.author = this.getAuthorsStringFromLibraryItems(itemsWithTracks) |     this.meta.author = this.getAuthorsStringFromLibraryItems(itemsWithTracks) | ||||||
|     this.meta.imageUrl = this.coverPath ? `${this.serverAddress}/feed/${this.slug}/cover${coverFileExtension}` : `${this.serverAddress}/Logo.png` |     this.meta.imageUrl = this.coverPath ? `/feed/${this.slug}/cover${coverFileExtension}` : `/Logo.png` | ||||||
|     this.meta.explicit = !!itemsWithTracks.some((li) => li.media.metadata.explicit) // explicit if any item is explicit
 |     this.meta.explicit = !!itemsWithTracks.some((li) => li.media.metadata.explicit) // explicit if any item is explicit
 | ||||||
| 
 | 
 | ||||||
|     this.episodes = [] |     this.episodes = [] | ||||||
| @ -402,12 +402,12 @@ class Feed { | |||||||
|     this.xml = null |     this.xml = null | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   buildXml() { |   buildXml(originalHostPrefix) { | ||||||
|     if (this.xml) return this.xml |     if (this.xml) return this.xml | ||||||
| 
 | 
 | ||||||
|     var rssfeed = new RSS(this.meta.getRSSData()) |     var rssfeed = new RSS(this.meta.getRSSData(originalHostPrefix)) | ||||||
|     this.episodes.forEach((ep) => { |     this.episodes.forEach((ep) => { | ||||||
|       rssfeed.item(ep.getRSSData()) |       rssfeed.item(ep.getRSSData(originalHostPrefix)) | ||||||
|     }) |     }) | ||||||
|     this.xml = rssfeed.xml() |     this.xml = rssfeed.xml() | ||||||
|     return this.xml |     return this.xml | ||||||
|  | |||||||
| @ -79,7 +79,7 @@ class FeedEpisode { | |||||||
|     this.title = episode.title |     this.title = episode.title | ||||||
|     this.description = episode.description || '' |     this.description = episode.description || '' | ||||||
|     this.enclosure = { |     this.enclosure = { | ||||||
|       url: `${serverAddress}${contentUrl}`, |       url: `${contentUrl}`, | ||||||
|       type: episode.audioTrack.mimeType, |       type: episode.audioTrack.mimeType, | ||||||
|       size: episode.size |       size: episode.size | ||||||
|     } |     } | ||||||
| @ -136,7 +136,7 @@ class FeedEpisode { | |||||||
|     this.title = title |     this.title = title | ||||||
|     this.description = mediaMetadata.description || '' |     this.description = mediaMetadata.description || '' | ||||||
|     this.enclosure = { |     this.enclosure = { | ||||||
|       url: `${serverAddress}${contentUrl}`, |       url: `${contentUrl}`, | ||||||
|       type: audioTrack.mimeType, |       type: audioTrack.mimeType, | ||||||
|       size: audioTrack.metadata.size |       size: audioTrack.metadata.size | ||||||
|     } |     } | ||||||
| @ -151,15 +151,19 @@ class FeedEpisode { | |||||||
|     this.fullPath = audioTrack.metadata.path |     this.fullPath = audioTrack.metadata.path | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getRSSData() { |   getRSSData(hostPrefix) { | ||||||
|     return { |     return { | ||||||
|       title: this.title, |       title: this.title, | ||||||
|       description: this.description || '', |       description: this.description || '', | ||||||
|       url: this.link, |       url: `${hostPrefix}${this.link}`, | ||||||
|       guid: this.enclosure.url, |       guid: `${hostPrefix}${this.enclosure.url}`, | ||||||
|       author: this.author, |       author: this.author, | ||||||
|       date: this.pubDate, |       date: this.pubDate, | ||||||
|       enclosure: this.enclosure, |       enclosure: { | ||||||
|  |         url: `${hostPrefix}${this.enclosure.url}`, | ||||||
|  |         type: this.enclosure.type, | ||||||
|  |         size: this.enclosure.size | ||||||
|  |       }, | ||||||
|       custom_elements: [ |       custom_elements: [ | ||||||
|         { 'itunes:author': this.author }, |         { 'itunes:author': this.author }, | ||||||
|         { 'itunes:duration': secondsToTimestamp(this.duration) }, |         { 'itunes:duration': secondsToTimestamp(this.duration) }, | ||||||
|  | |||||||
| @ -60,42 +60,36 @@ class FeedMeta { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getRSSData() { |   getRSSData(hostPrefix) { | ||||||
|     const blockTags = [ |     const blockTags = [{ 'itunes:block': 'yes' }, { 'googleplay:block': 'yes' }] | ||||||
|       { 'itunes:block': 'yes' }, |  | ||||||
|       { 'googleplay:block': 'yes' } |  | ||||||
|     ] |  | ||||||
|     return { |     return { | ||||||
|       title: this.title, |       title: this.title, | ||||||
|       description: this.description || '', |       description: this.description || '', | ||||||
|       generator: 'Audiobookshelf', |       generator: 'Audiobookshelf', | ||||||
|       feed_url: this.feedUrl, |       feed_url: `${hostPrefix}${this.feedUrl}`, | ||||||
|       site_url: this.link, |       site_url: `${hostPrefix}${this.link}`, | ||||||
|       image_url: this.imageUrl, |       image_url: `${hostPrefix}${this.imageUrl}`, | ||||||
|       custom_namespaces: { |       custom_namespaces: { | ||||||
|         'itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd', |         itunes: 'http://www.itunes.com/dtds/podcast-1.0.dtd', | ||||||
|         'psc': 'http://podlove.org/simple-chapters', |         psc: 'http://podlove.org/simple-chapters', | ||||||
|         'podcast': 'https://podcastindex.org/namespace/1.0', |         podcast: 'https://podcastindex.org/namespace/1.0', | ||||||
|         'googleplay': 'http://www.google.com/schemas/play-podcasts/1.0' |         googleplay: 'http://www.google.com/schemas/play-podcasts/1.0' | ||||||
|       }, |       }, | ||||||
|       custom_elements: [ |       custom_elements: [ | ||||||
|         { 'language': this.language || 'en' }, |         { language: this.language || 'en' }, | ||||||
|         { 'author': this.author || 'advplyr' }, |         { author: this.author || 'advplyr' }, | ||||||
|         { 'itunes:author': this.author || 'advplyr' }, |         { 'itunes:author': this.author || 'advplyr' }, | ||||||
|         { 'itunes:summary': this.description || '' }, |         { 'itunes:summary': this.description || '' }, | ||||||
|         { 'itunes:type': this.type }, |         { 'itunes:type': this.type }, | ||||||
|         { |         { | ||||||
|           'itunes:image': { |           'itunes:image': { | ||||||
|             _attr: { |             _attr: { | ||||||
|               href: this.imageUrl |               href: `${hostPrefix}${this.imageUrl}` | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           'itunes:owner': [ |           'itunes:owner': [{ 'itunes:name': this.ownerName || this.author || '' }, { 'itunes:email': this.ownerEmail || '' }] | ||||||
|             { 'itunes:name': this.ownerName || this.author || '' }, |  | ||||||
|             { 'itunes:email': this.ownerEmail || '' } |  | ||||||
|           ] |  | ||||||
|         }, |         }, | ||||||
|         { 'itunes:explicit': !!this.explicit }, |         { 'itunes:explicit': !!this.explicit }, | ||||||
|         ...(this.preventIndexing ? blockTags : []) |         ...(this.preventIndexing ? blockTags : []) | ||||||
|  | |||||||
| @ -0,0 +1,202 @@ | |||||||
|  | const { expect } = require('chai') | ||||||
|  | const sinon = require('sinon') | ||||||
|  | const { up, down } = require('../../../server/migrations/v2.17.5-remove-host-from-feed-urls') | ||||||
|  | const { Sequelize, DataTypes } = require('sequelize') | ||||||
|  | const Logger = require('../../../server/Logger') | ||||||
|  | 
 | ||||||
|  | const defineModels = (sequelize) => { | ||||||
|  |   const Feeds = sequelize.define('Feeds', { | ||||||
|  |     id: { type: DataTypes.UUID, primaryKey: true, defaultValue: DataTypes.UUIDV4 }, | ||||||
|  |     feedUrl: { type: DataTypes.STRING }, | ||||||
|  |     imageUrl: { type: DataTypes.STRING }, | ||||||
|  |     siteUrl: { type: DataTypes.STRING }, | ||||||
|  |     serverAddress: { type: DataTypes.STRING } | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   const FeedEpisodes = sequelize.define('FeedEpisodes', { | ||||||
|  |     id: { type: DataTypes.UUID, primaryKey: true, defaultValue: DataTypes.UUIDV4 }, | ||||||
|  |     feedId: { type: DataTypes.UUID }, | ||||||
|  |     siteUrl: { type: DataTypes.STRING }, | ||||||
|  |     enclosureUrl: { type: DataTypes.STRING } | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   return { Feeds, FeedEpisodes } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | describe('Migration v2.17.4-use-subfolder-for-oidc-redirect-uris', () => { | ||||||
|  |   let queryInterface, logger, context | ||||||
|  |   let sequelize | ||||||
|  |   let Feeds, FeedEpisodes | ||||||
|  |   const feed1Id = '00000000-0000-4000-a000-000000000001' | ||||||
|  |   const feed2Id = '00000000-0000-4000-a000-000000000002' | ||||||
|  |   const feedEpisode1Id = '00000000-4000-a000-0000-000000000011' | ||||||
|  |   const feedEpisode2Id = '00000000-4000-a000-0000-000000000012' | ||||||
|  |   const feedEpisode3Id = '00000000-4000-a000-0000-000000000021' | ||||||
|  | 
 | ||||||
|  |   before(async () => { | ||||||
|  |     sequelize = new Sequelize({ dialect: 'sqlite', storage: ':memory:', logging: false }) | ||||||
|  |     queryInterface = sequelize.getQueryInterface() | ||||||
|  |     ;({ Feeds, FeedEpisodes } = defineModels(sequelize)) | ||||||
|  |     await sequelize.sync() | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   after(async () => { | ||||||
|  |     await sequelize.close() | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     // Reset tables before each test
 | ||||||
|  |     await Feeds.destroy({ where: {}, truncate: true }) | ||||||
|  |     await FeedEpisodes.destroy({ where: {}, truncate: true }) | ||||||
|  | 
 | ||||||
|  |     logger = { | ||||||
|  |       info: sinon.stub(), | ||||||
|  |       error: sinon.stub() | ||||||
|  |     } | ||||||
|  |     context = { queryInterface, logger } | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   describe('up', () => { | ||||||
|  |     it('should remove serverAddress from URLs in Feeds and FeedEpisodes tables', async () => { | ||||||
|  |       await Feeds.bulkCreate([ | ||||||
|  |         { id: feed1Id, feedUrl: 'http://server1.com/feed1', imageUrl: 'http://server1.com/img1', siteUrl: 'http://server1.com/site1', serverAddress: 'http://server1.com' }, | ||||||
|  |         { id: feed2Id, feedUrl: 'http://server2.com/feed2', imageUrl: 'http://server2.com/img2', siteUrl: 'http://server2.com/site2', serverAddress: 'http://server2.com' } | ||||||
|  |       ]) | ||||||
|  | 
 | ||||||
|  |       await FeedEpisodes.bulkCreate([ | ||||||
|  |         { id: feedEpisode1Id, feedId: feed1Id, siteUrl: 'http://server1.com/episode11', enclosureUrl: 'http://server1.com/enclosure11' }, | ||||||
|  |         { id: feedEpisode2Id, feedId: feed1Id, siteUrl: 'http://server1.com/episode12', enclosureUrl: 'http://server1.com/enclosure12' }, | ||||||
|  |         { id: feedEpisode3Id, feedId: feed2Id, siteUrl: 'http://server2.com/episode21', enclosureUrl: 'http://server2.com/enclosure21' } | ||||||
|  |       ]) | ||||||
|  | 
 | ||||||
|  |       await up({ context }) | ||||||
|  |       const feeds = await Feeds.findAll({ raw: true }) | ||||||
|  |       const feedEpisodes = await FeedEpisodes.findAll({ raw: true }) | ||||||
|  | 
 | ||||||
|  |       expect(logger.info.calledWith('[2.17.5 migration] UPGRADE BEGIN: 2.17.5-remove-host-from-feed-urls')).to.be.true | ||||||
|  |       expect(logger.info.calledWith('[2.17.5 migration] Removing serverAddress from Feeds table URLs')).to.be.true | ||||||
|  | 
 | ||||||
|  |       expect(feeds[0].feedUrl).to.equal('/feed1') | ||||||
|  |       expect(feeds[0].imageUrl).to.equal('/img1') | ||||||
|  |       expect(feeds[0].siteUrl).to.equal('/site1') | ||||||
|  |       expect(feeds[1].feedUrl).to.equal('/feed2') | ||||||
|  |       expect(feeds[1].imageUrl).to.equal('/img2') | ||||||
|  |       expect(feeds[1].siteUrl).to.equal('/site2') | ||||||
|  | 
 | ||||||
|  |       expect(logger.info.calledWith('[2.17.5 migration] Removed serverAddress from Feeds table URLs')).to.be.true | ||||||
|  |       expect(logger.info.calledWith('[2.17.5 migration] Removing serverAddress from FeedEpisodes table URLs')).to.be.true | ||||||
|  | 
 | ||||||
|  |       expect(feedEpisodes[0].siteUrl).to.equal('/episode11') | ||||||
|  |       expect(feedEpisodes[0].enclosureUrl).to.equal('/enclosure11') | ||||||
|  |       expect(feedEpisodes[1].siteUrl).to.equal('/episode12') | ||||||
|  |       expect(feedEpisodes[1].enclosureUrl).to.equal('/enclosure12') | ||||||
|  |       expect(feedEpisodes[2].siteUrl).to.equal('/episode21') | ||||||
|  |       expect(feedEpisodes[2].enclosureUrl).to.equal('/enclosure21') | ||||||
|  | 
 | ||||||
|  |       expect(logger.info.calledWith('[2.17.5 migration] Removed serverAddress from FeedEpisodes table URLs')).to.be.true | ||||||
|  |       expect(logger.info.calledWith('[2.17.5 migration] UPGRADE END: 2.17.5-remove-host-from-feed-urls')).to.be.true | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     it('should handle null URLs in Feeds and FeedEpisodes tables', async () => { | ||||||
|  |       await Feeds.bulkCreate([{ id: feed1Id, feedUrl: 'http://server1.com/feed1', imageUrl: null, siteUrl: 'http://server1.com/site1', serverAddress: 'http://server1.com' }]) | ||||||
|  | 
 | ||||||
|  |       await FeedEpisodes.bulkCreate([{ id: feedEpisode1Id, feedId: feed1Id, siteUrl: null, enclosureUrl: 'http://server1.com/enclosure11' }]) | ||||||
|  | 
 | ||||||
|  |       await up({ context }) | ||||||
|  |       const feeds = await Feeds.findAll({ raw: true }) | ||||||
|  |       const feedEpisodes = await FeedEpisodes.findAll({ raw: true }) | ||||||
|  | 
 | ||||||
|  |       expect(feeds[0].feedUrl).to.equal('/feed1') | ||||||
|  |       expect(feeds[0].imageUrl).to.be.null | ||||||
|  |       expect(feeds[0].siteUrl).to.equal('/site1') | ||||||
|  |       expect(feedEpisodes[0].siteUrl).to.be.null | ||||||
|  |       expect(feedEpisodes[0].enclosureUrl).to.equal('/enclosure11') | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     it('should handle null serverAddress in Feeds table', async () => { | ||||||
|  |       await Feeds.bulkCreate([{ id: feed1Id, feedUrl: 'http://server1.com/feed1', imageUrl: 'http://server1.com/img1', siteUrl: 'http://server1.com/site1', serverAddress: null }]) | ||||||
|  |       await FeedEpisodes.bulkCreate([{ id: feedEpisode1Id, feedId: feed1Id, siteUrl: 'http://server1.com/episode11', enclosureUrl: 'http://server1.com/enclosure11' }]) | ||||||
|  | 
 | ||||||
|  |       await up({ context }) | ||||||
|  |       const feeds = await Feeds.findAll({ raw: true }) | ||||||
|  |       const feedEpisodes = await FeedEpisodes.findAll({ raw: true }) | ||||||
|  | 
 | ||||||
|  |       expect(feeds[0].feedUrl).to.equal('http://server1.com/feed1') | ||||||
|  |       expect(feeds[0].imageUrl).to.equal('http://server1.com/img1') | ||||||
|  |       expect(feeds[0].siteUrl).to.equal('http://server1.com/site1') | ||||||
|  |       expect(feedEpisodes[0].siteUrl).to.equal('http://server1.com/episode11') | ||||||
|  |       expect(feedEpisodes[0].enclosureUrl).to.equal('http://server1.com/enclosure11') | ||||||
|  |     }) | ||||||
|  |   }) | ||||||
|  | 
 | ||||||
|  |   describe('down', () => { | ||||||
|  |     it('should add serverAddress back to URLs in Feeds and FeedEpisodes tables', async () => { | ||||||
|  |       await Feeds.bulkCreate([ | ||||||
|  |         { id: feed1Id, feedUrl: '/feed1', imageUrl: '/img1', siteUrl: '/site1', serverAddress: 'http://server1.com' }, | ||||||
|  |         { id: feed2Id, feedUrl: '/feed2', imageUrl: '/img2', siteUrl: '/site2', serverAddress: 'http://server2.com' } | ||||||
|  |       ]) | ||||||
|  | 
 | ||||||
|  |       await FeedEpisodes.bulkCreate([ | ||||||
|  |         { id: feedEpisode1Id, feedId: feed1Id, siteUrl: '/episode11', enclosureUrl: '/enclosure11' }, | ||||||
|  |         { id: feedEpisode2Id, feedId: feed1Id, siteUrl: '/episode12', enclosureUrl: '/enclosure12' }, | ||||||
|  |         { id: feedEpisode3Id, feedId: feed2Id, siteUrl: '/episode21', enclosureUrl: '/enclosure21' } | ||||||
|  |       ]) | ||||||
|  | 
 | ||||||
|  |       await down({ context }) | ||||||
|  |       const feeds = await Feeds.findAll({ raw: true }) | ||||||
|  |       const feedEpisodes = await FeedEpisodes.findAll({ raw: true }) | ||||||
|  | 
 | ||||||
|  |       expect(logger.info.calledWith('[2.17.5 migration] DOWNGRADE BEGIN: 2.17.5-remove-host-from-feed-urls')).to.be.true | ||||||
|  |       expect(logger.info.calledWith('[2.17.5 migration] Adding serverAddress back to Feeds table URLs')).to.be.true | ||||||
|  | 
 | ||||||
|  |       expect(feeds[0].feedUrl).to.equal('http://server1.com/feed1') | ||||||
|  |       expect(feeds[0].imageUrl).to.equal('http://server1.com/img1') | ||||||
|  |       expect(feeds[0].siteUrl).to.equal('http://server1.com/site1') | ||||||
|  |       expect(feeds[1].feedUrl).to.equal('http://server2.com/feed2') | ||||||
|  |       expect(feeds[1].imageUrl).to.equal('http://server2.com/img2') | ||||||
|  |       expect(feeds[1].siteUrl).to.equal('http://server2.com/site2') | ||||||
|  | 
 | ||||||
|  |       expect(logger.info.calledWith('[2.17.5 migration] Added serverAddress back to Feeds table URLs')).to.be.true | ||||||
|  |       expect(logger.info.calledWith('[2.17.5 migration] Adding serverAddress back to FeedEpisodes table URLs')).to.be.true | ||||||
|  | 
 | ||||||
|  |       expect(feedEpisodes[0].siteUrl).to.equal('http://server1.com/episode11') | ||||||
|  |       expect(feedEpisodes[0].enclosureUrl).to.equal('http://server1.com/enclosure11') | ||||||
|  |       expect(feedEpisodes[1].siteUrl).to.equal('http://server1.com/episode12') | ||||||
|  |       expect(feedEpisodes[1].enclosureUrl).to.equal('http://server1.com/enclosure12') | ||||||
|  |       expect(feedEpisodes[2].siteUrl).to.equal('http://server2.com/episode21') | ||||||
|  |       expect(feedEpisodes[2].enclosureUrl).to.equal('http://server2.com/enclosure21') | ||||||
|  | 
 | ||||||
|  |       expect(logger.info.calledWith('[2.17.5 migration] DOWNGRADE END: 2.17.5-remove-host-from-feed-urls')).to.be.true | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     it('should handle null URLs in Feeds and FeedEpisodes tables', async () => { | ||||||
|  |       await Feeds.bulkCreate([{ id: feed1Id, feedUrl: '/feed1', imageUrl: null, siteUrl: '/site1', serverAddress: 'http://server1.com' }]) | ||||||
|  |       await FeedEpisodes.bulkCreate([{ id: feedEpisode1Id, feedId: feed1Id, siteUrl: null, enclosureUrl: '/enclosure11' }]) | ||||||
|  | 
 | ||||||
|  |       await down({ context }) | ||||||
|  |       const feeds = await Feeds.findAll({ raw: true }) | ||||||
|  |       const feedEpisodes = await FeedEpisodes.findAll({ raw: true }) | ||||||
|  | 
 | ||||||
|  |       expect(feeds[0].feedUrl).to.equal('http://server1.com/feed1') | ||||||
|  |       expect(feeds[0].imageUrl).to.be.null | ||||||
|  |       expect(feeds[0].siteUrl).to.equal('http://server1.com/site1') | ||||||
|  |       expect(feedEpisodes[0].siteUrl).to.be.null | ||||||
|  |       expect(feedEpisodes[0].enclosureUrl).to.equal('http://server1.com/enclosure11') | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     it('should handle null serverAddress in Feeds table', async () => { | ||||||
|  |       await Feeds.bulkCreate([{ id: feed1Id, feedUrl: '/feed1', imageUrl: '/img1', siteUrl: '/site1', serverAddress: null }]) | ||||||
|  |       await FeedEpisodes.bulkCreate([{ id: feedEpisode1Id, feedId: feed1Id, siteUrl: '/episode11', enclosureUrl: '/enclosure11' }]) | ||||||
|  | 
 | ||||||
|  |       await down({ context }) | ||||||
|  |       const feeds = await Feeds.findAll({ raw: true }) | ||||||
|  |       const feedEpisodes = await FeedEpisodes.findAll({ raw: true }) | ||||||
|  | 
 | ||||||
|  |       expect(feeds[0].feedUrl).to.equal('/feed1') | ||||||
|  |       expect(feeds[0].imageUrl).to.equal('/img1') | ||||||
|  |       expect(feeds[0].siteUrl).to.equal('/site1') | ||||||
|  |       expect(feedEpisodes[0].siteUrl).to.equal('/episode11') | ||||||
|  |       expect(feedEpisodes[0].enclosureUrl).to.equal('/enclosure11') | ||||||
|  |     }) | ||||||
|  |   }) | ||||||
|  | }) | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user