comic_walker 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31c86d5ee6917c1d2443b444ad0af64ea30312b7
4
- data.tar.gz: ad9a19cdaba4cc3ef9e78caaf4283e1024e25e1e
3
+ metadata.gz: 7fe84087441847fad172320a68b0a4849d5ccb0f
4
+ data.tar.gz: ad4627cd74295d5e64c457d74729cd379dedb10f
5
5
  SHA512:
6
- metadata.gz: 484678c62454decd50c4153b0f3b9f2ab5e018f4cf08d26ffa61196a3d3132968a1c6cf09df458aef884a179874936c90e4d3a5fdadcb18989eaee0b83d56034
7
- data.tar.gz: 0265eec741fde6d0d8a647d4023ab5578e66d3e6917341295fa5fe53c8d414606965a12225da895d2dedb9bdad3c688555b2be4cf54f4d16c5358e368de2ccce
6
+ metadata.gz: 7ca5b14ea61e4a6118ee906b64e7dff21b95ec07b9c9e70827b765d6952fb8d824973bb1ba50ffe8ed918a40030d2f625d29621d41a69d2380cadc43d4bedbcf
7
+ data.tar.gz: 62076faf1ff081f17c5f42e5e9cba25949ad523eb87c3e2b54fda226524cfdd7b9e4be0bd6db98670776eca38666ea4a63c8b9c576c05020650989c1ca68a14c
@@ -1,3 +1,9 @@
1
+ # 0.3.0 (2014-05-12)
2
+ - Download configuration_pack and images directly
3
+ - Drop jar file download
4
+ - Drop base64-encoded image decoder
5
+ - Many internal method changes
6
+
1
7
  # 0.2.2 (2014-05-11)
2
8
  - Fix image fetch errors for recent comics
3
9
 
@@ -23,7 +23,8 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency "yard"
24
24
  spec.add_dependency "addressable"
25
25
  spec.add_dependency "http-cookie"
26
+ spec.add_dependency "net-http-persistent"
27
+ spec.add_dependency "retryable"
26
28
  spec.add_dependency "rmagick"
27
- spec.add_dependency "rubyzip"
28
29
  spec.add_dependency "thor"
29
30
  end
@@ -4,7 +4,7 @@ require 'pp'
4
4
  require 'thor'
5
5
  require 'yaml'
6
6
  require 'comic_walker/client'
7
- require 'comic_walker/end_layer_decoder'
7
+ require 'comic_walker/content_downloader'
8
8
  require 'comic_walker/item_decoder'
9
9
  require 'comic_walker/v1/client'
10
10
 
@@ -20,7 +20,7 @@ module ComicWalker
20
20
  def save(*cids)
21
21
  client = Client.new
22
22
  cids.each do |cid|
23
- save_content(client, cid)
23
+ ContentDownloader.new(client, cid).save
24
24
  end
25
25
  end
26
26
 
@@ -45,9 +45,7 @@ module ComicWalker
45
45
  jar = HTTP::CookieJar.new
46
46
  load_cookies(jar)
47
47
  client = V1::Client.new(jar, load_uuid)
48
- json = client.start do
49
- client.contents(per_page: options[:per_page], page: options[:page])
50
- end
48
+ json = client.contents(per_page: options[:per_page], page: options[:page])
51
49
  save_cookies(jar)
52
50
 
53
51
  json['contents'].sort_by { |content| Time.parse(content['updated_at']) }.reverse_each do |content|
@@ -72,48 +70,6 @@ module ComicWalker
72
70
 
73
71
  private
74
72
 
75
- def save_content(client, cid)
76
- license = client.get_license(cid)
77
- info = license.get_info.first
78
- last_update = Time.strptime(info['last_update'], '%Y%m%d%H%M')
79
- title = info['issues'].first['content_name']
80
-
81
- license.with_jar do |zip|
82
- pages = []
83
- items = {}
84
- decoder = nil
85
- while entry = zip.get_next_entry
86
- case entry.name
87
- when 'configuration_pack.json'
88
- config = JSON.parse(zip.read)
89
- decoder = ItemDecoder.new(config)
90
- when /end_layer\.json/
91
- begin
92
- pp EndLayerDecoder.new(license.key).decode(zip.read)
93
- rescue JSON::ParserError
94
- $stderr.puts "WARNING: #{cid}: Could not decode end_layer.json.enc"
95
- end
96
- when %r{\Aitem/}
97
- items[entry.name] = zip.read
98
- end
99
- end
100
-
101
- img_dir = Pathname.new(title).join(cid).tap(&:mkpath)
102
- decoder.pages.each.with_index do |file, i|
103
- dat_path = Pathname.new(file).join('0.dat')
104
- img_fname = dat_path.parent.basename.sub_ext('.jpg')
105
- img_path = img_dir.join(sprintf('%03d_%s', i, img_fname))
106
-
107
- if data = items[dat_path.to_s]
108
- decoder.decode_b64(file, dat_path, img_path, data)
109
- else
110
- decoder.decode(file, dat_path, img_path, license.get_jpeg(file))
111
- end
112
- puts "#{dat_path} -> #{img_path}"
113
- end
114
- end
115
- end
116
-
117
73
  def load_cookies(jar)
118
74
  if COOKIE_PATH.readable?
119
75
  jar.load(COOKIE_PATH.to_s, session: true)
@@ -147,20 +103,18 @@ module ComicWalker
147
103
  page = 1
148
104
  per_page = 200
149
105
  child_cids = {}
150
- client.start do
151
- until child_cids.size == parent_cids do
152
- json = client.contents(page: page, per_page: per_page)
153
- contents = json['contents']
154
- if contents.empty?
155
- break
156
- end
157
- contents.each do |c|
158
- if parent_cids.include?(c['content_id'])
159
- child_cids[c['content_id']] = c['sub_contents']
160
- end
106
+ until child_cids.size == parent_cids do
107
+ json = client.contents(page: page, per_page: per_page)
108
+ contents = json['contents']
109
+ if contents.empty?
110
+ break
111
+ end
112
+ contents.each do |c|
113
+ if parent_cids.include?(c['content_id'])
114
+ child_cids[c['content_id']] = c['sub_contents']
161
115
  end
162
- page += 1
163
116
  end
117
+ page += 1
164
118
  end
165
119
  parent_cids.each do |cid|
166
120
  unless child_cids.has_key?(cid)
@@ -1,23 +1,19 @@
1
- require 'addressable/uri'
1
+ require 'uri'
2
2
  require 'base64'
3
- require 'net/http'
4
- require 'zip'
3
+ require 'net/http/persistent'
5
4
 
6
5
  module ComicWalker
7
6
  class Client
8
7
  class License
9
8
  def initialize(json)
10
9
  @json = json
10
+ @http = Net::HTTP::Persistent.new('comic_walker')
11
11
  end
12
12
 
13
13
  def agreement
14
14
  @json['agreement']
15
15
  end
16
16
 
17
- def jar_url
18
- url_prefix + agreement['jar_file_name']
19
- end
20
-
21
17
  def info_url
22
18
  url_prefix + agreement['info_file_name']
23
19
  end
@@ -26,31 +22,26 @@ module ComicWalker
26
22
  agreement['url_prefix']
27
23
  end
28
24
 
29
- def key
30
- Base64.decode64(agreement['key'])
25
+ CONFIGURATION_PACK_FILENAME = 'configuration_pack.json'
26
+
27
+ def configuration_pack_url
28
+ url_prefix + CONFIGURATION_PACK_FILENAME
29
+ end
30
+
31
+ def get(url)
32
+ @http.request(URI.parse(url))
31
33
  end
32
34
 
33
- def with_jar(&block)
34
- uri = Addressable::URI.parse(jar_url)
35
- body = Net::HTTP.start(uri.host) do |http|
36
- http.get(uri.request_uri).body
37
- end
38
- Zip::InputStream.open(StringIO.new(body), &block)
35
+ def get_configuration_pack
36
+ JSON.parse(get(configuration_pack_url).body)
39
37
  end
40
38
 
41
39
  def get_info
42
- uri = Addressable::URI.parse(info_url)
43
- body = Net::HTTP.start(uri.host) do |http|
44
- http.get(uri.request_uri).body
45
- end
46
- JSON.parse(body)
40
+ JSON.parse(get(info_url).body)
47
41
  end
48
42
 
49
43
  def get_jpeg(file)
50
- uri = Addressable::URI.parse(url_prefix + file + '/0.jpeg')
51
- Net::HTTP.start(uri.host) do |http|
52
- http.get(uri.request_uri).body
53
- end
44
+ get(url_prefix + file + '/0.jpeg').body
54
45
  end
55
46
  end
56
47
  end
@@ -0,0 +1,27 @@
1
+ module ComicWalker
2
+ class ContentDownloader
3
+ def initialize(client, cid)
4
+ @client = client
5
+ @cid = cid
6
+ end
7
+
8
+ def save
9
+ license = @client.get_license(@cid)
10
+ decoder = ItemDecoder.new(license.get_configuration_pack)
11
+ img_dir = image_dir(license)
12
+ decoder.pages.each.with_index do |file, i|
13
+ dat_path = Pathname.new(file).join('0.dat')
14
+ img_fname = dat_path.parent.basename.sub_ext('.jpg')
15
+ img_path = img_dir.join(sprintf('%03d_%s', i, img_fname))
16
+ decoder.decode(file, dat_path, img_path, license.get_jpeg(file))
17
+ puts "#{dat_path} -> #{img_path}"
18
+ end
19
+ end
20
+
21
+ def image_dir(license)
22
+ info = license.get_info.first
23
+ title = info['issues'].first['content_name']
24
+ Pathname.new(title).join(@cid).tap(&:mkpath)
25
+ end
26
+ end
27
+ end
@@ -5,18 +5,7 @@ module ComicWalker
5
5
  class ItemDecoder
6
6
  attr_reader :pages
7
7
 
8
- DEFAULT_CT = '20000101000000'
9
- DEFAULT_ST = '20000101000000'
10
- DEFAULT_ET = '99991231235959'
11
-
12
8
  def initialize(configuration_pack)
13
- fname = '0.dat'
14
- ct = configuration_pack['ct'] || DEFAULT_CT
15
- st = configuration_pack['st'] || DEFAULT_ST
16
- et = configuration_pack['et'] || DEFAULT_ET
17
- @key1 = (ct + st + fname).unpack('C*')
18
- @key2 = (ct + fname + et).unpack('C*')
19
- @key3 = (fname + st + et).unpack('C*')
20
9
  @pages = []
21
10
  configuration_pack['configuration']['contents'].each do |content|
22
11
  pages[content['index']-1] = content['file']
@@ -27,19 +16,6 @@ module ComicWalker
27
16
  end
28
17
  end
29
18
 
30
- def decode_b64(file, dat_path, img_path, b64data)
31
- bs = 128
32
- hs = 1024
33
- blob = Unknown.finish(@key1, hs,
34
- Unknown.decrypt(@key2, bs,
35
- Unknown.prepare(@key3,
36
- Base64.decode64(b64data).unpack('C*')
37
- )
38
- )
39
- ).pack('C*')
40
- decode(file, dat_path, img_path, blob)
41
- end
42
-
43
19
  def decode(file, dat_path, img_path, blob)
44
20
  src = Magick::Image.from_blob(blob).first
45
21
  width = src.columns
@@ -5,47 +5,6 @@ module ComicWalker
5
5
  module Unknown
6
6
  module_function
7
7
 
8
- # @param [Array<Fixnum>] key
9
- # @param [Array<Fixnum>] data Encrypted data
10
- # @return [Array<Fixnum>]
11
- def prepare(key, data)
12
- s = Cipher.gen_rc4_table(key)
13
- data.map.with_index do |b, i|
14
- b ^ s[i % 256]
15
- end
16
- end
17
-
18
- # @param [Array<Fixnum>] key
19
- # @param [Integer] bsize block size?
20
- # @param [Array<Fixnum>] data Encrypted data
21
- # @return [Array<Fixnum>] Chunked decrypted data
22
- def decrypt(key, bsize, data)
23
- s = []
24
- 0.step(data.size-1, bsize) do |i|
25
- s << data[i]
26
- end
27
-
28
- c = Cipher.decrypt_rc4(key, s)
29
- # s.size == c.size
30
-
31
- 0.step(data.size-1, bsize) do |i|
32
- data[i] = c.shift
33
- end
34
- data
35
- end
36
-
37
- # @param [Array<Fixnum>] key
38
- # @param [Integer] hsize header size?
39
- # @param [Array<Fixnum>] data Encrypted data
40
- # @return [Array<Fixnum>] data
41
- def finish(key, hsize, data)
42
- hsize = [hsize, data.size].min
43
- Cipher.decrypt_rc4(key, data.slice(0, hsize)).each.with_index do |x, i|
44
- data[i] = x
45
- end
46
- data
47
- end
48
-
49
8
  # Calculate moves.
50
9
  # @param [Integer] width Width of the image
51
10
  # @param [Integer] height Height of the image
@@ -1,7 +1,9 @@
1
1
  require 'addressable/uri'
2
2
  require 'http-cookie'
3
3
  require 'json'
