1
0
mirror of https://github.com/comatory/fb2iCal synced 2025-06-05 22:09:25 +02:00

refactor retrieving ICS file

This commit is contained in:
Ondrej Synacek
2019-10-23 20:37:06 +02:00
parent 52b86953aa
commit a41992d53b
7 changed files with 91 additions and 64 deletions

24
lib/services/crawler.js Normal file
View File

@@ -0,0 +1,24 @@
const request = require('request')
const crawl = async (url) => {
return new Promise((resolve, reject) => {
console.info(`Started request for URL: ${url}`)
request({
url,
headers: {
'Accept-Language': 'en-US, en',
'User-Agent': 'request',
},
}, (err, res, body) => {
if (err) {
reject(err)
return
}
console.info(`Finished request for URL: ${url}`)
resolve(body)
})
})
}
module.exports = crawl

View File

@@ -0,0 +1,16 @@
const ics = require('ics')
const generateICS = async(data) => {
return new Promise((resolve, reject) => {
ics.createEvent(data, (err, value) => {
if (err) {
reject(err)
return
}
resolve(value)
})
})
}
module.exports = generateICS

View File

@@ -0,0 +1,18 @@
const crawl = require('./crawler')
const parseHTML = require('./parser')
const generateICS = require('./ics-generator')
const { getNormalizedUrl } = require('../utils')
const retrieveICS = async (URLparameter) => {
try {
const url = getNormalizedUrl(URLparameter)
const html = await crawl(url)
const eventData = parseHTML(html)
const icsFile = await generateICS(eventData)
return icsFile
} catch (err) {
throw err
}
}
module.exports = retrieveICS

96
lib/services/parser.js Normal file
View File

@@ -0,0 +1,96 @@
const dayjs = require('dayjs')
const cheerio = require('cheerio')
const { createParserError } = require('../utils')
// NOTE: Specific formatting for `ics` library
const parseDates = (startDate, endDate) => {
const start = startDate ? [
startDate.year(),
startDate.month() + 1,
startDate.date(),
startDate.hour(),
startDate.minute(),
] : (() => {
const now = dayjs()
return [
now.year(),
now.month() + 1,
now.date()
]
})()
const diffInMinutes = endDate ?
endDate.diff(startDate, 'minutes') :
120
const duration = { minutes: diffInMinutes }
return {
start,
duration,
}
}
const parseEventData = (eventData) => {
const startDate = eventData.startDate && dayjs(eventData.startDate)
const endDate = eventData.endDate && dayjs(eventData.endDate)
const { start, duration } = parseDates(startDate, endDate)
const { location } = eventData || {}
const { address } = location || {}
const locationStr = location ? [
location.name || '',
address.streetAddress || '',
address.addressLocality || '',
address.postalCode || '',
address.addressCountry || '',
].join(' ') : ''
const cleanedLocationStr = locationStr.replace(/\r?\n|\r/g, ' ')
const title = eventData.name || ''
const url = eventData.url || ''
const description = eventData.description || ''
return {
start,
duration,
location: cleanedLocationStr,
title,
url,
description,
}
}
const parseHTML = (html) => {
try {
// NOTE: Mobile web should have serialized
// event info in one of the script tags
const $ = cheerio.load(html)
const $scripts = $('head script[type="application/ld+json"]')
const rawData = $scripts.toArray().reduce((data, node) => {
const firstNode = node.children[0]
if (!firstNode || !firstNode.data) {
return data
}
if (firstNode.data.startsWith('//<![CDATA')) {
return firstNode.data
}
return data
}, null)
if (!rawData) {
throw createParserError()
}
const eventData = JSON.parse(rawData.slice(12, -5))
const data = parseEventData(eventData)
return data
} catch (err) {
throw err
}
}
module.exports = parseHTML