mirror of
https://github.com/daniel-j/fimfic2epub.git
synced 2024-06-02 02:24:31 +12:00
small fixes and optimizations
This commit is contained in:
parent
0fbacd0546
commit
9d97330b4d
12
README.md
12
README.md
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue