1 |
|
2 |
|
3 | fs = require('fs')
|
4 | request = require('request')
|
5 | program = require('commander')
|
6 | async = require('async')
|
7 | _ = require('lodash')
|
8 | exec = require('child_process').exec
|
9 | moment = require('moment')
|
10 | cheerio = require('cheerio')
|
11 | clc = require('cli-color')
|
12 | mangaUrls = require('./manga')
|
13 |
|
14 | program
|
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 |
|
28 |
|
29 |
|
30 |
|
31 | pages = {}
|
32 | pageAmount = {}
|
33 | host = undefined
|
34 | host = undefined
|
35 |
|
36 | padding = (value, length) ->
|
37 | String(('0' for i in [0...length]).join('') + value).slice(length * -1)
|
38 |
|
39 | createFolder = (folderPath) ->
|
40 | for path in folderPath.split '/'
|
41 | initPath = "#{initPath || '.'}/#{path}"
|
42 | fs.mkdirSync(initPath) unless fs.existsSync(initPath)
|
43 |
|
44 | imageDownload = (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 |
|
62 |
|
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 |
|
75 | mangaDownload = (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 |
|
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 |
|
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 |
|
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]
|
136 | else img.attr('src')
|
137 |
|
138 |
|
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 |
|
148 | mangaList = ->
|
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 |
|
172 | episodeList = ->
|
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 |
|
187 |
|
188 |
|
189 | if program.list then mangaList()
|
190 | else if program.eplist then episodeList()
|
191 | else 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())
|
195 | else
|
196 | console.log 'Error: please specify manga, volume and episode'
|