UNPKG

8.48 kBtext/coffeescriptView Raw
1#!/usr/bin/env node_modules/coffee-script/bin/coffee
2
3fs = require('fs')
4request = require('request')
5program = require('commander')
6async = require('async')
7_ = require('lodash')
8exec = require('child_process').exec
9moment = require('moment')
10cheerio = require('cheerio')
11clc = require('cli-color')
12mangaUrls = require('./manga')
13
14program
15 .version('0.0.1')
16 .usage('-m [manga ex. bleach] -v [volume ex. 30] -e [episode ex. 268]')
17 .option('-m, --manga <value>', 'Specify manga, view manga list on https://github.com/phatograph/mangafetcher#currently-supported-manga')
18 .option('-v, --volume <n>', 'Specify volume')
19 .option('-e, --episode <a>..<b>', 'Specify episode', (val) -> val.split('..').map(Number))
20 .option('-p, --pages [items]', 'Specify pages (optional) e.g. -p 2,4,5', (val) -> val.split(','))
21 .option('-l, --list', 'List mode')
22 .option('-x, --eplist', 'Episode List mode')
23 .option('-r, --rerender <value>', 'Rerender mode (for mangahere)')
24 .parse(process.argv)
25
26##############################################################################
27# Image Downloading Functions
28##############################################################################
29
30# Shared variables
31pages = {}
32pageAmount = {}
33host = undefined
34host = undefined
35
36padding = (value, length) ->
37 String(('0' for i in [0...length]).join('') + value).slice(length * -1)
38
39createFolder = (folderPath) ->
40 for path in folderPath.split '/'
41 initPath = "#{initPath || '.'}/#{path}"
42 fs.mkdirSync(initPath) unless fs.existsSync(initPath)
43
44imageDownload = (imgUri, i, paddedVol, paddedEp, ep) ->
45 request.head uri: imgUri, followRedirect: false, (err2, res2, body2) ->
46 if err2 or res2.statusCode isnt 200
47 console.log clc.red "Oops, something went wrong. Error: #{err2}"
48 return false
49 if res2.headers['content-type'] is 'image/jpeg'
50 folderPath = "manga/#{program.manga}/#{program.manga}-#{paddedVol}-#{paddedEp}"
51 folderPath += "-#{program.pages}" if host is 'http://www.mangapark.com/' and program.pages
52 fileName = "#{padding(i, 3)}.jpg"
53 filePath = "./#{folderPath}/#{fileName}"
54
55 createFolder(folderPath)
56 request(uri: imgUri, timeout: 120 * 1000)
57 .pipe fs.createWriteStream(filePath)
58 .on 'finish', ->
59 pages[ep].splice(pages[ep].indexOf(i), 1)
60
61 # Since iOS seems to sort images by created date, this should do the trick.
62 # Also rounds this by 60 (minutes)
63 exec("touch -t #{moment().format('YYYYMMDD')}#{padding(~~(i / 60), 2)}#{padding(i % 60, 2)} #{filePath}")
64
65 if pages[ep].length is 0
66 console.log clc.green "\nDone ##{ep}!"
67 else if pages[ep].length > 3
68 if (pageAmount[ep] - pages[ep].length) % 5
69 process.stdout.write "."
70 else
71 process.stdout.write "#{pageAmount[ep] - pages[ep].length}"
72 else
73 process.stdout.write "\nRemaining (##{ep}): #{pages[ep].join(', ')}" if pages[ep].length
74
75mangaDownload = (vol, ep) ->
76 fraction = if ep.match /\./ then _.last(ep.split('.')) else false
77 ep = ep.split('.')[0]
78 uri = switch mangaUrls[program.manga].format
79 when 1 then "#{mangaUrls[program.manga].url}/v#{if vol is 'TBD' then 'TBD' else padding(vol, 2)}/c#{padding(ep, 3)}/"
80 when 2 then "#{mangaUrls[program.manga].url}/v#{vol}/c#{ep}/"
81 when 3 then "#{mangaUrls[program.manga].url}/v#{padding(vol, 2)}/c#{padding(ep, 3)}#{if fraction then '.' + fraction else ''}/"
82 when 4 then "#{mangaUrls[program.manga].url}/c#{ep}/"
83 else "#{mangaUrls[program.manga].url}/c#{padding(ep, 3)}#{if fraction then '.' + fraction else ''}/"
84 paddedVol = padding(vol, 3)
85 paddedEp = padding(ep, 3)
86 paddedEp += ".#{fraction}" if fraction
87 host = mangaUrls[program.manga].url.match(/http:\/\/[.\w\d]+\//) || []
88 host = host[0]
89
90 if host is 'http://www.mangapark.com/'
91 if program.pages
92 uri += "10-#{program.pages}"
93 else
94 uri += 'all'
95
96 console.log uri
97
98 request uri: uri, (err, res, body) ->
99 if err or res.statusCode isnt 200
100 console.log clc.red "Oops, something went wrong #{'(Error: ' + res.statusCode + ')'if res}"
101 return false
102
103 $ = cheerio.load(body)
104
105 # Tap-in for mangapark.com
106 if host is 'http://www.mangapark.com/'
107 imgs = $('img.img')
108 pages[ep] = imgs.map (i) -> i
109 pageAmount[ep] = pages[ep].length
110 imgs.each (i) -> imageDownload @attr('src'), i, paddedVol, paddedEp, ep
111
112 # Other sites
113 else
114 pageAmount[ep] = switch host
115 when 'http://mangafox.me/' then $('form#top_bar select.m option').length
116 else $('section.readpage_top select.wid60 option').length
117 pages[ep] = program.pages || [0..pageAmount[ep]]
118 # uri = uri.slice(0, -1) if uri.match /\/$/ # Remove trailing `/`
119
120 console.log clc.green "Downloading up to #{pages[ep].length} page(s)"
121 for i in _.clone pages[ep]
122 do (i) ->
123 request uri: "#{uri}#{ if i > 0 then i + '.html' else '' }", followRedirect: false, (err, res, body) ->
124 $$ = cheerio.load(body)
125
126 if err or res.statusCode isnt 200
127 pages[ep].splice(pages[ep].indexOf(i), 1)
128 else
129 img = $$('img#image')
130
131 unless img.length
132 pages[ep].splice(pages[ep].indexOf(i), 1)
133 else
134 imgUri = switch host
135 when 'http://mangafox.me/' then img.attr('onerror').match(/http.+jpg/)[0] # New manga seems to fallback to another CDN
136 else img.attr('src')
137
138 # Rerender mode for mangahere
139 imgUri = switch program.rerender
140 when '0' then imgUri.replace(/.\.m.cdn\.net/, 'm.mhcdn.net')
141 when '1' then imgUri.replace(/.\.m.cdn\.net/, 's.mangahere.com')
142 when '2' then imgUri.replace(/.\.m.cdn\.net/, 'z.mfcdn.net')
143 else imgUri
144
145 console.log imgUri if program.pages
146 imageDownload imgUri, i, paddedVol, paddedEp, ep
147
148mangaList = ->
149 for name, url of mangaUrls
150 do (name, url) ->
151 host = mangaUrls[name].url.match(/http:\/\/[.\w\d]+\//) || []
152 host = host[0]
153
154 request uri: "#{mangaUrls[name].url}/", followRedirect: false, (err, res, body) ->
155 $ = cheerio.load(body)
156 label = switch host
157 when 'http://mangafox.me/' then $('a.tips').first().text().trim()
158 when 'http://www.mangapark.com/' then $('div.ch li.new span a b').first().text().trim().replace(/\n/, '').replace(/(\s+|\t)/, ' ')
159 else $('div.detail_list span.left a.color_0077').first().text().trim()
160 labelNum = _.last(label.split(' '))
161 labelNum = ~~(_.last(labelNum.split('.')))
162 folderPath = "./manga/#{name}"
163
164 if fs.existsSync(folderPath)
165 fs.readdir folderPath, (e, folders) ->
166 _.remove(folders, (x) -> x is '.DS_Store')
167 latestFolder = ~~(_.last(_.last(folders).split('-'))) if folders.length
168 color = if latestFolder is labelNum then clc.green else clc.red
169
170 console.log "#{label} [#{clc.yellow name}] (local: #{color(latestFolder || '-')}/#{labelNum})"
171
172episodeList = ->
173 unless program.manga
174 console.log 'Error: please specify manga'
175 return
176
177 request uri: "#{mangaUrls[program.manga].url}/", followRedirect: false, (err, res, body) ->
178 $ = cheerio.load(body)
179 $('div.detail_list ul span.left').each (i, l) ->
180 text = @parent().text().trim()
181 .replace(/\r?\n|\r|\t/g, '')
182 .replace(/\s{2,}/g, ' | ')
183 console.log text
184
185##############################################################################
186# App Kickoff!
187##############################################################################
188
189if program.list then mangaList()
190else if program.eplist then episodeList()
191else if program.manga and program.episode
192 episodes = [program.episode[0]..(program.episode[1] || program.episode[0])]
193 for ep in episodes
194 mangaDownload(program.volume || 0, ep.toString())
195else
196 console.log 'Error: please specify manga, volume and episode'