comic_walker 0.1.0 → 0.2.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: e501759d14c46245526e2b8cbff03a38b9f390f9
4
- data.tar.gz: a1eccbbfd4a9f823614adc8c1adf8b6a98bebc5b
3
+ metadata.gz: 6a5cddb9273f468c064ce300cbe66a9ca1f87680
4
+ data.tar.gz: a2f75342e60753047ab80894a5e4f52281a41396
5
5
  SHA512:
6
- metadata.gz: 1fe26f0cc27ad11cda330ff55e502e27b4a09a71a9f1c264026042765a0a311e68cec060b1e555fb9a5e58ca44319c8baa75e6e863e81fddf0adcfb37d0f6eed
7
- data.tar.gz: 2d2393961e1859b017c960e29deed845913df53c98472382785418ccd9b51cf04675e17570f2c95d439efec7f1af9d6557651b0d0417e4a7220daae1660c24df
6
+ metadata.gz: 3002a2c97d87584ef29cc9c620d23abb58095a2df5234058d6b5daef8287ce0a76ec5ed53c46d12f6934e858b59333818b955f25348089362ef425917267f713
7
+ data.tar.gz: f7fa296b48bd74e0781458e753d90405e7c884540429e7378be8f49b29ebe3832b676e8c26c1d723da8aeaa622e10febdbf0fba0554a63aac97585546a5142d3
@@ -0,0 +1,8 @@
1
+ # 0.2.0 (2014-04-13)
2
+ - Rename to `save` subcommand.
3
+ - Add `contents` subcommand.
4
+ - Add `save-all` subcommand.
5
+ - Ignore JSON::ParserError from EndLayerDecoder.
6
+
7
+ # 0.1.0 (2014-03-30)
8
+ - Initial release.
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # ComicWalker
2
+ [![Gem Version](https://badge.fury.io/rb/comic_walker.svg)](http://badge.fury.io/rb/comic_walker)
2
3
 
3
4
  Client library for [ComicWalker](http://comic-walker.com/).
4
5
 
@@ -21,44 +22,24 @@ Or install it yourself as:
21
22
  ### CLI
22
23
 
23
24
  ```
24
- % comic-walker KDCW_KS04000001010001_68
25
+ % comic-walker save KDCW_KS04000001010001_68
25
26
  item/xhtml/p-cover.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/000_p-cover.jpg
26
27
  item/xhtml/p-001.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/001_p-001.jpg
27
28
  item/xhtml/p-002.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/002_p-002.jpg
28
29
  item/xhtml/p-white.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/003_p-white.jpg
29
30
  item/xhtml/p-004.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/004_p-004.jpg
30
- item/xhtml/p-005.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/005_p-005.jpg
31
- item/xhtml/p-006.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/006_p-006.jpg
32
- item/xhtml/p-007.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/007_p-007.jpg
33
- item/xhtml/p-008.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/008_p-008.jpg
34
- item/xhtml/p-009.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/009_p-009.jpg
35
- item/xhtml/p-010.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/010_p-010.jpg
36
- item/xhtml/p-011.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/011_p-011.jpg
37
- item/xhtml/p-012.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/012_p-012.jpg
38
- item/xhtml/p-013.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/013_p-013.jpg
39
- item/xhtml/p-014.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/014_p-014.jpg
40
- item/xhtml/p-015.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/015_p-015.jpg
41
- item/xhtml/p-016.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/016_p-016.jpg
42
- item/xhtml/p-017.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/017_p-017.jpg
43
- item/xhtml/p-018.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/018_p-018.jpg
44
- item/xhtml/p-019.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/019_p-019.jpg
45
- item/xhtml/p-020.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/020_p-020.jpg
46
- item/xhtml/p-021.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/021_p-021.jpg
47
- item/xhtml/p-022.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/022_p-022.jpg
48
- item/xhtml/p-023.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/023_p-023.jpg
49
- item/xhtml/p-024.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/024_p-024.jpg
50
- item/xhtml/p-025.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/025_p-025.jpg
51
- item/xhtml/p-026.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/026_p-026.jpg
52
- item/xhtml/p-027.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/027_p-027.jpg
53
- item/xhtml/p-028.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/028_p-028.jpg
54
- item/xhtml/p-029.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/029_p-029.jpg
55
- item/xhtml/p-030.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/030_p-030.jpg
56
- item/xhtml/p-031.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/031_p-031.jpg
57
- item/xhtml/p-032.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/032_p-032.jpg
58
- item/xhtml/p-033.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/033_p-033.jpg
59
- item/xhtml/p-034.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/034_p-034.jpg
60
- item/xhtml/p-035.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/035_p-035.jpg
61
- item/xhtml/p-036.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/036_p-036.jpg
31
+ (snip)
32
+ ```
33
+
34
+ ```
35
+ % comic-walker contents
36
+ 冴えない彼女の育てかた http://comic-walker.com/contents/detail/KDCW_FS02000006010000_68
37
+ Updated: 2014-03-24 13:39:24
38
+ Deliver: 2014-03-24 13:35:00 - 2064-03-12 17:54:00
39
+ Episodes:
40
+ 第1話 http://comic-walker.com/viewer/?cid=KDCW_FS02000006010001_68
41
+ Next: 2014-04-01 00:00:00
42
+ (snip)
62
43
  ```
63
44
 
64
45
  ## Contributing
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'comic_walker/cli'
3
3
 
4
- ComicWalker::CLI.new.run(ARGV)
4
+ ComicWalker::CLI.start(ARGV)
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.add_dependency "http-cookie"
26
26
  spec.add_dependency "rmagick"
27
27
  spec.add_dependency "rubyzip"
28
+ spec.add_dependency "thor"
28
29
  end
@@ -1,19 +1,77 @@
1
1
  require 'json'
2
2
  require 'pathname'
3
3
  require 'pp'
4
+ require 'thor'
5
+ require 'yaml'
4
6
  require 'comic_walker/client'
5
7
  require 'comic_walker/end_layer_decoder'
6
8
  require 'comic_walker/item_decoder'
9
+ require 'comic_walker/v1/client'
7
10
 
8
11
  module ComicWalker
9
- class CLI
10
- def run(cids)
12
+ class CLI < Thor
13
+ package_name 'comic_walker'
14
+
15
+ CONFIG_DIR = Pathname.new(ENV['HOME']).join('.comic-walker')
16
+ CONFIG_PATH = CONFIG_DIR.join('config.yml')
17
+ COOKIE_PATH = CONFIG_DIR.join('cookie.yml')
18
+
19
+ desc 'save CID1 CID2...', 'Save contents'
20
+ def save(*cids)
11
21
  client = Client.new
12
22
  cids.each do |cid|
13
23
  save_content(client, cid)
14
24
  end
15
25
  end
16
26
 
27
+ desc 'save-all CID1 CID2...', 'Save all sub-contents'
28
+ def save_all(*parent_cids)
29
+ jar = HTTP::CookieJar.new
30
+ load_cookies(jar)
31
+ child_cids = find_sub_contents(V1::Client.new(jar, load_uuid), parent_cids)
32
+ save_cookies(jar)
33
+ save(*child_cids)
34
+ end
35
+
36
+ desc 'contents', 'List contents'
37
+ option :page,
38
+ desc: 'Page',
39
+ aliases: :p,
40
+ type: :numeric
41
+ option :per_page,
42
+ desc: 'Per page',
43
+ type: :numeric
44
+ def contents
45
+ jar = HTTP::CookieJar.new
46
+ load_cookies(jar)
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
51
+ save_cookies(jar)
52
+
53
+ json['contents'].sort_by { |content| Time.parse(content['updated_at']) }.reverse_each do |content|
54
+ next unless content.has_key?('sub_contents')
55
+ puts "#{content['name']} http://comic-walker.com/contents/detail/#{content['content_id']}"
56
+ puts " Updated: #{format_time(content['updated_at'])}"
57
+ puts " Deliver: #{format_time(content['deliver_start_date'])} - #{format_time(content['deliver_stop_date'])}"
58
+ tags = content['tags']
59
+ puts " Episodes:"
60
+ tags['visible_episodes'].each_key do |episode|
61
+ _, desc, cid = *episode.split('|')
62
+ puts " #{desc} http://comic-walker.com/viewer/?cid=#{cid}"
63
+ end
64
+ if next_issue = tags['publication_date_of_next_issue']
65
+ next_issue = format_time(next_issue.keys.first)
66
+ else
67
+ next_issue = 'UNKNOWN'
68
+ end
69
+ puts " Next: #{next_issue}"
70
+ end
71
+ end
72
+
73
+ private
74
+
17
75
  def save_content(client, cid)
18
76
  license = client.get_license(cid)
19
77
  info = license.get_info.first
@@ -33,7 +91,11 @@ module ComicWalker
33
91
  pages[content['index']-1] = content['file']
34
92
  end
35
93
  when /end_layer\.json/
36
- pp EndLayerDecoder.new(license.key).decode(zip.read)
94
+ begin
95
+ pp EndLayerDecoder.new(license.key).decode(zip.read)
96
+ rescue JSON::ParserError
97
+ $stderr.puts "WARNING: #{cid}: Could not decode end_layer.json.enc"
98
+ end
37
99
  when %r{\Aitem/}
38
100
  items[entry.name] = zip.read
39
101
  end
@@ -50,5 +112,61 @@ module ComicWalker
50
112
  end
51
113
  end
52
114
  end
115
+
116
+ def load_cookies(jar)
117
+ if COOKIE_PATH.readable?
118
+ jar.load(COOKIE_PATH.to_s, session: true)
119
+ end
120
+ end
121
+
122
+ def save_cookies(jar)
123
+ CONFIG_DIR.mkpath
124
+ jar.save(COOKIE_PATH.to_s, session: true)
125
+ end
126
+
127
+ def load_uuid
128
+ CONFIG_DIR.mkpath
129
+ yaml = CONFIG_PATH.readable? && YAML.load_file(CONFIG_PATH.to_s)
130
+ uuid = yaml && yaml['uuid']
131
+ unless uuid
132
+ require 'securerandom'
133
+ uuid = SecureRandom.uuid.upcase
134
+ CONFIG_PATH.open('w') do |f|
135
+ YAML.dump({uuid: uuid}, f)
136
+ end
137
+ end
138
+ uuid
139
+ end
140
+
141
+ def format_time(str)
142
+ Time.parse(str).strftime('%F %X')
143
+ end
144
+
145
+ def find_sub_contents(client, parent_cids)
146
+ page = 1
147
+ per_page = 200
148
+ child_cids = {}
149
+ client.start do
150
+ until child_cids.size == parent_cids do
151
+ json = client.contents(page: page, per_page: per_page)
152
+ contents = json['contents']
153
+ if contents.empty?
154
+ break
155
+ end
156
+ contents.each do |c|
157
+ if parent_cids.include?(c['content_id'])
158
+ child_cids[c['content_id']] = c['sub_contents']
159
+ end
160
+ end
161
+ page += 1
162
+ end
163
+ end
164
+ parent_cids.each do |cid|
165
+ unless child_cids.has_key?(cid)
166
+ $stderr.puts "No such content: #{cid}"
167
+ end
168
+ end
169
+ child_cids.values.flatten
170
+ end
53
171
  end
54
172
  end
@@ -11,9 +11,21 @@ module ComicWalker
11
11
  @bid = '000000000000000000000NFBR'
12
12
  end
13
13
 
14
+ class Error < StandardError
15
+ attr_reader :code, :reason
16
+ def initialize(code, reason)
17
+ super("#{code}: #{reason}")
18
+ @code = code
19
+ @reason = reason
20
+ end
21
+ end
22
+
14
23
  def get_license(cid)
15
24
  u1 = get_u1(cid)
16
25
  json = get_li(cid, u1)
26
+ if error = json['error']
27
+ raise Error.new(error['code'], error['message'])
28
+ end
17
29
  license_b64 = json['license']
18
30
  License.new(JSON.parse(Cipher.decrypt_license(@bid, u1, license_b64)))
19
31
  end
@@ -0,0 +1,126 @@
1
+ require 'addressable/uri'
2
+ require 'http-cookie'
3
+ require 'json'
4
+ require 'net/http'
5
+
6
+ module ComicWalker
7
+ module V1
8
+ class Client
9
+ BASE_URI = Addressable::URI.parse("https://cnts.comic-walker.com")
10
+
11
+ 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
15
+ @jar = jar
16
+ @uuid = uuid
17
+ end
18
+
19
+ def start(&block)
20
+ @https.start do
21
+ block.call
22
+ end
23
+ end
24
+
25
+ AID = 'KDCWI_JP'
26
+ AVER = '1.2.0'
27
+
28
+ class UnknownDeviceError < StandardError
29
+ end
30
+ class NoValidSessionError < StandardError
31
+ end
32
+
33
+ def create_session
34
+ retried = 0
35
+ begin
36
+ res = post('/user_sessions/create', {
37
+ DID: @uuid,
38
+ PIN: @uuid,
39
+ AID: AID,
40
+ AVER: AVER,
41
+ })
42
+ case res.body
43
+ when 'UnknownDeviceError'
44
+ raise UnknownDeviceError.new
45
+ when 'ValidSessionExistsError'
46
+ nil
47
+ else
48
+ JSON.parse(res.body)
49
+ 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
+ end
59
+ end
60
+
61
+ def create_user
62
+ post('/users/create', {
63
+ DID: @uuid,
64
+ PIN: @uuid,
65
+ AID: AID,
66
+ AVER: AVER,
67
+ })
68
+ end
69
+
70
+ def contents(params = {})
71
+ retried = 0
72
+ params = {
73
+ AID: AID,
74
+ AVER: AVER,
75
+ W: '320',
76
+ H: '480',
77
+ FORMATS: 'epub_pdf_fixedlayout',
78
+ include_hidden: 1,
79
+ include_meta: 1,
80
+ languages: 'ja',
81
+ }.merge(params)
82
+
83
+ begin
84
+ res = get('/v1/contents', params)
85
+ case res.body
86
+ when 'NoValidSessionError'
87
+ raise NoValidSessionError.new
88
+ else
89
+ JSON.parse(res.body)
90
+ 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
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def get(path, params = {})
105
+ uri = BASE_URI.join(path)
106
+ uri.query_values = params
107
+ req = Net::HTTP::Get.new(uri.request_uri)
108
+ request_with_cookie(uri, req)
109
+ end
110
+
111
+ def post(path, params = {})
112
+ uri = BASE_URI.join(path)
113
+ req = Net::HTTP::Post.new(uri.request_uri)
114
+ req.set_form_data(params)
115
+ request_with_cookie(uri, req)
116
+ end
117
+
118
+ def request_with_cookie(uri, req)
119
+ req['cookie'] = HTTP::Cookie.cookie_value(@jar.cookies(uri.to_s))
120
+ @https.request(req).tap do |res|
121
+ @jar.parse(res['set-cookie'], uri.to_s)
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -1,5 +1,5 @@
1
1
  module ComicWalker
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
 
4
4
  VIEWER_VERSION = "1.0.2_2014-03-20"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: comic_walker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kohei Suzuki
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-30 00:00:00.000000000 Z
11
+ date: 2014-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: thor
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  description: Client library for ComicWalker
112
126
  email:
113
127
  - eagletmt@gmail.com
@@ -117,6 +131,7 @@ extensions: []
117
131
  extra_rdoc_files: []
118
132
  files:
119
133
  - ".gitignore"
134
+ - CHANGELOG.md
120
135
  - Gemfile
121
136
  - LICENSE.txt
122
137
  - README.md
@@ -131,6 +146,7 @@ files:
131
146
  - lib/comic_walker/end_layer_decoder.rb
132
147
  - lib/comic_walker/item_decoder.rb
133
148
  - lib/comic_walker/item_decoder/unknown.rb
149
+ - lib/comic_walker/v1/client.rb
134
150
  - lib/comic_walker/version.rb
135
151
  homepage: https://github.com/eagletmt/comic_walker
136
152
  licenses: