mangadex 5.4.9 → 5.4.11.3

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: 498b3099b585f68a83983fd391d7b379ffa6b37ad41073d14c6b44d426691e5a
4
- data.tar.gz: ba5b94653c90682241b2d98c9652fa3614ae7c1fc9b6a735919cea17aa7ab0b4
3
+ metadata.gz: fbbc515fa22a74a47bc30674ac2f8f746b73651c4a02d61e50dde6dd53640d1f
4
+ data.tar.gz: e2fb5177e657b300f6bc750fd000ab009acc19674510b4326776b3e16508bca7
5
5
  SHA512:
6
- metadata.gz: 8afe63e8dcd7612a0f85b86a5f9b393f12401c6fdbc21118e490739b6ae5a77790f34f323daa32b9ef4bd9b70be35f7882266b284c2ca7a02acb97992f31c262
7
- data.tar.gz: 2cfa10681bad8af8a1968cc528e923d82d9808948d3f174111e65b1a8ffaf6792febc717d5c5cef311c83331c75c0508f9dfde2e6119621e94eb1a954d5aa836
6
+ metadata.gz: 5417e6a54522a4d9188a3fb4d15ddcaf96b613e410a20572522ed1a41d668a725ba71fff10e69d90d95f2d4652ef391f1c84d911664fa5509ba3ca929c69d927
7
+ data.tar.gz: f831205af93f06716c89ae81ff3e4564fc98176f76a54c77dce4989d10c818553179d3d39d5354d5d4934d69d9b9af848faea30431e0523882be70de4b5b5ce3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mangadex (5.3.3.4)
4
+ mangadex (5.4.11)
5
5
  activesupport (~> 6.1)
6
6
  psych (~> 4.0.1)
7
7
  rest-client (~> 2.1)
@@ -32,9 +32,9 @@ GEM
32
32
  i18n (1.8.10)
33
33
  concurrent-ruby (~> 1.0)
34
34
  method_source (1.0.0)
35
- mime-types (3.3.1)
35
+ mime-types (3.4.1)
36
36
  mime-types-data (~> 3.2015)
37
- mime-types-data (3.2021.0901)
37
+ mime-types-data (3.2022.0105)
38
38
  minitest (5.14.4)
39
39
  mustermann (1.1.1)
40
40
  ruby2_keywords (~> 0.0.1)
@@ -42,7 +42,8 @@ GEM
42
42
  pry (0.14.1)
43
43
  coderay (~> 1.1)
44
44
  method_source (~> 1.0)
45
- psych (4.0.1)
45
+ psych (4.0.3)
46
+ stringio
46
47
  public_suffix (4.0.6)
47
48
  rack (2.2.3)
48
49
  rack-protection (2.1.0)
@@ -82,9 +83,10 @@ GEM
82
83
  smart_properties (1.16.3)
83
84
  sorbet (0.5.9152)
84
85
  sorbet-static (= 0.5.9152)
85
- sorbet-runtime (0.5.9211)
86
+ sorbet-runtime (0.5.9542)
86
87
  sorbet-static (0.5.9152-universal-darwin-20)
87
88
  sorbet-static (0.5.9152-x86_64-linux)
89
+ stringio (3.0.1)
88
90
  tilt (2.0.10)
89
91
  tzinfo (2.0.4)
90
92
  concurrent-ruby (~> 1.0)
data/README.md CHANGED
@@ -23,7 +23,341 @@ Or install it yourself as:
23
23
  ## Usage
24
24
 
25
25
  Please note that I tried my best to follow Mangadex's naming conventions for [their documentation](https://api.mangadex.org). Track the progress [here in an issue](https://github.com/thedrummeraki/mangadex/issues/5).
26
- To find out how to use the gem, you're welcome to [check this out](lib/mangadex).
26
+ Although a work a in progress, feel free to [check this out](lib/mangadex).
27
+
28
+ ### Basic Usage
29
+
30
+ Here's a couple of cool things you can do with the gem:
31
+
32
+ #### Get a list of manga
33
+
34
+ ```ruby
35
+ response = Mangadex::Manga.list
36
+ manga = response.data # Array of #<Mangadex::Manga>
37
+ ```
38
+
39
+ #### Get a manga by id, with cover_art
40
+
41
+ ```ruby
42
+ manga_id = 'd86cf65b-5f6c-437d-a0af-19a31f94ec55'
43
+ response = Mangadex::Manga.get(manga_id, includes: :cover_art)
44
+ entity = response.data # Object of #<Mangadex::Manga>
45
+
46
+ # Original size
47
+ entity.cover_art.image_url(size: :original)
48
+ entity.cover_art.image_url(size: :medium)
49
+ entity.cover_art.image_url(size: :small) # default size
50
+ ```
51
+
52
+ #### Get a manga's chapter list, ordered by volume and chapter number
53
+
54
+ ```ruby
55
+ manga_id = 'd86cf65b-5f6c-437d-a0af-19a31f94ec55'
56
+ manga_response = Mangadex::Manga.get(manga_id, includes: :cover_art)
57
+ entity = manga_response.data
58
+
59
+ chapter_response = Mangadex::Chapter.list(
60
+ manga: entity.id,
61
+ order: { volume: 'asc', chapter: 'asc' },
62
+ translated_language: 'en',
63
+ )
64
+ chapters = chapter_response.data # Array of #<Mangadex::Chapter>
65
+ ```
66
+
67
+ #### Get a chapter's pages
68
+
69
+ ```ruby
70
+ chapter_id = 'e7bb1892-7f83-4a89-bccc-0d6d403a85fc'
71
+ chapter = Mangadex::Chapter.get(chapter_id).data
72
+ pages = chapter.page_urls # Data saver true by default
73
+ ```
74
+
75
+ #### Search for manga by title
76
+
77
+ ```ruby
78
+ response = Mangadex::Manga.list(title: 'Ijiranaide nagatoro')
79
+ found_manga = response.data
80
+ ```
81
+
82
+ #### Authenticate
83
+
84
+ ```ruby
85
+ Mangadex::Auth.login(username: 'username', password: 'password') do |user|
86
+ # `user` is of type Mangadex::Api::User
87
+ puts(user.mangadex_user_id)
88
+ puts(user.session)
89
+ puts(user.refresh)
90
+ puts(user.session_valid_until)
91
+ end
92
+ ```
93
+
94
+ You can access the authenticated user by using context:
95
+
96
+ ```ruby
97
+ user = Mangadex.context.user
98
+ ```
99
+
100
+ #### Refresh the user's token
101
+
102
+ ```ruby
103
+ Mangadex.context.user.refresh_session! do |user|
104
+ # `user` is of type Mangadex::Api::User
105
+ puts(user.mangadex_user_id)
106
+ puts(user.session)
107
+ puts(user.refresh)
108
+ puts(user.session_valid_until)
109
+ end
110
+ ```
111
+
112
+ #### Create an public MDList, add then remove a manga
113
+
114
+ ```ruby
115
+ Mangadex::Auth.login(...)
116
+ response = Mangadex::CustomList.create(name: 'My awesome list!', visibility: 'public')
117
+ custom_list = response.data
118
+
119
+ manga_id = 'd86cf65b-5f6c-437d-a0af-19a31f94ec55'
120
+ # Add the manga
121
+ custom_list.add_manga(manga_id)
122
+
123
+ # Remove the manga
124
+ custom_list.remove_manga(manga_id)
125
+
126
+ # Get manga list
127
+ manga = custom_list.manga_details.data # Array of #<Mangadex::Manga>
128
+ ```
129
+
130
+ ### Are you on Rails?
131
+
132
+ This gem tries its best to be agnostic to popular frameworks like Rails. Here's however a couple of things to can do to integrate the gem to your project.
133
+
134
+ First, add the gem to your `Gemfile`.
135
+
136
+ #### Configurating the gem
137
+
138
+ Create a initilizer file to `config/initializers/mangadex.rb`. You can add the following:
139
+
140
+ ```ruby
141
+ Mangadex.configure do |config|
142
+ # Override the default content ratings
143
+ config.default_content_ratings = %w(safe suggestive)
144
+
145
+ # Override the Mangadex API URL (ie: proxy)
146
+ config.mangadex_url = 'https://my-proxy-mangadex-url.com'
147
+ end
148
+ ```
149
+
150
+ #### Authenticate your users
151
+
152
+ This could be useful if you want to support authentication. You will need to persist your user's session, refresh token and session expiry date.
153
+
154
+ ##### Persist the user information
155
+
156
+ If you haven't done so, create your user.
157
+
158
+ ```ruby
159
+ class CreateUsers < ActiveRecord::Migration[6.1]
160
+ def change
161
+ create_table :users do |t|
162
+ t.string :mangadex_user_id, null: false
163
+ t.string :username
164
+ t.string :session
165
+ t.string :refresh
166
+ t.datetime :session_valid_until
167
+
168
+ t.timestamps
169
+ end
170
+ end
171
+ end
172
+
173
+ ```
174
+
175
+ Already created your `User` class? Make sure it has all of the following:
176
+ - `mangade_user_id`: ID used to identify your user on Mangadex
177
+ - `username`: Your username
178
+ - `session`: The session token (valid for 15 minutes)
179
+ - `refresh`: The refresh token, used to refresh the session (valid for 1 month)
180
+ - `session_valid_until`: The time `session` session expires at
181
+
182
+ If anything is missing, create a migration.
183
+
184
+ #### Authentication flow on the controller
185
+
186
+ Add these methods to your controller's helper (ie: `ApplicationHelper`):
187
+
188
+ ```ruby
189
+ module ApplicationHelper
190
+ def current_user
191
+ @current_user ||= User.find_by(id: session[:id])
192
+ end
193
+
194
+ def logged_in?
195
+ current_user.present?
196
+ end
197
+
198
+ def log_in(user)
199
+ # `user` is an instance of your `User` class
200
+ session[:id] = user.id6
201
+ end
202
+
203
+ def log_out
204
+ # Logout from Mangadex
205
+ Mangadex::Auth.logout
206
+
207
+ # Remove the session
208
+ session.delete(:id)
209
+ end
210
+ end
211
+ ```
212
+
213
+ First make sure `ApplicationController` includes the helper above
214
+
215
+ ```ruby
216
+ class ApplicationController < ActionController::Base
217
+ include ApplicationHelper
218
+
219
+ # ...
220
+ end
221
+ ```
222
+
223
+ We recommend creating a controller for authentication. Here's how you can implement the login and logout actions:
224
+
225
+ ```ruby
226
+ # app/controllers/session_controller.rb
227
+ class SessionController < ApplicationController
228
+ # GET /login
229
+ def new
230
+ # render the login form
231
+ end
232
+
233
+ # POST /login
234
+ def create
235
+ username = params[:username]
236
+ password = params[:password]
237
+
238
+ # You can also use `email` instead of `username` to log in
239
+ user = Mangadex::Auth.login(username: username, password: password) do |mangadex_user|
240
+ # Find the user by mangadex user id (or initialize if it doesn't exist)
241
+ our_user = User.find_or_initialize_by(mangadex_user_id: mangadex_user.mangadex_user_id) do |new_user|
242
+ new_user.username = mangadex_user.data.username
243
+ end
244
+
245
+ # Update the session info data
246
+ our_user.session = mangadex_user.session
247
+ our_user.refresh = mangadex_user.refresh
248
+ our_user.session_valid_until = mangadex_user.session_valid_until
249
+
250
+ # ...then save the user
251
+ our_user.save!
252
+ our_user
253
+ end
254
+
255
+ # `user` will be an instance of your `User` class
256
+ # Now, we can log in then redirect to the root.
257
+ log_in(user)
258
+ redirect_to(root_path)
259
+ rescue Mangadex::Errors::AuthenticationError => error
260
+ # See https://api.mangadex.org/docs.html to learn more about errors
261
+ Rails.logger.error(error.response.errors)
262
+
263
+ # Handle authentication errors here
264
+ end
265
+
266
+ # DELETE /logout
267
+ def destroy
268
+ log_out
269
+
270
+ redirect_to(root_path)
271
+ end
272
+ end
273
+ ```
274
+
275
+ Finally, add the routes.
276
+
277
+ ```ruby
278
+ # config/routes.rb
279
+ Rails.application.routes.draw do
280
+ # ...
281
+ get '/login' => 'session#new'
282
+ post '/login' => 'session#create'
283
+ delete '/logout' => 'session#destroy'
284
+ end
285
+ ```
286
+
287
+ #### Protected resources
288
+
289
+ Here's an example of a controller that requires every action to be logged in. This is based on the steps above.
290
+
291
+ ```ruby
292
+ class ProtectedController < ApplicationController
293
+ before_action :ensure_logged_in!
294
+
295
+ private
296
+
297
+ def ensure_logged_in!
298
+ return if logged_in?
299
+
300
+ redirect_to(login_path) # go to /login
301
+ end
302
+ end
303
+ ```
304
+
305
+ We're going with managing (list, create, show, edit, delete) MDLists (ie: custom lists). __We're not using strong params below to keep things simple, but you should, especially when mutating data (ie: creating and editing)__.
306
+
307
+ ```ruby
308
+ class CustomListsController < ProtectedController
309
+ # GET /custom_list
310
+ def index
311
+ @custom_lists = Mangadex::CustomList.list
312
+ end
313
+
314
+ # GET /custom_list/new
315
+ def new
316
+ # new custom list form
317
+ end
318
+
319
+ # POST /custom_list
320
+ def create
321
+ @custom_list = Mangadex::CustomList.create(
322
+ name: params[:name],
323
+ visibility: params[:visibility],
324
+ manga: params[:manga], # Manga ID
325
+ )
326
+ end
327
+
328
+ # GET /custom_list/<id>
329
+ def show
330
+ @custom_list = Mangadex::CustomList.get(params[:id])
331
+ end
332
+
333
+ # GET /custom_list/<id>/edit
334
+ def edit
335
+ @custom_list = Mangadex::CustomList.get(params[:id])
336
+ # edit custom list form
337
+ end
338
+
339
+ # PUT /custom_list/<id>
340
+ # PATCH /custom_list/<id>
341
+ def update
342
+ # Note: when updating the custom list, be sure to pass in
343
+ # the current version number!
344
+ @custom_list = Mangadex::CustomList.update(
345
+ params[:id],
346
+ {
347
+ name: params[:name],
348
+ visibility: params[:visibility],
349
+ manga: params[:manga],
350
+ version: params[:version].to_i,
351
+ }
352
+ )
353
+ end
354
+
355
+ # DELETE /custom_list/<id>
356
+ def destroy
357
+ Mangadex::CustomList.delete(params[:id])
358
+ end
359
+ end
360
+ ```
27
361
 
