File size: 3,976 Bytes
95f4e64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
const Router = require('router')
const qs = require('querystring')
const cors = require('cors')

function getRouter({ manifest , get }) {
	const router = new Router()

	// CORS is mandatory for the addon protocol
	router.use(cors())

	// Serve the manifest
	const manifestBuf = JSON.stringify(manifest)
	function manifestHandler(req, res) {
		const { config } = req.params
		let manifestRespBuf = manifestBuf
		if (config && manifest.behaviorHints && (manifest.behaviorHints.configurationRequired || manifest.behaviorHints.configurable)) {
			const manifestClone = JSON.parse(manifestBuf)
			// we remove configurationRequired so the addon is installable after configuration
			delete manifestClone.behaviorHints.configurationRequired
			// we remove configuration page for installed addon too (could be added later to the router)
			delete manifestClone.behaviorHints.configurable			
			manifestRespBuf = JSON.stringify(manifestClone)
		}
		res.setHeader('Content-Type', 'application/json; charset=utf-8')
		res.end(manifestRespBuf)
	}

	const hasConfig = (manifest.config || []).length

	if (hasConfig && !(manifest.behaviorHints || {}).configurable) {
		console.warn('manifest.config is set but manifest.behaviorHints.configurable is disabled, the "Configure" button will not show in the Stremio apps')
	}

	const configPrefix = hasConfig ? '/:config?' : ''
	// having config prifix always set to '/:config?' won't resault in a problem for non configurable addons,
	// since now the order is restricted by resources.

	router.get(`${configPrefix}/manifest.json`, manifestHandler)

	// using the same methode used in builder.js to extract resources from manifest
	const handlersInManifest = []
	if (manifest.catalogs.length > 0) handlersInManifest.push('catalog')
	manifest.resources.forEach((r) => handlersInManifest.push(r.name || r))
	
	// converting the resources array to a regular expression
	const ResourcesRegex = handlersInManifest && handlersInManifest.length ? '(' + handlersInManifest.join('|') + ')' : '' 

	// Handle all resources
	router.get(`${configPrefix}/:resource${ResourcesRegex}/:type/:id/:extra?.json`, function(req, res, next) {
		const { resource, type, id } = req.params
		let { config } = req.params
		// we get `extra` from `req.url` because `req.params.extra` decodes the characters
		// and breaks dividing querystring parameters with `&`, in case `&` is one of the
		// encoded characters of a parameter value
		const extra = req.params.extra ? qs.parse(req.url.split('/').pop().slice(0, -5)) : {}
		if ((config || '').length) {
			try {
				config = JSON.parse(config)
			} catch(e) {
				config = false
			}
		}
		res.setHeader('Content-Type', 'application/json; charset=utf-8')
		get(resource, type, id, extra, config, req)
			.then(resp => {

				let cacheHeaders = {
					cacheMaxAge: 'max-age',
					staleRevalidate: 'stale-while-revalidate',
					staleError: 'stale-if-error'
				}

				const cacheControl = Object.keys(cacheHeaders).map(prop => {
					const cacheProp = cacheHeaders[prop]
					const cacheValue = resp[prop]
					if (!Number.isInteger(cacheValue)) return false
					if (cacheValue > 365 * 24 * 60 * 60)
						console.warn(`${prop} set to more then 1 year, be advised that cache times are in seconds, not milliseconds.`)
					return cacheProp + '=' + cacheValue
				}).filter(val => !!val).join(', ')

				if (cacheControl)
					res.setHeader('Cache-Control', `${cacheControl}, public`)

				if (resp.redirect) {
					res.redirect(307, resp.redirect)
					return
				}

				res.setHeader('Content-Type', 'application/json; charset=utf-8')

				res.end(JSON.stringify(resp))
			})
			.catch(err => {
				if (err.noHandler) {
					if (next) next()
					else {
						res.writeHead(404)
						res.end(JSON.stringify({ err: 'not found' }))
					}
				} else {
					console.error(err)
					res.writeHead(500)
					res.end(JSON.stringify({ err: 'handler error' }))
				}
			})
	})

	return router
}

module.exports = getRouter