-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.js
156 lines (139 loc) · 3.99 KB
/
index.js
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
const url = require('url')
const http = require('http')
const fetch = require('node-fetch')
const puppeteer = require('puppeteer')
// Base JSON Template of what will be returned,
// is modified by updateDate and send on every request
const jsonTemplate = {
titleText: 'N.O.S. Radio News',
mainText: '',
redirectionUrl: 'http://nos.nl/nieuws/',
streamUrl: url.resolve(process.env.BASE_URL || '', '/stream'),
}
let audioFile = ''
let lastUpdate = new Date(0)
const promiseWithTimeout = (timeoutMs, promise) => {
let timeoutHandle
const timeoutPromise = new Promise((resolve, reject) => {
timeoutHandle = setTimeout(
() => reject(new Error('Promise timed out')),
timeoutMs,
)
})
return Promise.race([promise, timeoutPromise]).then((result) => {
clearTimeout(timeoutHandle)
return result
})
}
const getAudioFileLocationPromise = async (page) => {
await page.setRequestInterception(true)
return () =>
new Promise((resolve) => {
page.on('request', (interceptedRequest) => {
const url = interceptedRequest.url()
if (url.includes('bulalg.mp3')) {
// it's the audio file!
resolve(url)
}
interceptedRequest.continue()
})
})
}
/**
* Fetch the latest audio from the NOS servers
*
* @returns {Promise<void>}
*/
async function updateAudio() {
const browser = await puppeteer.launch({
executablePath: process.env.CHROME_LOCATION,
defaultViewport: {
width: 1440,
height: 700,
},
args: ['--disable-features=site-per-process'],
})
try {
const page = await browser.newPage()
await page.goto('https://www.nporadio1.nl/uitzendingen', {
waitUntil: 'networkidle2',
})
const [consentToCookie] = await page.$x(
"//button[contains(., 'accepteren')]",
)
await consentToCookie.click({
waitUntil: 'networkidle2',
})
await page.waitForXPath("//button[contains(., 'Laatste journaal')]")
const audioFileLocationPromise = (await getAudioFileLocationPromise(page))()
const [button] = await page.$x("//button[contains(., 'Laatste journaal')]")
await button.click({
waitUntil: 'networkidle2',
})
const audioUrl = await promiseWithTimeout(20000, audioFileLocationPromise)
await browser.close()
const newAudioFile = await fetch(audioUrl).then((res) => res.buffer())
// only update if we succesfully fetched
if (newAudioFile.length > 100) {
audioFile = newAudioFile
lastUpdate = new Date()
}
} catch (e) {
console.error(e)
} finally {
// close browser
await browser.close()
}
}
/**
* Fetch the latest news if needed
*
* @returns {Promise<void>}
*/
function updateAudioIfNeeded() {
// don't update if we last updated less then 5 minutes ago
if (Date.now() - lastUpdate > 5 * 60 * 1000) {
// only update once
lastUpdate = new Date()
return updateAudio()
}
return Promise.resolve()
}
async function main() {
// create the server
const port = process.env.PORT || 8080
http
.createServer(async (req, res) => {
switch (req.url) {
case '/':
await updateAudioIfNeeded()
res.writeHead(200, { 'Content-Type': 'application/json' })
res.write(
JSON.stringify({
...jsonTemplate,
updateDate: lastUpdate.toISOString(),
// why not use the date as the unique identifier too
// unix timestamps should be unique
uid: lastUpdate.getTime(),
}),
)
res.end()
break
case '/stream':
res.writeHead(200, { 'Content-Type': 'audio/mpeg' })
// stream the latest saved audio
res.write(audioFile, 'binary')
res.end(null, 'binary')
break
default:
res.writeHead(404)
res.end()
break
}
})
.listen(port)
// eslint-disable-next-line no-console
console.log('Server listening on port', port)
updateAudioIfNeeded() // get the latest news
}
main()