fetch-fic
Version:
Package up delicious, delicious fanfic from various sources into epub ebooks ready for reading in your ereader of choice.
103 lines (94 loc) • 3.31 kB
JavaScript
module.exports = write
const fs = require('fs')
const url = require('url')
const TOML = require('@iarna/toml')
const fetch = use('fetch')
const Fic = use('fic')
const getFic = use('get-fic')
const Output = use('output')
const progress = use('progress')
const forEach = use('for-each')
function write (args) {
const output = args.output
const user = args.xf_user
const maxConcurrency = args.concurrency
const requestsPerSecond = args['requests-per-second']
const fetchOpts = {
cacheBreak: !args.cache,
noNetwork: !args.network,
timeout: 3500,
maxConcurrency: 6,
requestsPerSecond: 10,
perSite: {
"forums.spacebattles.com": {
maxConcurrency: 2,
requestsPerSecond: 1,
},
"forums.sufficientvelocity.com": {
maxConcurrency: 6,
requestsPerSecond: 4,
},
"forum.questionablequesting.com": {
maxConcurrency: 6,
requestsPerSecond: 4,
},
"questionablequesting.com": {
maxConcurrency: 6,
requestsPerSecond: 4,
},
}
}
const fetchAndSpin = fetch.withOpts(fetchOpts).wrapWith(progress.spinWhileAnd)
if (args.xf_user) fetchAndSpin.setGlobalCookie(`xf_user=${args.xf_user}`)
const trackers = args.fic.map(() => progress.tracker.newItem(1))
process.emit('debug', `Generating epubs for ${args.fic.join(', ')}`)
return forEach(args.fic, {concurrency: 3}, async (ficFile, ficNum) => {
process.emit('debug', `Generating #${ficNum} for ${ficFile}`)
const topFic = Fic.fromJSON(TOML.parse(fs.readFileSync(ficFile, 'utf8')))
let fics = (topFic.chapters.length ? [topFic] : []).concat(topFic.fics || [])
const tracker = trackers[ficNum]
const completeWhenDone = (fetch) => {
return async (href, opts) => {
try {
return fetch(href, opts)
} finally {
tracker.completeWork(1)
}
}
}
const fetchAndFinish = fetchAndSpin.wrapWith(completeWhenDone)
fetchAndFinish.tracker = tracker
fics = fics.filter((fic, subficNum) => {
if (topFic === fic && topFic.fics && !topFic.chapters) return false
if (!fic.title) {
process.emit('warn', `Skipping #${subficNum} in ${ficFile}, missing title`)
return false
}
tracker.addWork(fic.chapters.length)
process.emit('debug', `Fetching #${ficNum} for ${ficFile}: ${fic.title}`)
return true
})
try {
await forEach(fics, {concurrency: 10}, async (fic, subficNum, subficCount) => {
let ficStatus = ''
if (args.fic.length > 1) {
ficStatus = subficCount > 1 ? ` [${ficNum + 1}.${subficNum + 1}/${args.fic.length}]` : ` [${ficNum + 1}/${args.fic.length}]`
} else if (subficCount > 1) {
ficStatus = ` [${subficNum + 1}/${subficCount}]`
}
progress.show(`${fic.title}${ficStatus}`, 'Fetching fic')
process.emit('debug', `Outputting ${fic.title}`)
const ficStream = getFic(fetchAndFinish, fic)
const filename = await Output.as(output).from(ficStream).write()
progress.output(`${filename}\n`)
})
} finally {
process.emit('debug', `Fetching #${ficNum} for ${ficFile}: Complete`)
tracker.finish()
progress.hide()
return
}
})
}