4
- require 'net/http'
4
+ require 'net/http/persistent'
5
+ require 'retryable'
6
+ require 'uri'
5
7
 
6
8
  module ComicWalker
7
9
  module V1
@@ -9,19 +11,11 @@ module ComicWalker
9
11
  BASE_URI = Addressable::URI.parse("https://cnts.comic-walker.com")
10
12
 
11
13
  def initialize(jar, uuid)
12
- @https = Net::HTTP.new(BASE_URI.host, 443)
13
- @https.use_ssl = true
14
- @https.verify_mode = OpenSSL::SSL::VERIFY_PEER
14
+ @https = Net::HTTP::Persistent.new('comic_walker')
15
15
  @jar = jar
16
16
  @uuid = uuid
17
17
  end
18
18
 
19
- def start(&block)
20
- @https.start do
21
- block.call
22
- end
23
- end
24
-
25
19
  AID = 'KDCWI_JP'
26
20
  AVER = '1.2.0'
27
21
 
@@ -31,8 +25,8 @@ module ComicWalker
31
25
  end
32
26
 
33
27
  def create_session
34
- retried = 0
35
- begin
28
+ on_exception = lambda { |exception| create_user }
29
+ retryable(tries: 2, on: UnknownDeviceError, sleep: 0, exception_cb: on_exception) do
36
30
  res = post('/user_sessions/create', {
37
31
  DID: @uuid,
38
32
  PIN: @uuid,
@@ -47,14 +41,6 @@ module ComicWalker
47
41
  else
48
42
  JSON.parse(res.body)
49
43
  end
50
- rescue UnknownDeviceError => e
51
- if retried == 0
52
- retried += 1
53
- create_user
54
- retry
55
- else
56
- raise e
57
- end
58
44
  end
59
45
  end
60
46
 
@@ -68,7 +54,6 @@ module ComicWalker
68
54
  end
69
55
 
70
56
  def contents(params = {})
71
- retried = 0
72
57
  params = {
73
58
  AID: AID,
74
59
  AVER: AVER,
@@ -80,7 +65,8 @@ module ComicWalker
80
65
  languages: 'ja',
81
66
  }.merge(params)
82
67
 
83
- begin
68
+ on_exception = lambda { |exception| create_session }
69
+ retryable(tries: 2, on: NoValidSessionError, sleep: 0, exception_cb: on_exception) do
84
70
  res = get('/v1/contents', params)
85
71
  case res.body
86
72
  when 'NoValidSessionError'
@@ -88,14 +74,6 @@ module ComicWalker
88
74
  else
89
75
  JSON.parse(res.body)
90
76
  end
91
- rescue NoValidSessionError => e
92
- if retried == 0
93
- retried += 1
94
- create_session
95
- retry
96
- else
97
- raise e
98
- end
99
77
  end
100
78
  end
101
79
 
@@ -117,7 +95,7 @@ module ComicWalker
117
95
 
118
96
  def request_with_cookie(uri, req)
119
97
  req['cookie'] = HTTP::Cookie.cookie_value(@jar.cookies(uri.to_s))
120
- @https.request(req).tap do |res|
98
+ @https.request(URI.parse(uri.to_s), req).tap do |res|
121
99
  @jar.parse(res['set-cookie'], uri.to_s)
122
100
  end
123
101
  end
@@ -1,5 +1,5 @@
1
1
  module ComicWalker
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
 
4
4
  VIEWER_VERSION = "1.0.5_2014-04-11"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: comic_walker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kohei Suzuki
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rmagick
84
+ name: net-http-persistent
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -95,7 +95,21 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: rubyzip
98
+ name: retryable
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rmagick
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - ">="
@@ -143,7 +157,7 @@ files:
143
157
  - lib/comic_walker/cli.rb
144
158
  - lib/comic_walker/client.rb
145
159
  - lib/comic_walker/client/license.rb
146
- - lib/comic_walker/end_layer_decoder.rb
160
+ - lib/comic_walker/content_downloader.rb
147
161
  - lib/comic_walker/item_decoder.rb
148
162
  - lib/comic_walker/item_decoder/unknown.rb
149
163
  - lib/comic_walker/v1/client.rb
@@ -1,14 +0,0 @@
1
- require 'json'
2
- require 'comic_walker/cipher'
3
-
4
- module ComicWalker
5
- class EndLayerDecoder
6
- def initialize(key)
7
- @key = key
8
- end
9
-
10
- def decode(b64)
11
- JSON.parse(Cipher.decrypt_b64(@key, b64))
12
- end
13
- end
14
- end