Skip to content

Commit d866f11

Browse files
committed
refactor webcast script to use youtube rss feed
1 parent c94e284 commit d866f11

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+177
-71
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ FROM node:15-alpine
44
LABEL maintainer="[email protected]"
55

66
HEALTHCHECK --interval=10s --timeout=3s \
7-
CMD ./scripts/healthcheck.js
7+
CMD ./lib/utils/healthcheck.js
88

99
RUN apk add --no-cache --upgrade bash
1010

README.md

Lines changed: 1 addition & 1 deletion

app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const bodyParser = require('koa-bodyparser');
77
const mongoose = require('mongoose');
88
const { requestLogger, logger } = require('./middleware/logger');
99
const { responseTime, errors } = require('./middleware');
10-
const { v4 } = require('./services');
10+
const { v4 } = require('./routes');
1111

1212
const app = new Koa();
1313

jobs/webcast.js

Lines changed: 61 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
const got = require('got');
22
const fuzz = require('fuzzball');
3+
const Parser = require('rss-parser');
4+
const { fail, success } = require('../lib/healthchecks');
35
const { logger } = require('../middleware/logger');
46

57
const YOUTUBE_PREFIX = 'https://youtu.be';
6-
const API = process.env.SPACEX_API;
78
const CHANNEL_ID = 'UCtI0Hodo5o5dUb67FeUjDeA';
89
const {
910
SPACEX_KEY,
10-
YOUTUBE_KEY,
1111
WEBCAST_HEALTHCHECK,
12+
SPACEX_API: API,
1213
} = process.env;
1314

1415
/**
@@ -17,91 +18,85 @@ const {
1718
*/
1819
module.exports = async () => {
1920
try {
20-
// Check if any upcoming streams on youtube
21-
const url = `https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=${CHANNEL_ID}&eventType=upcoming&maxResults=1&type=video&key=${YOUTUBE_KEY}`;
22-
const upcomingStreams = await got(url, {
21+
let updated = false;
22+
let match = false;
23+
const parser = new Parser();
24+
const url = `https://www.youtube.com/feeds/videos.xml?channel_id=${CHANNEL_ID}`;
25+
const rssResult = await got(url, {
26+
resolveBodyOnly: true,
27+
});
28+
const result = await parser.parseString(rssResult);
29+
const latest = result.items[0];
30+
const rssTitle = latest.title;
31+
const rssYoutubeId = latest.link.split('v=')[1];
32+
33+
const launches = await got.post(`${API}/launches/query`, {
34+
json: {
35+
query: {
36+
upcoming: true,
37+
},
38+
options: {
39+
sort: {
40+
flight_number: 'asc',
41+
},
42+
limit: 1,
43+
},
44+
},
2345
resolveBodyOnly: true,
2446
responseType: 'json',
2547
});
48+
const launchId = launches.docs[0].id;
49+
const missionName = launches.docs[0].name;
2650

27-
if (upcomingStreams?.items?.length === 1) {
28-
const launches = await got.post(`${API}/launches/query`, {
51+
const ratio = fuzz.ratio(rssTitle, missionName);
52+
if (ratio >= 50) {
53+
match = true;
54+
const pastLaunches = await got.post(`${API}/launches/query`, {
2955
json: {
3056
query: {
31-
upcoming: true,
57+
upcoming: false,
3258
},
3359
options: {
3460
sort: {
35-
flight_number: 'asc',
61+
flight_number: 'desc',
3662
},
3763
limit: 1,
3864
},
3965
},
4066
resolveBodyOnly: true,
4167
responseType: 'json',
4268
});
43-
const launchId = launches.docs[0].id;
44-
const missionName = launches.docs[0].name;
45-
const youtubeTitle = upcomingStreams.items[0].snippet.title;
46-
const youtubeId = upcomingStreams.items[0].id.videoId;
47-
48-
// Fuzzy check video title to make sure it's at least related to the launch
49-
const ratio = fuzz.ratio(youtubeTitle, missionName);
50-
if (ratio >= 50) {
51-
const pastLaunches = await got.post(`${API}/launches/query`, {
69+
const pastYoutubeId = pastLaunches.docs[0].links.youtube_id;
70+
if (rssYoutubeId !== pastYoutubeId) {
71+
await got.patch(`${API}/launches/${launchId}`, {
5272
json: {
53-
query: {
54-
upcoming: false,
55-
},
56-
options: {
57-
sort: {
58-
flight_number: 'desc',
59-
},
60-
limit: 1,
61-
},
73+
'links.webcast': `${YOUTUBE_PREFIX}/${rssYoutubeId}`,
74+
'links.youtube_id': rssYoutubeId,
75+
},
76+
headers: {
77+
'spacex-key': SPACEX_KEY,
6278
},
63-
resolveBodyOnly: true,
64-
responseType: 'json',
65-
});
66-
const pastYoutubeId = pastLaunches.docs[0].links.youtube_id;
67-
if (youtubeId !== pastYoutubeId) {
68-
await got.patch(`${API}/launches/${launchId}`, {
69-
json: {
70-
'links.webcast': `${YOUTUBE_PREFIX}/${youtubeId}`,
71-
'links.youtube_id': youtubeId,
72-
},
73-
headers: {
74-
'spacex-key': SPACEX_KEY,
75-
},
76-
});
77-
logger.info({
78-
rawMission: youtubeTitle,
79-
apiMission: missionName,
80-
url: `${YOUTUBE_PREFIX}/${youtubeId}`,
81-
matchRatio: ratio,
82-
match: true,
83-
});
84-
}
85-
logger.info('Past youtube id matches, skipping...');
86-
} else {
87-
logger.info({
88-
rawMission: youtubeTitle,
89-
apiMission: missionName,
90-
url: `${YOUTUBE_PREFIX}/${youtubeId}`,
91-
matchRatio: ratio,
92-
match: false,
9379
});
80+
updated = true;
9481
}
95-
} else {
96-
logger.info({
97-
match: false,
98-
});
99-
}
100-
101-
if (WEBCAST_HEALTHCHECK) {
102-
await got(WEBCAST_HEALTHCHECK);
10382
}
83+
const log = {
84+
name: 'webcast',
85+
ratio,
86+
match,
87+
updated,
88+
youtubeTitle: rssTitle,
89+
youtubeId: rssYoutubeId,
90+
};
91+
await success(WEBCAST_HEALTHCHECK, log);
92+
logger.info(log);
10493
} catch (error) {
105-
console.log(`Webcast Error: ${error.message}`);
94+
const formatted = {
95+
name: 'webcast',
96+
error: error.message,
97+
stack: error.stack,
98+
};
99+
await fail(WEBCAST_HEALTHCHECK, formatted);
100+
logger.error(formatted);
106101
}
107102
};

jobs/worker.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ const payloadsJob = new CronJob('25 * * * *', payloads);
3737
// Every hour on :35
3838
const starlinkJob = new CronJob('35 * * * *', starlink);
3939

40-
// Every hour on :45
41-
const webcastJob = new CronJob('45 * * * *', webcast);
40+
// Every 5 minutes
41+
const webcastJob = new CronJob('*/5 * * * *', webcast);
4242

4343
launchesJob.start();
4444
payloadsJob.start();

lib/constants.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* Healthchecks.io API prefix
3+
*/
4+
module.exports.HEALTHCHECK_PREFIX = 'https://hc-ping.com';

lib/healthchecks/fail.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Imports
3+
*/
4+
const got = require('got');
5+
const { HEALTHCHECK_PREFIX } = require('../constants');
6+
7+
/**
8+
* Send fail signal to healthcheck.io
9+
*
10+
* @param {String} id UUID of healthcheck
11+
* @param {String|Object} [msg] Message to pass to healthcheck
12+
* @returns {Boolean} True if successful
13+
*/
14+
module.exports = async (id, msg = {}) => {
15+
const response = await got.post({
16+
prefixUrl: HEALTHCHECK_PREFIX,
17+
url: `${id}/fail`,
18+
json: msg,
19+
});
20+
if (response.statusCode === 200) {
21+
return true;
22+
}
23+
return false;
24+
};

lib/healthchecks/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Export healthcheck.io funcs
3+
*/
4+
module.exports.fail = require('./fail');
5+
module.exports.start = require('./start');
6+
module.exports.success = require('./success');

lib/healthchecks/start.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Imports
3+
*/
4+
const got = require('got');
5+
const { HEALTHCHECK_PREFIX } = require('../constants');
6+
7+
/**
8+
* Send start signal to healthcheck.io
9+
*
10+
* @param {String} id UUID of healthcheck
11+
* @param {String|Object} [msg] Message to pass to healthcheck
12+
* @returns {Boolean} true if successful
13+
*/
14+
module.exports = async (id, msg = {}) => {
15+
const response = await got.post({
16+
prefixUrl: HEALTHCHECK_PREFIX,
17+
url: `${id}/start`,
18+
json: msg,
19+
});
20+
if (response.statusCode === 200) {
21+
return true;
22+
}
23+
return false;
24+
};

lib/healthchecks/success.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Imports
3+
*/
4+
const got = require('got');
5+
const { HEALTHCHECK_PREFIX } = require('../constants');
6+
7+
/**
8+
* Send start signal to healthcheck.io
9+
*
10+
* @param {String} id UUID of healthcheck
11+
* @param {String|Object} [msg] Message to pass to healthcheck
12+
* @returns {Boolean} True if successful
13+
*/
14+
module.exports = async (id, msg = {}) => {
15+
const response = await got.post({
16+
prefixUrl: HEALTHCHECK_PREFIX,
17+
url: `${id}`,
18+
json: msg,
19+
});
20+
if (response.statusCode === 200) {
21+
return true;
22+
}
23+
return false;
24+
};

0 commit comments

Comments
 (0)