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 +4 -4
- data/README.md +2 -4
- data/examples/feed/tracker.rb +68 -35
- data/examples/feed/viewer.rb +12 -2
- data/lib/jnc_api/version.rb +1 -1
- data/lib/jnc_api.rb +34 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7bb496cf77ffdd9ffb87bd9d7d33a897546d32f2266d07bb1daab3620d48e235
|
4
|
+
data.tar.gz: 6b8bad8863037477e53977329d39149839769a61d5443f530b647510a8ab0924
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
13
|
+
$ gem install jnc_api
|
16
14
|
|
17
15
|
## Usage
|
18
16
|
|
data/examples/feed/tracker.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
79
|
-
|
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
|
-
|
69
|
+
user_id = JSON.parse(api.me)["legacyId"]
|
82
70
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
91
|
-
|
92
|
-
|
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
|
data/examples/feed/viewer.rb
CHANGED
@@ -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>
|
data/lib/jnc_api/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2024-07-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|