jnc_api 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
  SHA256:
3
- metadata.gz: 369eb625aa055dce9f77b637d1c6a6e8251ecdffe14dc2f232568ecbb8495237
4
- data.tar.gz: 439806ffe90c5804bfdc319e4f82789e0e77e66d095e9daf1d326e5f966fb9bd
3
+ metadata.gz: 7bb496cf77ffdd9ffb87bd9d7d33a897546d32f2266d07bb1daab3620d48e235
4
+ data.tar.gz: 6b8bad8863037477e53977329d39149839769a61d5443f530b647510a8ab0924
5
5
  SHA512:
6
- metadata.gz: 25dd6f4b7c9c46a4c841fe6bf3df95dddf8d89aff993d477a53593846b7412c0bd48387de2b817f13310c1f54d0612084ec8e6f2416385f3cf1cbb035d93f11a
7
- data.tar.gz: 79995090f88bf2fddca1cc93688ecb2774aba33c2b01fb56c58193ada886884818eab099ecd77e0d56cd229860fae8ec9ccf5c5adf10eaa76293f2c91064ca38
6
+ metadata.gz: 24f6fa7999c012f0870e23d9fec16e3dc6076306da1cc84e7a470a831bdf9e6d56eef338440c3d7dc5ba1b05196657280c9d0f11f093898b5646e78317d53658
7
+ data.tar.gz: 798236a71e88c3b8b50c0b8a789fcd6b0f31304a5039274b65d945612c71ee0a042ce93d8356b2665605f342d8b29c1b4a514300b9987b17e16c7af04be98cde
data/README.md CHANGED
@@ -4,15 +4,13 @@ A thin wrapper for the j-novel.club API
4
4
 
5
5
  ## Installation
6
6
 
7
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
8
-
9
7
  Install the gem and add to the application's Gemfile by executing:
10
8
 
11
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
9
+ $ bundle add jnc_api
12
10
 
13
11
  If bundler is not being used to manage dependencies, install the gem by executing:
14
12
 
15
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
13
+ $ gem install jnc_api
16
14
 
17
15
  ## Usage
18
16
 
@@ -23,6 +23,7 @@ gemfile do
23
23
  gem "activerecord", "~> 7.1", require: "active_record"
24
24
  gem "sqlite3", "~> 1.4", require: "sqlite3"
25
25
  gem "addressable", "~> 2.8", require: "addressable"
26
+ gem 'optparse', require: "optparse"
26
27
 
27
28
  gem "jnc_api", path: "../..", require: "jnc_api"
28
29
  end
@@ -46,52 +47,35 @@ else
46
47
 
47
48
  api = JncApi::Api.login(login, password)
48
49
  puts "Your JNC_TOKEN is #{api.token}"
50
+ puts "Press [Enter] to continue ..."
51
+ gets
49
52
  end
50
53
 
51
- user_id = JSON.parse(api.me)["legacyId"]
52
- feed = JSON.parse(api.feed(user_id))
53
-
54
- feed["items"].each do |item|
55
- sleep 3
56
-
57
- url = Addressable::URI.parse(item["id"])
58
- slug = url.path.split("/").last
59
-
60
- # persist/update part data
61
- part = Part.find_or_create_by(slug: slug)
62
-
63
- # skip items if the feed item is already seen
64
- if part.date_published &&
65
- item["date_published"].to_datetime == part.date_published.to_datetime
66
- puts "Skipping part #{part.slug} (feed item already seen)"
67
- next
68
- end
69
-
70
- p = JSON.parse(api.parts_data(slug))
71
-
54
+ def assign_part_from_json(part, p)
72
55
  part.series_type = p["type"]
73
56
  part.legacy_id = p["partLegacyId"]
74
57
  part.title = p["partTitle"]
75
58
  part.updated = p["updated"]
76
59
  part.clear_data = p["clearData"]
60
+ end
77
61
 
78
- part.image = item["image"]
79
- part.date_published = item["date_published"]
62
+ options = {}
63
+ OptionParser.new do |opts|
64
+ opts.on("--slug=SLUG", "Only download this part as identified by the slug") do |s|
65
+ options[:slug] = s
66
+ end
67
+ end.parse!
80
68
 
81
- part.save!
69
+ user_id = JSON.parse(api.me)["legacyId"]
82
70
 
83
- # skip items that are not novels e.g. manga
84
- # we currently have no way to decode the manga parts
85
- if p["type"] != "NOVEL"
86
- puts "Skipping part #{part.slug} (not NOVEL type unsupported)"
87
- next
88
- end
71
+ if options[:slug]
72
+ opt_slug = options[:slug]
73
+ p = JSON.parse(api.parts_data(opt_slug))
74
+ part = Part.find_or_create_by(slug: opt_slug)
89
75
 
90
- # skip items if the part is already up-to-date
91
- if part.data && p["updated"].to_datetime == part.updated.to_datetime
92
- puts "Skipping part #{part.slug} (feed item updated)"
93
- next
94
- end
76
+ assign_part_from_json(part, p)
77
+
78
+ part.save!
95
79
 
96
80
  e = api.embed(p["partLegacyId"])
97
81
 
@@ -100,4 +84,53 @@ feed["items"].each do |item|
100
84
  part.save!
101
85
 
102
86
  puts "Updated part #{part.slug}"
87
+ else
88
+ feed = JSON.parse(api.feed(user_id))
89
+
90
+ feed["items"].each do |item|
91
+ url = Addressable::URI.parse(item["id"])
92
+ slug = url.path.split("/").last
93
+
94
+ # persist/update part data
95
+ part = Part.find_or_create_by(slug: slug)
96
+
97
+ # skip items if the feed item is already seen
98
+ if part.date_published &&
99
+ item["date_published"].to_datetime == part.date_published.to_datetime
100
+ puts "Skipping part #{part.slug} (feed item already seen)"
101
+ next
102
+ end
103
+
104
+ sleep 3
105
+
106
+ p = JSON.parse(api.parts_data(slug))
107
+
108
+ assign_part_from_json(part, p)
109
+
110
+ part.image = item["image"]
111
+ part.date_published = item["date_published"]
112
+
113
+ part.save!
114
+
115
+ # skip items that are not novels e.g. manga
116
+ # we currently have no way to decode the manga parts
117
+ if p["type"] != "NOVEL"
118
+ puts "Skipping part #{part.slug} (not NOVEL type unsupported)"
119
+ next
120
+ end
121
+
122
+ # skip items if the part is already up-to-date
123
+ if part.data && p["updated"].to_datetime == part.updated.to_datetime
124
+ puts "Skipping part #{part.slug} (feed item updated)"
125
+ next
126
+ end
127
+
128
+ e = api.embed(p["partLegacyId"])
129
+
130
+ part.data = e
131
+ part.crypt_data = p["cryptData"]
132
+ part.save!
133
+
134
+ puts "Updated part #{part.slug}"
135
+ end
103
136
  end
@@ -39,7 +39,7 @@ class App < Sinatra::Application
39
39
  enable :inline_templates
40
40
 
41
41
  get "/" do
42
- @parts = Part.order("date_published DESC")
42
+ @parts = Part.order("date_published DESC").where(series_type: "NOVEL")
43
43
  erb :index
44
44
  end
45
45
 
@@ -65,6 +65,10 @@ __END__
65
65
  @@ layout
66
66
  <!doctype html>
67
67
  <html>
68
+ <head>
69
+ <link rel="preconnect" href="https://fonts.googleapis.com">
70
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
71
+ <link href="https://fonts.googleapis.com/css2?family=Goudy+Bookletter+1911&display=swap" rel="stylesheet">
68
72
  <style>
69
73
  body { background-color: linen; }
70
74
  section { max-width: 80em; }
@@ -72,8 +76,14 @@ __END__
72
76
  background-color: white;
73
77
  max-width: 50em;
74
78
  padding: 0.25em 1em;
79
+ font-family: "Goudy Bookletter 1911", serif;
80
+ font-weight: 600;
81
+ font-style: normal;
82
+ font-size: large;
83
+ line-height: 1.5em;
75
84
  }
76
85
  </style>
86
+ </head>
77
87
  <body><%= yield %></body>
78
88
  </html>
79
89
 
@@ -95,7 +105,7 @@ __END__
95
105
  <hr /><%= @part.data %><hr />
96
106
  <% if ENV["JNC_TOKEN"] %>
97
107
  <form action="/mark_as_completed/<%= @part.slug %>" method="post" id="mark-button">
98
- <p><input type="submit" value="Mark as completed"></p>
108
+ <p><input style="font-size: 2em;" type="submit" value="Mark as completed"></p>
99
109
  </div>
100
110
  <% end %>
101
111
  <div><p><a href="/">Back to index</a></p></div>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JncApi
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/jnc_api.rb CHANGED
@@ -30,7 +30,9 @@ module JncApi
30
30
  feed: "https://labs.j-novel.club/feed/user",
31
31
  parts: "https://labs.j-novel.club/app/v1/parts",
32
32
  embed: "https://labs.j-novel.club/embed/",
33
- completion: "https://labs.j-novel.club/app/v1/me/completion"
33
+ completion: "https://labs.j-novel.club/app/v1/me/completion",
34
+ manga_parts: "https://api.j-novel.club/api/mangaParts",
35
+ webpub: "https://m11.j-novel.club/nebel/wp",
34
36
  }
35
37
 
36
38
  class Api
@@ -157,5 +159,36 @@ module JncApi
157
159
  raise Error.new(response.inspect)
158
160
  end
159
161
  end
162
+
163
+ # we can't seem to use bearer tokens for the manga endpoints
164
+ def manga_parts(part_id)
165
+ response =
166
+ HTTParty.get(
167
+ @routes[:manga_parts] + "/#{part_id}/token?access_token=#{@token}",
168
+ )
169
+
170
+ if response.success?
171
+ response.body
172
+ else
173
+ raise Error.new(response.inspect)
174
+ end
175
+ end
176
+
177
+ # this endpoint seems to be on a CDN without need for authorization
178
+ # likely for cheaper static hosting (as long as the locations are
179
+ # difficult to guess such as uuid)
180
+ def webpub(uuid, ngtoken)
181
+ response = HTTParty.post(
182
+ @routes[:webpub] + "/#{part_id}",
183
+ body: ngtoken,
184
+ )
185
+
186
+ if response.success?
187
+ response.body
188
+ else
189
+ raise Error.new(response.inspect)
190
+ end
191
+ end
192
+
160
193
  end
161
194
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jnc_api
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
  - parasquid
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-19 00:00:00.000000000 Z
11
+ date: 2024-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty