small fixes and optimizations

This commit is contained in:
daniel-j 2016-08-15 15:12:20 +02:00
parent 0fbacd0546
commit 9d97330b4d
5 changed files with 70 additions and 28 deletions

View file

@ -15,17 +15,21 @@ Right now the addon is not available in Add-ons for Firefox, but it's fully comp
Installation & usage (command line) Installation & usage (command line)
------------------- -------------------
You can install the tool by running `npm install -g fimfic2epub`. You can then run it with `$ fimfic2epub <story id> <optional filename>`. By default the EPUB will be saved in the current working directory with the filename `Title by Author.epub`. You can set filename to `-` and the epub will be emitted to stdout instead. You can install the tool by running `npm install -g fimfic2epub`. You can then run it like this:
Example: `$ fimfic2epub <story id/url> [<optional filename>]`
By default the EPUB will be saved in the current working directory with the filename `Title by Author.epub`. You can set filename to `-` and the epub will be emitted to stdout instead.
Examples
--------
``` ```
Download with automatic filename: Download with automatic filename:
$ fimfic2epub 180690 $ fimfic2epub 180690
$ fimfic2epub https://www.fimfiction.net/story/180690/tag-test
Download and save to a specified filename: Download and save to a specified filename:
$ fimfic2epub 180690 path/to/file.epub $ fimfic2epub 180690 path/to/file.epub
Same as above, but use a pipe:
$ fimfic2epub 180690 - > path/to/file.epub $ fimfic2epub 180690 - > path/to/file.epub
``` ```

View file

