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:
24
lib/services/crawler.js
Normal file
24
lib/services/crawler.js
Normal 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
|
16
lib/services/ics-generator.js
Normal file
16
lib/services/ics-generator.js
Normal 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
|
18
lib/services/ics-retriever.js
Normal file
18
lib/services/ics-retriever.js
Normal 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
96
lib/services/parser.js
Normal 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
|
Reference in New Issue
Block a user