jnc_api 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bb496cf77ffdd9ffb87bd9d7d33a897546d32f2266d07bb1daab3620d48e235
4
- data.tar.gz: 6b8bad8863037477e53977329d39149839769a61d5443f530b647510a8ab0924
3
+ metadata.gz: 4b5c0c7ff7e9d1c813d480ec4259924e06360da877dc5557b0e03a0a7ba8facd
4
+ data.tar.gz: d8a450bfb8247ed113e864e3bf485946a377e63113e71f4e600904bcfc9332df
5
5
  SHA512:
6
- metadata.gz: 24f6fa7999c012f0870e23d9fec16e3dc6076306da1cc84e7a470a831bdf9e6d56eef338440c3d7dc5ba1b05196657280c9d0f11f093898b5646e78317d53658
7
- data.tar.gz: 798236a71e88c3b8b50c0b8a789fcd6b0f31304a5039274b65d945612c71ee0a042ce93d8356b2665605f342d8b29c1b4a514300b9987b17e16c7af04be98cde
6
+ metadata.gz: fabdcf372e4dff0005238902b7711e2dd28c4c10473af52761e25b37dab99b4a5bc6e8fbbfd892d35e0aeb2d58081b3887deb572b882a56b390f29567eab4c72
7
+ data.tar.gz: 9e6226781e0f4f21265b5a4161ba7d01e87b841967bb37a3dfab3188321d36af9573dd02bb2cf897d5e462af3930c05ddc59cd5d8ee3c808e13a62094ee5f2a9
@@ -46,8 +46,26 @@ ActiveRecord::Schema.define do
46
46
  t.timestamps
47
47
  end
48
48
  end
49
+
50
+ unless table_exists?(:manga_parts)
51
+ create_table :manga_parts, force: :cascade do |t|
52
+ t.integer :part_id, null: false
53
+ t.text :uuid
54
+ t.text :ngtoken
55
+ t.text :kgtoken
56
+ t.text :webpub
57
+ t.timestamps
58
+ end
59
+ end
60
+
49
61
  end
50
62
 
51
63
  class Part < ActiveRecord::Base
64
+ has_many :manga_parts
52
65
  serialize :crypt_data, coder: JSON
53
66
  end
67
+
68
+ class MangaPart < ActiveRecord::Base
69
+ belongs_to :part
70
+ serialize :webpub, coder: JSON
71
+ end
@@ -23,7 +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
+ gem "optparse", require: "optparse"
27
27
 
28
28
  gem "jnc_api", path: "../..", require: "jnc_api"
29
29
  end
@@ -55,18 +55,22 @@ def assign_part_from_json(part, p)
55
55
  part.series_type = p["type"]
56
56
  part.legacy_id = p["partLegacyId"]
57
57
  part.title = p["partTitle"]
58
- part.updated = p["updated"]
59
58
  part.clear_data = p["clearData"]
60
59
  end
61
60
 
62
61
  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
62
+ OptionParser
63
+ .new do |opts|
64
+ opts.on(
65
+ "--slug=SLUG",
66
+ "Only download this part as identified by the slug"
67
+ ) { |s| options[:slug] = s }
66
68
  end
67
- end.parse!
69
+ .parse!
68
70
 
69
- user_id = JSON.parse(api.me)["legacyId"]
71
+ me = api.me
72
+ puts me
73
+ user_id = JSON.parse(me)["legacyId"]
70
74
 
71
75
  if options[:slug]
72
76
  opt_slug = options[:slug]
@@ -74,10 +78,11 @@ if options[:slug]
74
78
  part = Part.find_or_create_by(slug: opt_slug)
75
79
 
76
80
  assign_part_from_json(part, p)
81
+ part.updated = p["updated"]
77
82
 
78
83
  part.save!
79
84
 
80
- e = api.embed(p["partLegacyId"])
85
+ e = api.embed(p["partId"])
81
86
 
82
87
  part.data = e
83
88
  part.crypt_data = p["cryptData"]
@@ -98,37 +103,49 @@ else
98
103
  if part.date_published &&
99
104
  item["date_published"].to_datetime == part.date_published.to_datetime
100
105
  puts "Skipping part #{part.slug} (feed item already seen)"
106
+
101
107
  next
102
108
  end
103
109
 
104
- sleep 3
110
+ puts "sleeping for 5s"
111
+ sleep 5
105
112
 
106
113
  p = JSON.parse(api.parts_data(slug))
107
-
108
114
  assign_part_from_json(part, p)
109
115
 
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
116
  # skip items if the part is already up-to-date
123
117
  if part.data && p["updated"].to_datetime == part.updated.to_datetime
124
118
  puts "Skipping part #{part.slug} (feed item updated)"
119
+
125
120
  next
126
121
  end
127
122
 
128
- e = api.embed(p["partLegacyId"])
123
+ # note: we currently have no way to descramble the manga parts
124
+ if p["type"] == "MANGA"
125
+ puts "part #{part.slug} is manga, downloading webpub"
126
+ tokens = JSON.parse(api.manga_parts(part.legacy_id))
127
+ manga_part = part.manga_parts.find_or_create_by(uuid: tokens["uuid"])
128
+ manga_part.uuid = tokens["uuid"]
129
+ manga_part.ngtoken = tokens["ngtoken"]
130
+ manga_part.kgtoken = tokens["kgtoken"]
131
+
132
+ puts tokens
133
+ puts "sleeping for 30s"
134
+ sleep 5
135
+
136
+ manga_part.webpub = api.webpub(tokens["uuid"], tokens["ngtoken"])
137
+ manga_part.save!
138
+ else
139
+ e = api.embed(p["partLegacyId"])
140
+
141
+ part.data = e
142
+ part.crypt_data = p["cryptData"]
143
+ end
144
+
145
+ part.image = item["image"]
146
+ part.date_published = item["date_published"]
147
+ part.updated = p["updated"]
129
148
 
130
- part.data = e
131
- part.crypt_data = p["cryptData"]
132
149
  part.save!
133
150
 
134
151
  puts "Updated part #{part.slug}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JncApi
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/jnc_api.rb CHANGED
@@ -22,6 +22,19 @@ require "httparty"
22
22
  module JncApi
23
23
  class Error < StandardError
24
24
  end
25
+ V2_ROUTES = {
26
+ login: "https://labs.j-novel.club/app/v2/auth/login",
27
+ logout: "https://labs.j-novel.club/app/v2/auth/logout",
28
+ series: "https://labs.j-novel.club/app/v2/series",
29
+ me: "https://labs.j-novel.club/app/v2/me",
30
+ feed: "https://labs.j-novel.club/feed/user",
31
+ parts: "https://labs.j-novel.club/app/v2/parts",
32
+ embed: "https://labs.j-novel.club/embed/v2",
33
+ completion: "https://labs.j-novel.club/app/v2/me/completion",
34
+ manga_parts: "https://api.j-novel.club/api/mangaParts",
35
+ webpub: "https://m11.j-novel.club/nebel/wp"
36
+ }
37
+
25
38
  V1_ROUTES = {
26
39
  login: "https://labs.j-novel.club/app/v1/auth/login",
27
40
  logout: "https://labs.j-novel.club/app/v1/auth/logout",
@@ -32,13 +45,13 @@ module JncApi
32
45
  embed: "https://labs.j-novel.club/embed/",
33
46
  completion: "https://labs.j-novel.club/app/v1/me/completion",
34
47
  manga_parts: "https://api.j-novel.club/api/mangaParts",
35
- webpub: "https://m11.j-novel.club/nebel/wp",
48
+ webpub: "https://m11.j-novel.club/nebel/wp"
36
49
  }
37
50
 
38
51
  class Api
39
52
  attr_reader :token
40
53
 
41
- def self.login(login, password, routes = V1_ROUTES)
54
+ def self.login(login, password, routes = V2_ROUTES)
42
55
  response =
43
56
  HTTParty.post(
44
57
  routes[:login] + "?format=json",
@@ -61,7 +74,14 @@ module JncApi
61
74
  def initialize(routes: V1_ROUTES, token: nil)
62
75
  @routes = routes
63
76
  @token = token
64
- @default_headers = {"Content-Type" => "application/json"}
77
+ @default_headers = {
78
+ "User-Agent" =>
79
+ "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:127.0) Gecko/20100101 Firefox/127.0",
80
+ "Accept" =>
81
+ "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
82
+ "Accept-Language" => "en-US,en;q=0.5",
83
+ "Content-Type" => "application/json"
84
+ }
65
85
  @bearer = {"Authorization" => "Bearer #{@token}"}
66
86
  end
67
87
 
@@ -163,9 +183,10 @@ module JncApi
163
183
  # we can't seem to use bearer tokens for the manga endpoints
164
184
  def manga_parts(part_id)
165
185
  response =
166
- HTTParty.get(
167
- @routes[:manga_parts] + "/#{part_id}/token?access_token=#{@token}",
168
- )
186
+ HTTParty.get(
187
+ @routes[:manga_parts] + "/#{part_id}/token?access_token=#{@token}",
188
+ headers: @default_headers
189
+ )
169
190
 
170
191
  if response.success?
171
192
  response.body
@@ -178,10 +199,12 @@ module JncApi
178
199
  # likely for cheaper static hosting (as long as the locations are
179
200
  # difficult to guess such as uuid)
180
201
  def webpub(uuid, ngtoken)
181
- response = HTTParty.post(
182
- @routes[:webpub] + "/#{part_id}",
183
- body: ngtoken,
184
- )
202
+ response =
203
+ HTTParty.post(
204
+ @routes[:webpub] + "/#{uuid}",
205
+ body: ngtoken,
206
+ headers: @default_headers
207
+ )
185
208
 
186
209
  if response.success?
187
210
  response.body
@@ -189,6 +212,5 @@ module JncApi
189
212
  raise Error.new(response.inspect)
190
213
  end
191
214
  end
192
-
193
215
  end
194
216
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jnc_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - parasquid
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-07-02 00:00:00.000000000 Z
10
+ date: 2025-05-30 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: httparty
@@ -56,7 +55,6 @@ metadata:
56
55
  homepage_uri: https://codeberg.org/parasquid/jnc_api
57
56
  source_code_uri: https://codeberg.org/parasquid/jnc_api
58
57
  changelog_uri: https://codeberg.org/parasquid/jnc_api/src/branch/main/CHANGELOG.md
59
- post_install_message:
60
58
  rdoc_options: []
61
59
  require_paths:
62
60
  - lib
@@ -71,8 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
69
  - !ruby/object:Gem::Version
72
70
  version: '0'
73
71
  requirements: []
74
- rubygems_version: 3.4.6
75
- signing_key:
72
+ rubygems_version: 3.6.2
76
73
  specification_version: 4
77
74
  summary: Light wrapper for the J-Novel Club API.
78
75
  test_files: []