@ -4,7 +4,7 @@
"name": "fimfic2epub", "name": "fimfic2epub",
"short_name": "fimfic2epub", "short_name": "fimfic2epub",
"description": "Improved EPUB exporter for Fimfiction", "description": "Improved EPUB exporter for Fimfiction",
"version": "1.1.0", "version": "1.1.1",
"icons": { "icons": {
"128": "icon-128.png" "128": "icon-128.png"

View file

@ -1,6 +1,6 @@
{ {
"name": "fimfic2epub", "name": "fimfic2epub",
"version": "1.1.0", "version": "1.1.1",
"description": "Tool to generate EPUB ebooks from fimfiction stories", "description": "Tool to generate EPUB ebooks from fimfiction stories",
"author": "djazz", "author": "djazz",
"scripts": { "scripts": {
@ -52,7 +52,7 @@
"standard": "^7.1.2", "standard": "^7.1.2",
"stylus": "^0.54.5", "stylus": "^0.54.5",
"stylus-loader": "^2.2.0", "stylus-loader": "^2.2.0",
"webpack": "^2.1.0-beta.13", "webpack": "^2.1.0-beta.20",
"webpack-node-externals": "^1.3.3" "webpack-node-externals": "^1.3.3"
}, },

View file

@ -4,7 +4,7 @@ import escapeStringRegexp from 'escape-string-regexp'
import zeroFill from 'zero-fill' import zeroFill from 'zero-fill'
import { XmlEntities } from 'html-entities' import { XmlEntities } from 'html-entities'
import sanitize from 'sanitize-filename' import sanitize from 'sanitize-filename'
import URL from 'url'
import isNode from 'detect-node' import isNode from 'detect-node'
import { styleCss, coverstyleCss, titlestyleCss } from './styles' import { styleCss, coverstyleCss, titlestyleCss } from './styles'
@ -21,6 +21,16 @@ module.exports = class FimFic2Epub {
constructor (storyId) { constructor (storyId) {
this.storyId = storyId this.storyId = storyId
if (isNaN(storyId)) {
let url = URL.parse(storyId, false, true)
if (url.hostname === 'www.fimfiction.net' || url.hostname === 'fimfiction.net') {
let m = url.pathname.match(/^\/story\/(\d+)/)
if (m) {
this.storyId = m[1]
}
}
}
this.hasDownloaded = false
this.isDownloading = false this.isDownloading = false
this.zip = null this.zip = null
this.chapterContent = [] this.chapterContent = []
@ -29,6 +39,7 @@ module.exports = class FimFic2Epub {
this.isDownloading = false this.isDownloading = false
this.cachedFile = null this.cachedFile = null
this.hasCoverImage = false this.hasCoverImage = false
this.coverImageDimensions = {width: 0, height: 0}
this.includeTitlePage = true this.includeTitlePage = true
this.categories = [] this.categories = []
this.tags = [] this.tags = []
@ -40,7 +51,7 @@ module.exports = class FimFic2Epub {
reject('Already downloading') reject('Already downloading')
return return
} }
if (this.cachedFile) { if (this.hasDownloaded) {
resolve() resolve()
return return
} }
@ -58,18 +69,21 @@ module.exports = class FimFic2Epub {
console.log('Fetching story metadata...') console.log('Fetching story metadata...')
fetchRemote('https://www.fimfiction.net/api/story.php?story=' + this.storyId, (raw, type) => { let url = 'https://www.fimfiction.net/api/story.php?story=' + this.storyId
fetchRemote(url, (raw, type) => {
let data let data
try { try {
data = JSON.parse(raw) data = JSON.parse(raw)
} catch (e) { } catch (e) {}
console.log('Unable to fetch story json') if (!data) {
reject('Unable to fetch story json')
return return
} }
if (data.error) { if (data.error) {
console.error(data.error) reject(data.error)
return return
} }
this.storyInfo = data.story this.storyInfo = data.story
this.storyInfo.chapters = this.storyInfo.chapters || [] this.storyInfo.chapters = this.storyInfo.chapters || []
this.storyInfo.uuid = 'urn:fimfiction:' + this.storyInfo.id this.storyInfo.uuid = 'urn:fimfiction:' + this.storyInfo.id
@ -92,7 +106,8 @@ module.exports = class FimFic2Epub {
fetchTitlePage (resolve, reject) { fetchTitlePage (resolve, reject) {
console.log('Fetching index page...') console.log('Fetching index page...')
fetchRemote(this.storyInfo.url, (raw, type) => { let url = this.storyInfo.url
fetchRemote(url, (raw, type) => {
this.extractTitlePageInfo(raw).then(() => this.checkCoverImage(resolve, reject)) this.extractTitlePageInfo(raw).then(() => this.checkCoverImage(resolve, reject))
}) })
} }
@ -183,24 +198,32 @@ module.exports = class FimFic2Epub {
coverImage.src = this.storyInfo.full_image coverImage.src = this.storyInfo.full_image
coverImage.addEventListener('load', () => { coverImage.addEventListener('load', () => {
this.processStory(resolve, reject, coverImage) this.coverImageDimensions.width = coverImage.width
this.coverImageDimensions.height = coverImage.height
this.processStory(resolve, reject)
}, false) }, false)
coverImage.addEventListener('error', () => {
console.warn('Unable to fetch cover image, skipping...')
this.hasCoverImage = false
this.processStory(resolve, reject)
})
} else { } else {
const sizeOf = require('image-size') this.processStory(resolve, reject)
fetchRemote(this.storyInfo.full_image, (data, type) => {
this.processStory(resolve, reject, sizeOf(data))
}, 'buffer')
} }
} else { } else {
this.processStory(resolve, reject) this.processStory(resolve, reject)
} }
} }
processStory (resolve, reject, coverImage) { processStory (resolve, reject) {
console.log('Fetching chapters...') console.log('Fetching chapters...')
this.fetchChapters(() => { this.fetchChapters(() => {
console.log('Fetching remote files...')
this.fetchRemoteFiles(() => { this.fetchRemoteFiles(() => {
console.log('Finishing build...')
let coverFilename = '' let coverFilename = ''
this.remoteResources.forEach((r, url) => { this.remoteResources.forEach((r, url) => {
let dest = '../' + r.dest let dest = '../' + r.dest
@ -230,19 +253,20 @@ module.exports = class FimFic2Epub {
this.chapterContent.length = 0 this.chapterContent.length = 0
if (this.includeTitlePage) {
this.zip.file('Text/title.xhtml', template.createTitlePage(this))
}
if (this.hasCoverImage) { if (this.hasCoverImage) {
this.zip.file('Text/cover.xhtml', template.createCoverPage(coverFilename, coverImage.width, coverImage.height)) this.zip.file('Text/cover.xhtml', template.createCoverPage(coverFilename, this.coverImageDimensions.width, this.coverImageDimensions.height))
} else { } else {
this.zip.file('Text/cover.xhtml', template.createCoverPage(this)) this.zip.file('Text/cover.xhtml', template.createCoverPage(this))
} }
if (this.includeTitlePage) {
this.zip.file('Text/title.xhtml', template.createTitlePage(this))
}
this.zip.file('content.opf', template.createOpf(this)) this.zip.file('content.opf', template.createOpf(this))
this.isDownloading = false this.isDownloading = false
this.hasDownloaded = true
resolve() resolve()
}) })
}) })
@ -275,6 +299,10 @@ module.exports = class FimFic2Epub {
if (dest) { if (dest) {
r.dest = dest.replace('*', r.filename) r.dest = dest.replace('*', r.filename)
this.zip.file(r.dest, data) this.zip.file(r.dest, data)
if (isNode && r.filename === 'cover') {
const sizeOf = require('image-size')
this.coverImageDimensions = sizeOf(data)
}
} }
completeCount++ completeCount++
recursive() recursive()
@ -306,7 +334,8 @@ module.exports = class FimFic2Epub {
return return
} }
console.log('Fetching chapter ' + (index + 1) + ' of ' + chapters.length + ': ' + ch.title) console.log('Fetching chapter ' + (index + 1) + ' of ' + chapters.length + ': ' + ch.title)
fetchRemote(ch.link.replace('http', 'https'), (html) => { let url = ch.link.replace('http', 'https')
fetchRemote(url, (html) => {
template.createChapter(ch, html, (html) => { template.createChapter(ch, html, (html) => {
this.findRemoteResources('ch_' + zeroFill(3, index + 1), index, html) this.findRemoteResources('ch_' + zeroFill(3, index + 1), index, html)
this.chapterContent[index] = html this.chapterContent[index] = html
@ -359,6 +388,10 @@ module.exports = class FimFic2Epub {
resolve(this.cachedFile, this.filename) resolve(this.cachedFile, this.filename)
return return
} }
if (!this.hasDownloaded) {
reject('Not downloaded.')
return
}
this.zip this.zip
.generateAsync({ .generateAsync({
type: isNode ? 'nodebuffer' : 'blob', type: isNode ? 'nodebuffer' : 'blob',
@ -374,6 +407,10 @@ module.exports = class FimFic2Epub {
// example usage: .pipe(fs.createWriteStream(filename)) // example usage: .pipe(fs.createWriteStream(filename))
streamFile () { streamFile () {
if (!this.hasDownloaded) {
reject('Not downloaded.')
return
}
return this.zip return this.zip
.generateNodeStream({ .generateNodeStream({
type: 'nodebuffer', type: 'nodebuffer',

View file

@ -45,9 +45,11 @@ function fetchBackground (url, cb, responseType) {
fetch(objurl, cb, responseType) fetch(objurl, cb, responseType)
URL.revokeObjectURL(objurl) URL.revokeObjectURL(objurl)
}) })
} else { } else if (typeof safari !== 'undefined') {
safariQueue[url] = {cb: cb, responseType: responseType} safariQueue[url] = {cb: cb, responseType: responseType}
safari.self.tab.dispatchMessage('remote', url) safari.self.tab.dispatchMessage('remote', url)
} else {
cb(null)
} }
} }
@ -67,4 +69,3 @@ export default function fetchRemote (url, cb, responseType) {
} }
}, responseType) }, responseType)
} }