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, | ||||
|   "packages": { | ||||
|     "": { | ||||
|       "version": "2.0.17", | ||||
|       "version": "2.0.20", | ||||
|       "license": "GPL-3.0", | ||||
|       "dependencies": { | ||||
|         "archiver": "^5.3.0", | ||||
| @ -26,7 +26,6 @@ | ||||
|         "node-cron": "^3.0.0", | ||||
|         "node-ffprobe": "^3.0.0", | ||||
|         "node-stream-zip": "^1.15.0", | ||||
|         "podcast": "^2.0.0", | ||||
|         "proper-lockfile": "^4.1.2", | ||||
|         "read-chunk": "^3.1.0", | ||||
|         "recursive-readdir-async": "^1.1.8", | ||||
| @ -1560,14 +1559,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": { | ||||
|       "version": "2.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", | ||||
| @ -1713,34 +1704,6 @@ | ||||
|         "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": { | ||||
|       "version": "5.2.1", | ||||
|       "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": { | ||||
|       "version": "0.4.23", | ||||
|       "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", | ||||
|       "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": { | ||||
|       "version": "2.0.1", | ||||
|       "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", | ||||
|       "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": { | ||||
|       "version": "5.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", | ||||
| @ -3654,11 +3580,6 @@ | ||||
|       "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", | ||||
|       "requires": {} | ||||
|     }, | ||||
|     "xml": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", | ||||
|       "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=" | ||||
|     }, | ||||
|     "xml2js": { | ||||
|       "version": "0.4.23", | ||||
|       "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", | ||||
|  | ||||
| @ -45,7 +45,6 @@ | ||||
|     "node-cron": "^3.0.0", | ||||
|     "node-ffprobe": "^3.0.0", | ||||
|     "node-stream-zip": "^1.15.0", | ||||
|     "podcast": "^2.0.0", | ||||
|     "proper-lockfile": "^4.1.2", | ||||
|     "read-chunk": "^3.1.0", | ||||
|     "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 fs = require('fs-extra') | ||||
| const date = require('date-and-time') | ||||
| const { Podcast } = require('podcast') | ||||
| const Feed = require('../objects/Feed') | ||||
| const Logger = require('../Logger') | ||||
| 
 | ||||
| @ -35,7 +33,7 @@ class RssFeedManager { | ||||
|       res.sendStatus(404) | ||||
|       return | ||||
|     } | ||||
|     // var xml = feedData.feed.buildXml()
 | ||||
| 
 | ||||
|     var xml = feed.buildXml() | ||||
|     res.set('Content-Type', 'text/xml') | ||||
|     res.send(xml) | ||||
| @ -55,9 +53,6 @@ class RssFeedManager { | ||||
|       return | ||||
|     } | ||||
|     res.sendFile(episodePath) | ||||
|     // var remainingPath = req.params['0']
 | ||||
|     // var fullPath = Path.join(feedData.libraryItemPath, remainingPath)
 | ||||
|     // res.sendFile(fullPath)
 | ||||
|   } | ||||
| 
 | ||||
|   getFeedCover(req, res) { | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| const FeedMeta = require('./FeedMeta') | ||||
| const FeedEpisode = require('./FeedEpisode') | ||||
| const { Podcast } = require('podcast') | ||||
| const RSS = require('../libs/rss') | ||||
| 
 | ||||
| class Feed { | ||||
|   constructor(feed) { | ||||
| @ -90,6 +90,7 @@ class Feed { | ||||
|     this.meta.imageUrl = media.coverPath ? `${serverAddress}/feed/${slug}/cover` : `${serverAddress}/Logo.png` | ||||
|     this.meta.feedUrl = feedUrl | ||||
|     this.meta.link = `${serverAddress}/items/${libraryItem.id}` | ||||
|     this.meta.explicit = !!mediaMetadata.explicit | ||||
| 
 | ||||
|     this.episodes = [] | ||||
|     if (isPodcast) { // PODCAST EPISODES
 | ||||
| @ -113,11 +114,11 @@ class Feed { | ||||
|   buildXml() { | ||||
|     if (this.xml) return this.xml | ||||
| 
 | ||||
|     const pod = new Podcast(this.meta.getPodcastMeta()) | ||||
|     var rssfeed = new RSS(this.meta.getRSSData()) | ||||
|     this.episodes.forEach((ep) => { | ||||
|       pod.addItem(ep.getPodcastEpisode()) | ||||
|       rssfeed.item(ep.getRSSData()) | ||||
|     }) | ||||
|     this.xml = pod.buildXml() | ||||
|     this.xml = rssfeed.xml() | ||||
|     return this.xml | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| const Path = require('path') | ||||
| const date = require('date-and-time') | ||||
| const { secondsToTimestamp } = require('../utils/index') | ||||
| 
 | ||||
| class FeedEpisode { | ||||
|   constructor(episode) { | ||||
| @ -116,14 +117,23 @@ class FeedEpisode { | ||||
|     this.fullPath = audioTrack.metadata.path | ||||
|   } | ||||
| 
 | ||||
|   getPodcastEpisode() { | ||||
|   getRSSData() { | ||||
|     return { | ||||
|       title: this.title, | ||||
|       description: this.description || '', | ||||
|       enclosure: this.enclosure, | ||||
|       date: this.pubDate || '', | ||||
|       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.feedUrl = null | ||||
|     this.link = null | ||||
|     this.explicit = null | ||||
| 
 | ||||
|     if (meta) { | ||||
|       this.construct(meta) | ||||
| @ -19,6 +20,7 @@ class FeedMeta { | ||||
|     this.imageUrl = meta.imageUrl | ||||
|     this.feedUrl = meta.feedUrl | ||||
|     this.link = meta.link | ||||
|     this.explicit = meta.explicit | ||||
|   } | ||||
| 
 | ||||
|   toJSON() { | ||||
| @ -28,19 +30,46 @@ class FeedMeta { | ||||
|       author: this.author, | ||||
|       imageUrl: this.imageUrl, | ||||
|       feedUrl: this.feedUrl, | ||||
|       link: this.link | ||||
|       link: this.link, | ||||
|       explicit: this.explicit | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getPodcastMeta() { | ||||
|   getRSSData() { | ||||
|     return { | ||||
|       title: this.title, | ||||
|       description: this.description, | ||||
|       feedUrl: this.feedUrl, | ||||
|       siteUrl: this.link, | ||||
|       imageUrl: this.imageUrl, | ||||
|       author: this.author || 'advplyr', | ||||
|       language: 'en' | ||||
|       description: this.description || '', | ||||
|       generator: 'Audiobookshelf', | ||||
|       feed_url: this.feedUrl, | ||||
|       site_url: this.link, | ||||
|       image_url: this.imageUrl, | ||||
|       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