28
362
  ## Development
29
363
 
data/bin/console CHANGED
@@ -3,16 +3,23 @@
3
3
  require "bundler/setup"
4
4
  require "mangadex"
5
5
 
6
- username, password, email = [
7
- ENV['MD_USERNAME'],
8
- ENV['MD_PASSWORD'],
9
- ENV['MD_EMAIL'],
10
- ]
6
+ def try_logging_in
7
+ username, password, email = [
8
+ ENV['MD_USERNAME'],
9
+ ENV['MD_PASSWORD'],
10
+ ENV['MD_EMAIL'],
11
+ ]
11
12
 
12
- if (username || email) && password
13
- Mangadex::Auth.login(username: username, email: email, password: password)
13
+ if (username || email) && password
14
+ Mangadex::Auth.login(username: username, email: email, password: password)
15
+ end
16
+ rescue Mangadex::Errors::StandardError => e
17
+ puts e
18
+ false
14
19
  end
15
20
 
21
+ try_logging_in
22
+
16
23
  # You can add fixtures and/or initialization code here to make experimenting
17
24
  # with your gem easier. You can also use a different console, if you like.
18
25
 
@@ -61,10 +61,7 @@ user.data
61
61
 
62
62
  ```ruby
63
63
  # Refreshes the tokens now (Boolean)
64
- user.refresh!
65
-
66
- # Refreshes the tokens if expired, then return user itself (Mangadex::Api::User)
67
- user.with_valid_session
64
+ user.refresh_session!
68
65
 
69
66
  # Returns if user.session has expired (Boolean)
70
67
  user.session_expired?
data/lib/config.rb CHANGED
@@ -4,11 +4,6 @@ module Mangadex
4
4
  class Config
5
5
  extend T::Sig
6
6
 
7
- # Class used to persist users
8
- # Must respond to: :session, :refresh, :mangadex_user_id
9
- sig { returns(Class) }
10
- attr_accessor :user_class
11
-
12
7
  # Persisting strategy. See Mangadex::Storage::Base for more details.
13
8
  sig { returns(Class) }
14
9
  attr_accessor :storage_class
@@ -16,16 +11,19 @@ module Mangadex
16
11
  sig { returns(T::Array[ContentRating]) }
17
12
  attr_accessor :default_content_ratings
18
13
 
14
+ sig { returns(String) }
15
+ attr_accessor :mangadex_url
16
+
19
17
  sig { void }
20
18
  def initialize
21
- @user_class = Api::User
22
19
  @storage_class = Storage::Memory
23
20
  @default_content_ratings = ContentRating.parse(['safe', 'suggestive', 'erotica'])
21
+ @mangadex_url = 'https://api.mangadex.org'
24
22
  end
25
23
 
26
24
  sig { params(klass: Class).void }
27
25
  def user_class=(klass)
28
- missing_methods = [:session, :refresh, :mangadex_user_id] - klass.instance_methods
26
+ missing_methods = [:session, :refresh, :mangadex_user_id] - klass.new.methods
29
27
  if missing_methods.empty?
30
28
  @user_class = klass
31
29
  else
@@ -7,6 +7,13 @@ module Mangadex
7
7
  attr_accessor :mangadex_user_id, :session, :refresh, :session_valid_until
8
8
  attr_reader :data
9
9
 
10
+ SERIALIZABLE_KEYS = %i(
11
+ mangadex_user_id
12
+ session
13
+ refresh
14
+ session_valid_until
15
+ )
16
+
10
17
  sig { params(mangadex_user_id: String, session: T.nilable(String), refresh: T.nilable(String), data: T.untyped, session_valid_until: T.nilable(Time)).void }
11
18
  def initialize(mangadex_user_id:, session: nil, refresh: nil, data: nil, session_valid_until: nil)
12
19
  raise ArgumentError, 'Missing mangadex_user_id' if mangadex_user_id.to_s.empty?
@@ -18,10 +25,33 @@ module Mangadex
18
25
  @data = data
19
26
  end
20
27
 
28
+ # true: The tokens were successfully refreshed if the session expired
29
+ # false: Error: refresh token empty or could not refresh the token on the server
30
+ sig do
31
+ params(
32
+ block: T.nilable(
33
+ T.proc.params(arg0: User).returns(
34
+ T.untyped)
35
+ )
36
+ ).returns(T::Boolean)
37
+ end
38
+ def refresh_session(&block)
39
+ return true unless session_expired?
40
+
41
+ refresh_session!(&block)
42
+ end
43
+
21
44
  # true: The tokens were successfully refreshed
22
45
  # false: Error: refresh token empty or could not refresh the token on the server
23
- sig { returns(T::Boolean) }
24
- def refresh!
46
+ sig do
47
+ params(
48
+ block: T.nilable(
49
+ T.proc.params(arg0: User).returns(
50
+ T.untyped)
51
+ )
52
+ ).returns(T::Boolean)
53
+ end
54
+ def refresh_session!(&block)
25
55
  return false if refresh.nil?
26
56
 
27
57
  response = Mangadex::Internal::Request.post('/auth/refresh', payload: { token: refresh })
@@ -31,15 +61,11 @@ module Mangadex
31
61
  @refresh = response.dig('token', 'refresh')
32
62
  @session = response.dig('token', 'session')
33
63
 
34
- true
35
- end
64
+ if block_given?
65
+ yield(self)
66
+ end
36
67
 
37
- sig { returns(User) }
38
- def with_valid_session
39
- session_expired? && refresh!
40
- self
41
- ensure
42
- self
68
+ true
43
69
  end
44
70
 
45
71
  sig { returns(T::Boolean) }
@@ -65,6 +91,18 @@ module Mangadex
65
91
  !mangadex_user_id.nil? && !mangadex_user_id.strip.empty?
66
92
  end
67
93
 
94
+ sig { params(except: T.any(Symbol, T::Array[Symbol])).returns(Hash) }
95
+ def to_h(except: [])
96
+ except = Array(except).map(&:to_sym)
97
+ keys = SERIALIZABLE_KEYS.reject do |key|
98
+ except.include?(key)
99
+ end
100
+
101
+ keys.map do |key|
102
+ [key, send(key)]
103
+ end.to_h
104
+ end
105
+
68
106
  sig { params(mangadex_user_id: T.nilable(String)).returns(T.nilable(User)) }
69
107
  def self.from_storage(mangadex_user_id)
70
108
  return if mangadex_user_id.nil?
@@ -81,7 +119,7 @@ module Mangadex
81
119
  session: session,
82
120
  refresh: refresh,
83
121
  session_valid_until: session_valid_until,
84
- ).with_valid_session
122
+ )
85
123
  else
86
124
  nil
87
125
  end
@@ -18,7 +18,7 @@ module Mangadex
18
18
  if version != Mangadex::Version::STRING
19
19
  warn(
20
20
  "[Warning] This gem is compatible with #{Mangadex::Version::STRING} but it looks like Mangadex is at #{version}",
21
- "[Warning] Check out #{Mangadex::Internal::Request::BASE_URI} for more information.",
21
+ "[Warning] Check out #{Mangadex.configuration.mangadex_url} for more information.",
22
22
  )
23
23
  end
24
24
 
@@ -20,6 +20,8 @@ module Mangadex
20
20
  :fantia,
21
21
  :tumblr,
22
22
  :youtube,
23
+ :weibo,
24
+ :naver,
23
25
  :website,
24
26
  :version,
25
27
  :created_at,
@@ -0,0 +1,30 @@
1
+ # typed: true
2
+
3
+ module Mangadex
4
+ class AtHome
5
+ extend T::Sig
6
+
7
+ sig { params(chapter_id: String).returns(T::Api::GenericResponse) }
8
+ def self.server(chapter_id)
9
+ Mangadex::Internal::Request.get(
10
+ "/at-home/server/#{chapter_id}",
11
+ )
12
+ end
13
+
14
+ sig { params(chapter_id: String, data_saver: T::Boolean).returns(T.nilable(T::Array[String])) }
15
+ def self.page_urls(chapter_id, data_saver: true)
16
+ response = self.server(chapter_id)
17
+ return if response.is_a?(Mangadex::Api::Response)
18
+
19
+ base_url = response['baseUrl']
20
+ chapter_data = response['chapter']
21
+ hash = chapter_data['hash']
22
+ source = data_saver ? chapter_data['dataSaver'] : chapter_data['data']
23
+ data_source = data_saver ? 'data-saver' : 'data'
24
+
25
+ source.map do |filename|
26
+ [base_url, data_source, hash, filename].join('/')
27
+ end
28
+ end
29
+ end
30
+ end
data/lib/mangadex/auth.rb CHANGED
@@ -3,8 +3,17 @@ module Mangadex
3
3
  class Auth
4
4
  extend T::Sig
5
5
 
6
- sig { params(username: T.nilable(String), email: T.nilable(String), password: String).returns(T.nilable(Mangadex::Api::User)) }
7
- def self.login(username: nil, email: nil, password: nil)
6
+ sig do
7
+ params(
8
+ username: T.nilable(String),
9
+ email: T.nilable(String),
10
+ password: String,
11
+ block: T.nilable(T.proc.returns(T.untyped))
12
+ ).returns(
13
+ T.nilable(T.untyped),
14
+ )
15
+ end
16
+ def self.login(username: nil, email: nil, password: nil, &block)
8
17
  args = { password: password }
9
18
  args.merge!(email: email) if email
10
19
  args.merge!(username: username) if username
@@ -18,6 +27,8 @@ module Mangadex
18
27
  }),
19
28
  )
20
29
 
30
+ session_valid_until = Time.now + (15 * 60)
31
+
21
32
  session = response.dig('token', 'session')
22
33
  refresh = response.dig('token', 'refresh')
23
34
 
@@ -28,12 +39,18 @@ module Mangadex
28
39
  session: session,
29
40
  refresh: refresh,
30
41
  data: mangadex_user.data,
42
+ session_valid_until: session_valid_until,
31
43
  )
32
- return if user.session_expired?
44
+
45
+ user.persist
46
+ user
33
47
 
34
48
  Mangadex.context.user = user
35
49
 
36
- user.persist
50
+ if block_given?
51
+ return yield(user)
52
+ end
53
+
37
54
  user
38
55
  rescue Errors::UnauthorizedError => error
39
56
  raise Errors::AuthenticationError.new(error.response)
@@ -67,7 +84,7 @@ module Mangadex
67
84
 
68
85
  sig { returns(T::Boolean) }
69
86
  def self.refresh_token
70
- !(Mangadex.context.user&.refresh!).nil?
87
+ !(Mangadex.context.user&.refresh_session!).nil?
71
88
  end
72
89
 
73
90
  private
@@ -75,31 +75,9 @@ module Mangadex
75
75
  attributes&.title.presence || chapter.presence && "Chapter #{chapter}" || "N/A"
76
76
  end
77
77
 
78
- sig { returns(T.nilable(String)) }
79
- def locale
80
- found_locale = translated_language.split('-').first
81
- return if found_locale.nil?
82
-
83
- ISO_639.find(found_locale)
84
- end
85
-
86
- sig { returns(T.nilable(String)) }
87
- def locale_name
88
- locale&.english_name
89
- end
90
-
91
- sig { returns(T.nilable(String)) }
92
- def preview_image_url
93
- return if data_saver.empty?
94
-
95
- "https://uploads.mangadex.org/data-saver/#{attributes.hash}/#{data_saver.first}"
96
- end
97
-
98
- def as_json(*)
99
- super.merge({
100
- locale_name: locale_name,
101
- preview_image_url: preview_image_url,
102
- })
78
+ sig { params(data_saver: T::Boolean).returns(T.nilable(T::Array[String])) }
79
+ def page_urls(data_saver: true)
80
+ Mangadex::AtHome.page_urls(id, data_saver: data_saver)
103
81
  end
104
82
 
105
83
  def self.attributes_to_inspect
@@ -135,7 +135,8 @@ module Mangadex
135
135
 
136
136
  sig { params(args: T::Api::Arguments).returns(T.nilable(Mangadex::Api::Response[Manga])) }
137
137
  def manga_details(**args)
138
- ids = mangas.map(&:id)
138
+ custom_list = Mangadex::CustomList.get(id).data
139
+ ids = custom_list.mangas.map(&:id)
139
140
  ids.any? ? Mangadex::Manga.list(**args.merge(ids: ids)) : nil
140
141
  end
141
142
 
@@ -18,7 +18,7 @@ module Mangadex
18
18
 
19
19
  sig { returns(T.nilable(Mangadex::Api::User)) }
20
20
  def user
21
- @ignore_user ? nil : @user&.with_valid_session
21
+ @ignore_user ? nil : @user
22
22
  rescue Mangadex::Errors::UnauthorizedError
23
23
  warn("A user is present but not authenticated!")
24
24
  nil
@@ -29,7 +29,7 @@ module Mangadex
29
29
  @tags ||= Mangadex::Tag.list.data
30
30
  end
31
31
 
32
- sig { params(user: T.nilable(T.any(Hash, Mangadex::Api::User, Mangadex::User)), block: T.proc.returns(T.untyped)).returns(T.untyped) }
32
+ sig { params(user: T.nilable(T.untyped), block: T.proc.returns(T.untyped)).returns(T.untyped) }
33
33
  def with_user(user, &block)
34
34
  temp_set_value("user", user) do
35
35
  yield
@@ -64,11 +64,12 @@ module Mangadex
64
64
  session: user[:session],
65
65
  refresh: user[:refresh],
66
66
  )
67
- elsif user_object?(user)
67
+ elsif Mangadex::Internal::Context.user_object?(user)
68
68
  @user = Mangadex::Api::User.new(
69
69
  mangadex_user_id: user.mangadex_user_id.to_s,
70
70
  session: user.session,
71
71
  refresh: user.refresh,
72
+ session_valid_until: user.session_valid_until,
72
73
  data: user,
73
74
  )
74
75
  elsif user.nil?
@@ -113,18 +114,18 @@ module Mangadex
113
114
  end
114
115
  end
115
116
 
116
- private
117
-
118
- def user_object?(user)
117
+ def self.user_object?(user)
119
118
  return false if user.nil?
120
119
 
121
- missing_methods = [:session, :refresh, :mangadex_user_id] - user.methods
120
+ missing_methods = [:session, :refresh, :mangadex_user_id, :save] - user.methods
122
121
  return true if missing_methods.empty?
123
122
 
124
123
  warn("Potential user object #{user} is missing #{missing_methods}")
125
124
  false
126
125
  end
127
126
 
127
+ private
128
+
128
129
  def temp_set_value(name, value, &block)
129
130
  setter_method_name = "#{name}="
130
131
 
@@ -108,10 +108,11 @@ module Mangadex
108
108
  offset: { accepts: Integer },
109
109
  ids: { accepts: [String] },
110
110
  title: { accepts: String },
111
+ manga: { accepts: String },
111
112
  groups: { accepts: [String] },
112
113
  uploader: { accepts: [String], converts: converts(:to_a) },
113
114
  chapter: { accepts: [String], converts: converts(:to_a) },
114
- translated_language: { accepts: String },
115
+ translated_language: { accepts: [String], converts: converts(:to_a) },
115
116
  original_language: { accepts: [String] },
116
117
  excluded_original_language: { accepts: [String] },
117
118
  content_rating: { accepts: %w(safe suggestive erotica pornographic), converts: converts(:to_a) },
@@ -7,7 +7,6 @@ require 'active_support/core_ext/hash/keys'
7
7
  module Mangadex
8
8
  module Internal
9
9
  class Request
10
- BASE_URI = 'https://api.mangadex.org'
11
10
  ALLOWED_METHODS = %i(get post put delete).freeze
12
11
 
13
12
  attr_accessor :path, :headers, :payload, :method, :raw
@@ -51,7 +50,7 @@ module Mangadex
51
50
  end
52
51
 
53
52
  def run!(raw: false, auth: false)
54
- payload_details = request_payload ? "Payload: #{request_payload}" : "{no-payload}"
53
+ payload_details = request_payload ? "Payload: #{sensitive_request_payload}" : "{no-payload}"
55
54
  puts("[#{self.class.name}] #{method.to_s.upcase} #{request_url} #{payload_details}")
56
55
 
57
56
  raise Mangadex::Errors::UserNotLoggedIn.new if auth && Mangadex.context.user.nil?
@@ -100,7 +99,7 @@ module Mangadex
100
99
 
101
100
  def request_url
102
101
  request_path = path.start_with?('/') ? path : "/#{path}"
103
- "#{BASE_URI}#{request_path}"
102
+ "#{Mangadex.configuration.mangadex_url}#{request_path}"
104
103
  end
105
104
 
106
105
  def request_payload
@@ -108,12 +107,20 @@ module Mangadex
108
107
 
109
108
  JSON.generate(payload)
110
109
  end
110
+
111
+ def sensitive_request_payload(sensitive_fields: %w(password token))
112
+ payload = JSON.parse(request_payload)
113
+ sensitive_fields.map(&:to_s).each do |field|
114
+ payload[field] = '[REDACTED]' if payload.key?(field)
115
+ end
116
+ JSON.generate(payload)
117
+ end
111
118
 
112
119
  def request_headers
113
120
  return headers if Mangadex.context.user.nil?
114
121
 
115
122
  headers.merge({
116
- Authorization: Mangadex.context.user.with_valid_session.session,
123
+ Authorization: Mangadex.context.user.session,
117
124
  })
118
125
  end
119
126
 
@@ -31,7 +31,7 @@ module Mangadex
31
31
  self.name.split('::').last.underscore
32
32
  end
33
33
 
34
- def from_data(data, related_type: nil)
34
+ def from_data(data, related_type: nil, source_obj: nil)
35
35
  base_class_name = self.name.gsub('::', '_')
36
36
  klass_name = self.name
37
37
  target_attributes_class_name = "#{base_class_name}_Attributes"
@@ -57,7 +57,7 @@ module Mangadex
57
57
  data = data.with_indifferent_access
58
58
 
59
59
  relationships = data['relationships']&.map do |relationship_data|
60
- Relationship.from_data(relationship_data)
60
+ Relationship.from_data(relationship_data, MangadexObject.new(**data))
61
61
  end
62
62
 
63
63
  attributes = klass.new(**Hash(data['attributes']))
@@ -69,8 +69,11 @@ module Mangadex
69
69
  related_type: related_type,
70
70
  }
71
71
 
72
+ relationships = [source_obj].compact unless relationships.present?
72
73
  initialize_hash.merge!({relationships: relationships}) if relationships.present?
73
74
 
75
+ # binding.pry
76
+
74
77
  new(**initialize_hash)
75
78
  end
76
79
  end
@@ -70,12 +70,13 @@ module Mangadex
70
70
 
71
71
  sig { params(id: String, args: T::Api::Arguments).returns(T::Api::MangaResponse) }
72
72
  def self.view(id, **args)
73
+ to_a = Mangadex::Internal::Definition.converts(:to_a)
73
74
  Mangadex::Internal::Definition.must(id)
74
75
 
75
76
  Mangadex::Internal::Request.get(
76
77
  '/manga/%{id}' % {id: id},
77
78
  Mangadex::Internal::Definition.validate(args, {
78
- includes: { accepts: Array },
79
+ includes: { accepts: Array, converts: to_a },
79
80
  })
80
81
  )
81
82
  end
@@ -126,9 +127,9 @@ module Mangadex
126
127
  )
127
128
  end
128
129
 
129
- sig { params(status: String).returns(T::Api::GenericResponse) }
130
- def self.all_reading_status(status)
131
- args = { status: status }
130
+ sig { params(status: T.nilable(String)).returns(T::Api::GenericResponse) }
131
+ def self.all_reading_status(status = nil)
132
+ args = { status: status } if status.present?
132
133
 
133
134
  Mangadex::Internal::Request.get(
134
135
  '/manga/status',
@@ -165,6 +166,15 @@ module Mangadex
165
166
  )
166
167
  end
167
168
 
169
+ sig { params(id: T.any(T::Array[String], String), grouped: T::Boolean).returns(T::Api::GenericResponse) }
170
+ def self.read_markers(id, grouped: false)
171
+ Mangadex::Internal::Request.get(
172
+ '/manga/read',
173
+ { ids: Array(id), grouped: grouped },
174
+ auth: true,
175
+ )
176
+ end
177
+
168
178
  # Untested API endpoints
169
179
  sig { params(id: String, args: T::Api::Arguments).returns(T::Api::MangaResponse) }
170
180
  def self.update(id, **args)
@@ -202,5 +212,11 @@ module Mangadex
202
212
 
203
213
  ContentRating.new(attributes.content_rating)
204
214
  end
215
+
216
+ sig { params(args: T::Api::Arguments).returns(Mangadex::Api::Response[Chapter]) }
217
+ def chapters(**args)
218
+ chapter_args = args.merge({manga: id})
219
+ Chapter.list(**chapter_args)
220
+ end
205
221
  end
206
222
  end
@@ -22,19 +22,24 @@ module Mangadex
22
22
  ).freeze
23
23
 
24
24
  class << self
25
- def from_data(data)
25
+ # data: Relationship data
26
+ # source_obj: The object to witch the object belongs to
27
+ def from_data(data, source_obj = nil)
26
28
  data = data.with_indifferent_access
27
29
  klass = class_for_relationship_type(data['type'])
28
30
 
29
31
  if klass && data['attributes']&.any?
30
- return klass.from_data(data, related_type: data['related'])
32
+ return klass.from_data(data, related_type: data['related'], source_obj: source_obj)
31
33
  end
32
34
 
35
+ relationships = [source_obj] if source_obj
36
+
33
37
  new(
34
38
  id: data['id'],
35
39
  type: data['type'],
36
40
  attributes: OpenStruct.new(data['attributes']),
37
41
  related: data['related'],
42
+ relationships: relationships,
38
43
  )
39
44
  end
40
45
 
@@ -14,9 +14,10 @@ module Mangadex
14
14
  :locked,
15
15
  :official,
16
16
  :verified,
17
- :focused_language,
17
+ :focused_languages,
18
18
  :publish_delay,
19
19
  :inactive,
20
+ :manga_updates,
20
21
  :version,
21
22
  :created_at,
22
23
  :updated_at
@@ -22,3 +22,6 @@ require_relative "report_reason"
22
22
 
23
23
  # Relationship
24
24
  require_relative "relationship"
25
+
26
+ # At home
27
+ require_relative "at_home"
@@ -3,8 +3,8 @@ module Mangadex
3
3
  module Version
4
4
  MAJOR = "5"
5
5
  MINOR = "4"
6
- TINY = "9"
7
- PATCH = nil
6
+ TINY = "11"
7
+ PATCH = "3"
8
8
 
9
9
  STRING = [MAJOR, MINOR, TINY].compact.join('.')
10
10
  FULL = [MAJOR, MINOR, TINY, PATCH].compact.join('.')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mangadex
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.4.9
4
+ version: 5.4.11.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Akinyele Cafe-Febrissy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-18 00:00:00.000000000 Z
11
+ date: 2022-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: psych
@@ -184,6 +184,7 @@ files:
184
184
  - lib/mangadex/api/user.rb
185
185
  - lib/mangadex/api/version_checker.rb
186
186
  - lib/mangadex/artist.rb
187
+ - lib/mangadex/at_home.rb
187
188
  - lib/mangadex/auth.rb
188
189
  - lib/mangadex/author.rb
189
190
  - lib/mangadex/chapter.rb
@@ -261,7 +262,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
261
262
  - !ruby/object:Gem::Version
262
263
  version: '0'
263
264
  requirements: []
264
- rubygems_version: 3.2.32
265
+ rubygems_version: 3.2.15
265
266
  signing_key:
266
267
  specification_version: 4
267
268
  summary: Your next favourite Ruby gem for interacting with Mangadex.org