mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Update:Remove rss feed dependencies add node-xml lib
This commit is contained in:
		
							parent
							
								
									46fc89e247
								
							
						
					
					
						commit
						03bffb725a
					
				
							
								
								
									
										81
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										81
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -5,7 +5,7 @@ | |||||||
|   "requires": true, |   "requires": true, | ||||||
|   "packages": { |   "packages": { | ||||||
|     "": { |     "": { | ||||||
|       "version": "2.0.17", |       "version": "2.0.20", | ||||||
|       "license": "GPL-3.0", |       "license": "GPL-3.0", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "archiver": "^5.3.0", |         "archiver": "^5.3.0", | ||||||
| @ -26,7 +26,6 @@ | |||||||
|         "node-cron": "^3.0.0", |         "node-cron": "^3.0.0", | ||||||
|         "node-ffprobe": "^3.0.0", |         "node-ffprobe": "^3.0.0", | ||||||
|         "node-stream-zip": "^1.15.0", |         "node-stream-zip": "^1.15.0", | ||||||
|         "podcast": "^2.0.0", |  | ||||||
|         "proper-lockfile": "^4.1.2", |         "proper-lockfile": "^4.1.2", | ||||||
|         "read-chunk": "^3.1.0", |         "read-chunk": "^3.1.0", | ||||||
|         "recursive-readdir-async": "^1.1.8", |         "recursive-readdir-async": "^1.1.8", | ||||||
| @ -1560,14 +1559,6 @@ | |||||||
|         "node": ">=6" |         "node": ">=6" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/podcast": { |  | ||||||
|       "version": "2.0.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/podcast/-/podcast-2.0.0.tgz", |  | ||||||
|       "integrity": "sha512-1NZe7cVabfkcMe39yOOhBMJrXC6OROLOIBkfEPayiJL59ncyJmOeO5bxolSCSGroho8jQ0zURMczyICI1U/xbw==", |  | ||||||
|       "dependencies": { |  | ||||||
|         "rss": "^1.2.2" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/process-nextick-args": { |     "node_modules/process-nextick-args": { | ||||||
|       "version": "2.0.1", |       "version": "2.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", |       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", | ||||||
| @ -1713,34 +1704,6 @@ | |||||||
|         "node": ">= 4" |         "node": ">= 4" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/rss": { |  | ||||||
|       "version": "1.2.2", |  | ||||||
|       "resolved": "https://registry.npmjs.org/rss/-/rss-1.2.2.tgz", |  | ||||||
|       "integrity": "sha1-UKFpiHYTgTOnT5oF0r3I240nqSE=", |  | ||||||
|       "dependencies": { |  | ||||||
|         "mime-types": "2.1.13", |  | ||||||
|         "xml": "1.0.1" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/rss/node_modules/mime-db": { |  | ||||||
|       "version": "1.25.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz", |  | ||||||
|       "integrity": "sha1-wY29fHOl2/b0SgJNwNFloeexw5I=", |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">= 0.6" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/rss/node_modules/mime-types": { |  | ||||||
|       "version": "2.1.13", |  | ||||||
|       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz", |  | ||||||
|       "integrity": "sha1-4HqqnGxrmnyjASxpADrSWjnpKog=", |  | ||||||
|       "dependencies": { |  | ||||||
|         "mime-db": "~1.25.0" |  | ||||||
|       }, |  | ||||||
|       "engines": { |  | ||||||
|         "node": ">= 0.6" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/safe-buffer": { |     "node_modules/safe-buffer": { | ||||||
|       "version": "5.2.1", |       "version": "5.2.1", | ||||||
|       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", |       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", | ||||||
| @ -2072,11 +2035,6 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/xml": { |  | ||||||
|       "version": "1.0.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", |  | ||||||
|       "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=" |  | ||||||
|     }, |  | ||||||
|     "node_modules/xml2js": { |     "node_modules/xml2js": { | ||||||
|       "version": "0.4.23", |       "version": "0.4.23", | ||||||
|       "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", |       "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", | ||||||
| @ -3271,14 +3229,6 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", |       "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", | ||||||
|       "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" |       "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" | ||||||
|     }, |     }, | ||||||
|     "podcast": { |  | ||||||
|       "version": "2.0.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/podcast/-/podcast-2.0.0.tgz", |  | ||||||
|       "integrity": "sha512-1NZe7cVabfkcMe39yOOhBMJrXC6OROLOIBkfEPayiJL59ncyJmOeO5bxolSCSGroho8jQ0zURMczyICI1U/xbw==", |  | ||||||
|       "requires": { |  | ||||||
|         "rss": "^1.2.2" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "process-nextick-args": { |     "process-nextick-args": { | ||||||
|       "version": "2.0.1", |       "version": "2.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", |       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", | ||||||
| @ -3391,30 +3341,6 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", |       "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", | ||||||
|       "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" |       "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" | ||||||
|     }, |     }, | ||||||
|     "rss": { |  | ||||||
|       "version": "1.2.2", |  | ||||||
|       "resolved": "https://registry.npmjs.org/rss/-/rss-1.2.2.tgz", |  | ||||||
|       "integrity": "sha1-UKFpiHYTgTOnT5oF0r3I240nqSE=", |  | ||||||
|       "requires": { |  | ||||||
|         "mime-types": "2.1.13", |  | ||||||
|         "xml": "1.0.1" |  | ||||||
|       }, |  | ||||||
|       "dependencies": { |  | ||||||
|         "mime-db": { |  | ||||||
|           "version": "1.25.0", |  | ||||||
|           "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.25.0.tgz", |  | ||||||
|           "integrity": "sha1-wY29fHOl2/b0SgJNwNFloeexw5I=" |  | ||||||
|         }, |  | ||||||
|         "mime-types": { |  | ||||||
|           "version": "2.1.13", |  | ||||||
|           "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.13.tgz", |  | ||||||
|           "integrity": "sha1-4HqqnGxrmnyjASxpADrSWjnpKog=", |  | ||||||
|           "requires": { |  | ||||||
|             "mime-db": "~1.25.0" |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "safe-buffer": { |     "safe-buffer": { | ||||||
|       "version": "5.2.1", |       "version": "5.2.1", | ||||||
|       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", |       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", | ||||||
| @ -3654,11 +3580,6 @@ | |||||||
|       "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", |       "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", | ||||||
|       "requires": {} |       "requires": {} | ||||||
|     }, |     }, | ||||||
|     "xml": { |  | ||||||
|       "version": "1.0.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", |  | ||||||
|       "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=" |  | ||||||
|     }, |  | ||||||
|     "xml2js": { |     "xml2js": { | ||||||
|       "version": "0.4.23", |       "version": "0.4.23", | ||||||
|       "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", |       "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", | ||||||
|  | |||||||
| @ -45,7 +45,6 @@ | |||||||
|     "node-cron": "^3.0.0", |     "node-cron": "^3.0.0", | ||||||
|     "node-ffprobe": "^3.0.0", |     "node-ffprobe": "^3.0.0", | ||||||
|     "node-stream-zip": "^1.15.0", |     "node-stream-zip": "^1.15.0", | ||||||
|     "podcast": "^2.0.0", |  | ||||||
|     "proper-lockfile": "^4.1.2", |     "proper-lockfile": "^4.1.2", | ||||||
|     "read-chunk": "^3.1.0", |     "read-chunk": "^3.1.0", | ||||||
|     "recursive-readdir-async": "^1.1.8", |     "recursive-readdir-async": "^1.1.8", | ||||||
|  | |||||||
							
								
								
									
										193
									
								
								server/libs/rss/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								server/libs/rss/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,193 @@ | |||||||
|  | // node-rss
 | ||||||
|  | // SOURCE: https://github.com/dylang/node-rss
 | ||||||
|  | // LICENSE: https://creativecommons.org/licenses/by-sa/4.0/
 | ||||||
|  | 
 | ||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | var mime = require('mime-types'); | ||||||
|  | var xml = require('../xml'); | ||||||
|  | var fs = require('fs'); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function ifTruePush(bool, array, data) { | ||||||
|  |     if (bool) { | ||||||
|  |         array.push(data); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function ifTruePushArray(bool, array, dataArray) { | ||||||
|  |     if (!bool) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     dataArray.forEach(function (item) { | ||||||
|  |         ifTruePush(item, array, item); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function getSize(filename) { | ||||||
|  |     if (typeof fs === 'undefined') { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |     return fs.statSync(filename).size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function generateXML(data) { | ||||||
|  | 
 | ||||||
|  |     var channel = []; | ||||||
|  |     channel.push({ title: { _cdata: data.title } }); | ||||||
|  |     channel.push({ description: { _cdata: data.description || data.title } }); | ||||||
|  |     channel.push({ link: data.site_url || 'http://github.com/dylang/node-rss' }); | ||||||
|  |     // image_url set?
 | ||||||
|  |     if (data.image_url) { | ||||||
|  |         channel.push({ image: [{ url: data.image_url }, { title: data.title }, { link: data.site_url }] }); | ||||||
|  |     } | ||||||
|  |     channel.push({ generator: data.generator }); | ||||||
|  |     channel.push({ lastBuildDate: new Date().toUTCString() }); | ||||||
|  | 
 | ||||||
|  |     ifTruePush(data.feed_url, channel, { 'atom:link': { _attr: { href: data.feed_url, rel: 'self', type: 'application/rss+xml' } } }); | ||||||
|  |     ifTruePush(data.author, channel, { 'author': { _cdata: data.author } }); | ||||||
|  |     ifTruePush(data.pubDate, channel, { 'pubDate': new Date(data.pubDate).toGMTString() }); | ||||||
|  |     ifTruePush(data.copyright, channel, { 'copyright': { _cdata: data.copyright } }); | ||||||
|  |     ifTruePush(data.language, channel, { 'language': { _cdata: data.language } }); | ||||||
|  |     ifTruePush(data.managingEditor, channel, { 'managingEditor': { _cdata: data.managingEditor } }); | ||||||
|  |     ifTruePush(data.webMaster, channel, { 'webMaster': { _cdata: data.webMaster } }); | ||||||
|  |     ifTruePush(data.docs, channel, { 'docs': data.docs }); | ||||||
|  |     ifTruePush(data.ttl, channel, { 'ttl': data.ttl }); | ||||||
|  |     ifTruePush(data.hub, channel, { 'atom:link': { _attr: { href: data.hub, rel: 'hub' } } }); | ||||||
|  | 
 | ||||||
|  |     if (data.categories) { | ||||||
|  |         data.categories.forEach(function (category) { | ||||||
|  |             ifTruePush(category, channel, { category: { _cdata: category } }); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ifTruePushArray(data.custom_elements, channel, data.custom_elements); | ||||||
|  | 
 | ||||||
|  |     data.items.forEach(function (item) { | ||||||
|  |         var item_values = [ | ||||||
|  |             { title: { _cdata: item.title } } | ||||||
|  |         ]; | ||||||
|  |         ifTruePush(item.description, item_values, { description: { _cdata: item.description } }); | ||||||
|  |         ifTruePush(item.url, item_values, { link: item.url }); | ||||||
|  |         ifTruePush(item.link || item.guid || item.title, item_values, { guid: [{ _attr: { isPermaLink: !item.guid && !!item.url } }, item.guid || item.url || item.title] }); | ||||||
|  | 
 | ||||||
|  |         item.categories.forEach(function (category) { | ||||||
|  |             ifTruePush(category, item_values, { category: { _cdata: category } }); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         ifTruePush(item.author || data.author, item_values, { 'dc:creator': { _cdata: item.author || data.author } }); | ||||||
|  |         ifTruePush(item.date, item_values, { pubDate: new Date(item.date).toGMTString() }); | ||||||
|  | 
 | ||||||
|  |         //Set GeoRSS to true if lat and long are set
 | ||||||
|  |         data.geoRSS = data.geoRSS || (item.lat && item.long); | ||||||
|  |         ifTruePush(item.lat, item_values, { 'geo:lat': item.lat }); | ||||||
|  |         ifTruePush(item.long, item_values, { 'geo:long': item.long }); | ||||||
|  | 
 | ||||||
|  |         if (item.enclosure && item.enclosure.url) { | ||||||
|  |             if (item.enclosure.file) { | ||||||
|  |                 item_values.push({ | ||||||
|  |                     enclosure: { | ||||||
|  |                         _attr: { | ||||||
|  |                             url: item.enclosure.url, | ||||||
|  |                             length: item.enclosure.size || getSize(item.enclosure.file), | ||||||
|  |                             type: item.enclosure.type || mime.lookup(item.enclosure.file) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } else { | ||||||
|  |                 item_values.push({ | ||||||
|  |                     enclosure: { | ||||||
|  |                         _attr: { | ||||||
|  |                             url: item.enclosure.url, | ||||||
|  |                             length: item.enclosure.size || 0, | ||||||
|  |                             type: item.enclosure.type || mime.lookup(item.enclosure.url) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ifTruePushArray(item.custom_elements, item_values, item.custom_elements); | ||||||
|  | 
 | ||||||
|  |         channel.push({ item: item_values }); | ||||||
|  | 
 | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     //set up the attributes for the RSS feed.
 | ||||||
|  |     var _attr = { | ||||||
|  |         'xmlns:dc': 'http://purl.org/dc/elements/1.1/', | ||||||
|  |         'xmlns:content': 'http://purl.org/rss/1.0/modules/content/', | ||||||
|  |         'xmlns:atom': 'http://www.w3.org/2005/Atom', | ||||||
|  |         version: '2.0' | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Object.keys(data.custom_namespaces).forEach(function (name) { | ||||||
|  |         _attr['xmlns:' + name] = data.custom_namespaces[name]; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     //only add namespace if GeoRSS is true
 | ||||||
|  |     if (data.geoRSS) { | ||||||
|  |         _attr['xmlns:geo'] = 'http://www.w3.org/2003/01/geo/wgs84_pos#'; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return { | ||||||
|  |         rss: [ | ||||||
|  |             { _attr: _attr }, | ||||||
|  |             { channel: channel } | ||||||
|  |         ] | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function RSS(options, items) { | ||||||
|  |     options = options || {}; | ||||||
|  | 
 | ||||||
|  |     this.title = options.title || 'Untitled RSS Feed'; | ||||||
|  |     this.description = options.description || ''; | ||||||
|  |     this.generator = options.generator || 'RSS for Node'; | ||||||
|  |     this.feed_url = options.feed_url; | ||||||
|  |     this.site_url = options.site_url; | ||||||
|  |     this.image_url = options.image_url; | ||||||
|  |     this.author = options.author; | ||||||
|  |     this.categories = options.categories; | ||||||
|  |     this.pubDate = options.pubDate; | ||||||
|  |     this.hub = options.hub; | ||||||
|  |     this.docs = options.docs; | ||||||
|  |     this.copyright = options.copyright; | ||||||
|  |     this.language = options.language; | ||||||
|  |     this.managingEditor = options.managingEditor; | ||||||
|  |     this.webMaster = options.webMaster; | ||||||
|  |     this.ttl = options.ttl; | ||||||
|  |     //option to return feed as GeoRSS is set automatically if feed.lat/long is used
 | ||||||
|  |     this.geoRSS = options.geoRSS || false; | ||||||
|  |     this.custom_namespaces = options.custom_namespaces || {}; | ||||||
|  |     this.custom_elements = options.custom_elements || []; | ||||||
|  |     this.items = items || []; | ||||||
|  | 
 | ||||||
|  |     this.item = function (options) { | ||||||
|  |         options = options || {}; | ||||||
|  |         var item = { | ||||||
|  |             title: options.title || 'No title', | ||||||
|  |             description: options.description || '', | ||||||
|  |             url: options.url, | ||||||
|  |             guid: options.guid, | ||||||
|  |             categories: options.categories || [], | ||||||
|  |             author: options.author, | ||||||
|  |             date: options.date, | ||||||
|  |             lat: options.lat, | ||||||
|  |             long: options.long, | ||||||
|  |             enclosure: options.enclosure || false, | ||||||
|  |             custom_elements: options.custom_elements || [] | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         this.items.push(item); | ||||||
|  |         return this; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     this.xml = function (indent) { | ||||||
|  |         return '<?xml version="1.0" encoding="UTF-8"?>' + | ||||||
|  |             xml(generateXML(this), indent); | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = RSS; | ||||||
							
								
								
									
										17
									
								
								server/libs/xml/escapeForXML.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								server/libs/xml/escapeForXML.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | |||||||
|  | var XML_CHARACTER_MAP = { | ||||||
|  |   '&': '&', | ||||||
|  |   '"': '"', | ||||||
|  |   "'": ''', | ||||||
|  |   '<': '<', | ||||||
|  |   '>': '>' | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | function escapeForXML(string) { | ||||||
|  |   return string && string.replace | ||||||
|  |     ? string.replace(/([&"<>'])/g, function (str, item) { | ||||||
|  |       return XML_CHARACTER_MAP[item]; | ||||||
|  |     }) | ||||||
|  |     : string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = escapeForXML; | ||||||
							
								
								
									
										286
									
								
								server/libs/xml/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								server/libs/xml/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,286 @@ | |||||||
|  | // node-xml
 | ||||||
|  | // SOURCE: https://github.com/dylang/node-xml
 | ||||||
|  | // LICENSE: https://github.com/dylang/node-xml/blob/master/LICENSE
 | ||||||
|  | 
 | ||||||
|  | var escapeForXML = require('./escapeForXML'); | ||||||
|  | var Stream = require('stream').Stream; | ||||||
|  | 
 | ||||||
|  | var DEFAULT_INDENT = '    '; | ||||||
|  | 
 | ||||||
|  | function xml(input, options) { | ||||||
|  | 
 | ||||||
|  |   if (typeof options !== 'object') { | ||||||
|  |     options = { | ||||||
|  |       indent: options | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   var stream = options.stream ? new Stream() : null, | ||||||
|  |     output = "", | ||||||
|  |     interrupted = false, | ||||||
|  |     indent = !options.indent ? '' | ||||||
|  |       : options.indent === true ? DEFAULT_INDENT | ||||||
|  |         : options.indent, | ||||||
|  |     instant = true; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   function delay(func) { | ||||||
|  |     if (!instant) { | ||||||
|  |       func(); | ||||||
|  |     } else { | ||||||
|  |       process.nextTick(func); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function append(interrupt, out) { | ||||||
|  |     if (out !== undefined) { | ||||||
|  |       output += out; | ||||||
|  |     } | ||||||
|  |     if (interrupt && !interrupted) { | ||||||
|  |       stream = stream || new Stream(); | ||||||
|  |       interrupted = true; | ||||||
|  |     } | ||||||
|  |     if (interrupt && interrupted) { | ||||||
|  |       var data = output; | ||||||
|  |       delay(function () { stream.emit('data', data) }); | ||||||
|  |       output = ""; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function add(value, last) { | ||||||
|  |     format(append, resolve(value, indent, indent ? 1 : 0), last); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function end() { | ||||||
|  |     if (stream) { | ||||||
|  |       var data = output; | ||||||
|  |       delay(function () { | ||||||
|  |         stream.emit('data', data); | ||||||
|  |         stream.emit('end'); | ||||||
|  |         stream.readable = false; | ||||||
|  |         stream.emit('close'); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function addXmlDeclaration(declaration) { | ||||||
|  |     var encoding = declaration.encoding || 'UTF-8', | ||||||
|  |       attr = { version: '1.0', encoding: encoding }; | ||||||
|  | 
 | ||||||
|  |     if (declaration.standalone) { | ||||||
|  |       attr.standalone = declaration.standalone | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     add({ '?xml': { _attr: attr } }); | ||||||
|  |     output = output.replace('/>', '?>'); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // disable delay delayed
 | ||||||
|  |   delay(function () { instant = false }); | ||||||
|  | 
 | ||||||
|  |   if (options.declaration) { | ||||||
|  |     addXmlDeclaration(options.declaration); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (input && input.forEach) { | ||||||
|  |     input.forEach(function (value, i) { | ||||||
|  |       var last; | ||||||
|  |       if (i + 1 === input.length) | ||||||
|  |         last = end; | ||||||
|  |       add(value, last); | ||||||
|  |     }); | ||||||
|  |   } else { | ||||||
|  |     add(input, end); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (stream) { | ||||||
|  |     stream.readable = true; | ||||||
|  |     return stream; | ||||||
|  |   } | ||||||
|  |   return output; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function element(/*input, …*/) { | ||||||
|  |   var input = Array.prototype.slice.call(arguments), | ||||||
|  |     self = { | ||||||
|  |       _elem: resolve(input) | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |   self.push = function (input) { | ||||||
|  |     if (!this.append) { | ||||||
|  |       throw new Error("not assigned to a parent!"); | ||||||
|  |     } | ||||||
|  |     var that = this; | ||||||
|  |     var indent = this._elem.indent; | ||||||
|  |     format(this.append, resolve( | ||||||
|  |       input, indent, this._elem.icount + (indent ? 1 : 0)), | ||||||
|  |       function () { that.append(true) }); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   self.close = function (input) { | ||||||
|  |     if (input !== undefined) { | ||||||
|  |       this.push(input); | ||||||
|  |     } | ||||||
|  |     if (this.end) { | ||||||
|  |       this.end(); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return self; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function create_indent(character, count) { | ||||||
|  |   return (new Array(count || 0).join(character || '')) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function resolve(data, indent, indent_count) { | ||||||
|  |   indent_count = indent_count || 0; | ||||||
|  |   var indent_spaces = create_indent(indent, indent_count); | ||||||
|  |   var name; | ||||||
|  |   var values = data; | ||||||
|  |   var interrupt = false; | ||||||
|  | 
 | ||||||
|  |   if (typeof data === 'object') { | ||||||
|  |     var keys = Object.keys(data); | ||||||
|  |     name = keys[0]; | ||||||
|  |     values = data[name]; | ||||||
|  | 
 | ||||||
|  |     if (values && values._elem) { | ||||||
|  |       values._elem.name = name; | ||||||
|  |       values._elem.icount = indent_count; | ||||||
|  |       values._elem.indent = indent; | ||||||
|  |       values._elem.indents = indent_spaces; | ||||||
|  |       values._elem.interrupt = values; | ||||||
|  |       return values._elem; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   var attributes = [], | ||||||
|  |     content = []; | ||||||
|  | 
 | ||||||
|  |   var isStringContent; | ||||||
|  | 
 | ||||||
|  |   function get_attributes(obj) { | ||||||
|  |     var keys = Object.keys(obj); | ||||||
|  |     keys.forEach(function (key) { | ||||||
|  |       attributes.push(attribute(key, obj[key])); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   switch (typeof values) { | ||||||
|  |     case 'object': | ||||||
|  |       if (values === null) break; | ||||||
|  | 
 | ||||||
|  |       if (values._attr) { | ||||||
|  |         get_attributes(values._attr); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (values._cdata) { | ||||||
|  |         content.push( | ||||||
|  |           ('<![CDATA[' + values._cdata).replace(/\]\]>/g, ']]]]><![CDATA[>') + ']]>' | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (values.forEach) { | ||||||
|  |         isStringContent = false; | ||||||
|  |         content.push(''); | ||||||
|  |         values.forEach(function (value) { | ||||||
|  |           if (typeof value == 'object') { | ||||||
|  |             var _name = Object.keys(value)[0]; | ||||||
|  | 
 | ||||||
|  |             if (_name == '_attr') { | ||||||
|  |               get_attributes(value._attr); | ||||||
|  |             } else { | ||||||
|  |               content.push(resolve( | ||||||
|  |                 value, indent, indent_count + 1)); | ||||||
|  |             } | ||||||
|  |           } else { | ||||||
|  |             //string
 | ||||||
|  |             content.pop(); | ||||||
|  |             isStringContent = true; | ||||||
|  |             content.push(escapeForXML(value)); | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |         }); | ||||||
|  |         if (!isStringContent) { | ||||||
|  |           content.push(''); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |       //string
 | ||||||
|  |       content.push(escapeForXML(values)); | ||||||
|  | 
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     name: name, | ||||||
|  |     interrupt: interrupt, | ||||||
|  |     attributes: attributes, | ||||||
|  |     content: content, | ||||||
|  |     icount: indent_count, | ||||||
|  |     indents: indent_spaces, | ||||||
|  |     indent: indent | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function format(append, elem, end) { | ||||||
|  | 
 | ||||||
|  |   if (typeof elem != 'object') { | ||||||
|  |     return append(false, elem); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   var len = elem.interrupt ? 1 : elem.content.length; | ||||||
|  | 
 | ||||||
|  |   function proceed() { | ||||||
|  |     while (elem.content.length) { | ||||||
|  |       var value = elem.content.shift(); | ||||||
|  | 
 | ||||||
|  |       if (value === undefined) continue; | ||||||
|  |       if (interrupt(value)) return; | ||||||
|  | 
 | ||||||
|  |       format(append, value); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     append(false, (len > 1 ? elem.indents : '') | ||||||
|  |       + (elem.name ? '</' + elem.name + '>' : '') | ||||||
|  |       + (elem.indent && !end ? '\n' : '')); | ||||||
|  | 
 | ||||||
|  |     if (end) { | ||||||
|  |       end(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function interrupt(value) { | ||||||
|  |     if (value.interrupt) { | ||||||
|  |       value.interrupt.append = append; | ||||||
|  |       value.interrupt.end = proceed; | ||||||
|  |       value.interrupt = false; | ||||||
|  |       append(true); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   append(false, elem.indents | ||||||
|  |     + (elem.name ? '<' + elem.name : '') | ||||||
|  |     + (elem.attributes.length ? ' ' + elem.attributes.join(' ') : '') | ||||||
|  |     + (len ? (elem.name ? '>' : '') : (elem.name ? '/>' : '')) | ||||||
|  |     + (elem.indent && len > 1 ? '\n' : '')); | ||||||
|  | 
 | ||||||
|  |   if (!len) { | ||||||
|  |     return append(false, elem.indent ? '\n' : ''); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (!interrupt(elem)) { | ||||||
|  |     proceed(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function attribute(key, value) { | ||||||
|  |   return key + '=' + '"' + escapeForXML(value) + '"'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = xml; | ||||||
|  | module.exports.element = module.exports.Element = element; | ||||||
| @ -1,7 +1,5 @@ | |||||||
| const Path = require('path') | const Path = require('path') | ||||||
| const fs = require('fs-extra') | const fs = require('fs-extra') | ||||||
| const date = require('date-and-time') |  | ||||||
| const { Podcast } = require('podcast') |  | ||||||
| const Feed = require('../objects/Feed') | const Feed = require('../objects/Feed') | ||||||
| const Logger = require('../Logger') | const Logger = require('../Logger') | ||||||
| 
 | 
 | ||||||
| @ -35,7 +33,7 @@ class RssFeedManager { | |||||||
|       res.sendStatus(404) |       res.sendStatus(404) | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
|     // var xml = feedData.feed.buildXml()
 | 
 | ||||||
|     var xml = feed.buildXml() |     var xml = feed.buildXml() | ||||||
|     res.set('Content-Type', 'text/xml') |     res.set('Content-Type', 'text/xml') | ||||||
|     res.send(xml) |     res.send(xml) | ||||||
| @ -55,9 +53,6 @@ class RssFeedManager { | |||||||
|       return |       return | ||||||
|     } |     } | ||||||
|     res.sendFile(episodePath) |     res.sendFile(episodePath) | ||||||
|     // var remainingPath = req.params['0']
 |  | ||||||
|     // var fullPath = Path.join(feedData.libraryItemPath, remainingPath)
 |  | ||||||
|     // res.sendFile(fullPath)
 |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getFeedCover(req, res) { |   getFeedCover(req, res) { | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| const FeedMeta = require('./FeedMeta') | const FeedMeta = require('./FeedMeta') | ||||||
| const FeedEpisode = require('./FeedEpisode') | const FeedEpisode = require('./FeedEpisode') | ||||||
| const { Podcast } = require('podcast') | const RSS = require('../libs/rss') | ||||||
| 
 | 
 | ||||||
| class Feed { | class Feed { | ||||||
|   constructor(feed) { |   constructor(feed) { | ||||||
| @ -90,6 +90,7 @@ class Feed { | |||||||
|     this.meta.imageUrl = media.coverPath ? `${serverAddress}/feed/${slug}/cover` : `${serverAddress}/Logo.png` |     this.meta.imageUrl = media.coverPath ? `${serverAddress}/feed/${slug}/cover` : `${serverAddress}/Logo.png` | ||||||
|     this.meta.feedUrl = feedUrl |     this.meta.feedUrl = feedUrl | ||||||
|     this.meta.link = `${serverAddress}/items/${libraryItem.id}` |     this.meta.link = `${serverAddress}/items/${libraryItem.id}` | ||||||
|  |     this.meta.explicit = !!mediaMetadata.explicit | ||||||
| 
 | 
 | ||||||
|     this.episodes = [] |     this.episodes = [] | ||||||
|     if (isPodcast) { // PODCAST EPISODES
 |     if (isPodcast) { // PODCAST EPISODES
 | ||||||
| @ -113,11 +114,11 @@ class Feed { | |||||||
|   buildXml() { |   buildXml() { | ||||||
|     if (this.xml) return this.xml |     if (this.xml) return this.xml | ||||||
| 
 | 
 | ||||||
|     const pod = new Podcast(this.meta.getPodcastMeta()) |     var rssfeed = new RSS(this.meta.getRSSData()) | ||||||
|     this.episodes.forEach((ep) => { |     this.episodes.forEach((ep) => { | ||||||
|       pod.addItem(ep.getPodcastEpisode()) |       rssfeed.item(ep.getRSSData()) | ||||||
|     }) |     }) | ||||||
|     this.xml = pod.buildXml() |     this.xml = rssfeed.xml() | ||||||
|     return this.xml |     return this.xml | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| const Path = require('path') | const Path = require('path') | ||||||
| const date = require('date-and-time') | const date = require('date-and-time') | ||||||
|  | const { secondsToTimestamp } = require('../utils/index') | ||||||
| 
 | 
 | ||||||
| class FeedEpisode { | class FeedEpisode { | ||||||
|   constructor(episode) { |   constructor(episode) { | ||||||
| @ -116,14 +117,23 @@ class FeedEpisode { | |||||||
|     this.fullPath = audioTrack.metadata.path |     this.fullPath = audioTrack.metadata.path | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getPodcastEpisode() { |   getRSSData() { | ||||||
|     return { |     return { | ||||||
|       title: this.title, |       title: this.title, | ||||||
|       description: this.description || '', |       description: this.description || '', | ||||||
|       enclosure: this.enclosure, |  | ||||||
|       date: this.pubDate || '', |  | ||||||
|       url: this.link, |       url: this.link, | ||||||
|       author: this.author |       guid: this.enclosure.url, | ||||||
|  |       author: this.author, | ||||||
|  |       date: this.pubDate, | ||||||
|  |       enclosure: this.enclosure, | ||||||
|  |       custom_elements: [ | ||||||
|  |         { 'itunes:author': this.author }, | ||||||
|  |         { 'itunes:duration': secondsToTimestamp(this.duration) }, | ||||||
|  |         { 'itunes:summary': this.description || '' }, | ||||||
|  |         { | ||||||
|  |           "itunes:explicit": !!this.explicit | ||||||
|  |         } | ||||||
|  |       ] | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ class FeedMeta { | |||||||
|     this.imageUrl = null |     this.imageUrl = null | ||||||
|     this.feedUrl = null |     this.feedUrl = null | ||||||
|     this.link = null |     this.link = null | ||||||
|  |     this.explicit = null | ||||||
| 
 | 
 | ||||||
|     if (meta) { |     if (meta) { | ||||||
|       this.construct(meta) |       this.construct(meta) | ||||||
| @ -19,6 +20,7 @@ class FeedMeta { | |||||||
|     this.imageUrl = meta.imageUrl |     this.imageUrl = meta.imageUrl | ||||||
|     this.feedUrl = meta.feedUrl |     this.feedUrl = meta.feedUrl | ||||||
|     this.link = meta.link |     this.link = meta.link | ||||||
|  |     this.explicit = meta.explicit | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   toJSON() { |   toJSON() { | ||||||
| @ -28,19 +30,46 @@ class FeedMeta { | |||||||
|       author: this.author, |       author: this.author, | ||||||
|       imageUrl: this.imageUrl, |       imageUrl: this.imageUrl, | ||||||
|       feedUrl: this.feedUrl, |       feedUrl: this.feedUrl, | ||||||
|       link: this.link |       link: this.link, | ||||||
|  |       explicit: this.explicit | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getPodcastMeta() { |   getRSSData() { | ||||||
|     return { |     return { | ||||||
|       title: this.title, |       title: this.title, | ||||||
|       description: this.description, |       description: this.description || '', | ||||||
|       feedUrl: this.feedUrl, |       generator: 'Audiobookshelf', | ||||||
|       siteUrl: this.link, |       feed_url: this.feedUrl, | ||||||
|       imageUrl: this.imageUrl, |       site_url: this.link, | ||||||
|       author: this.author || 'advplyr', |       image_url: this.imageUrl, | ||||||
|       language: 'en' |       language: 'en', | ||||||
|  |       custom_namespaces: { | ||||||
|  |         'itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd', | ||||||
|  |         'psc': 'http://podlove.org/simple-chapters', | ||||||
|  |         'podcast': 'https://podcastindex.org/namespace/1.0' | ||||||
|  |       }, | ||||||
|  |       custom_elements: [ | ||||||
|  |         { 'author': this.author || 'advplyr' }, | ||||||
|  |         { 'itunes:author': this.author || 'advplyr' }, | ||||||
|  |         { 'itunes:summary': this.description || '' }, | ||||||
|  |         { | ||||||
|  |           'itunes:image': { | ||||||
|  |             _attr: { | ||||||
|  |               href: this.imageUrl | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           'itunes:owner': [ | ||||||
|  |             { 'itunes:name': this.author || '' }, | ||||||
|  |             { 'itunes:email': '' } | ||||||
|  |           ] | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           "itunes:explicit": !!this.explicit | ||||||
|  |         } | ||||||
|  |       ] | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user