mangadex 5.4.9 → 5.4.11

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: 498b3099b585f68a83983fd391d7b379ffa6b37ad41073d14c6b44d426691e5a
4
- data.tar.gz: ba5b94653c90682241b2d98c9652fa3614ae7c1fc9b6a735919cea17aa7ab0b4
3
+ metadata.gz: 29160bf116b71505a8b9697dd7c3940054e7f0b7a837c4f34470b5bff896879c
4
+ data.tar.gz: a245fdd04997fbc3ce6db61d2b3ab831d80df24db6431efc8a9a516a55111617
5
5
  SHA512:
6
- metadata.gz: 8afe63e8dcd7612a0f85b86a5f9b393f12401c6fdbc21118e490739b6ae5a77790f34f323daa32b9ef4bd9b70be35f7882266b284c2ca7a02acb97992f31c262
7
- data.tar.gz: 2cfa10681bad8af8a1968cc528e923d82d9808948d3f174111e65b1a8ffaf6792febc717d5c5cef311c83331c75c0508f9dfde2e6119621e94eb1a954d5aa836
6
+ metadata.gz: 0234ba9078a3927eecc7bf84acfde344612c4b4ae3ae0b8b30074daf320d874540bf2dfd4a4e82b7b23cfa534ed919271fa398ad22d0bdd8045ed6c2b2c5d1b6
7
+ data.tar.gz: 4ac033f5cfa89f851d2e8842b50b2bf487b5310c7c92391e0466bf6183a0b0fee1be1bb0b97765e6b8cf4597285edf8c84f7ee38f9892d7592d3cd4171180199
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
 
@@ -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
@@ -88,11 +88,9 @@ module Mangadex
88
88
  locale&.english_name
89
89
  end
90
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}"
91
+ sig { params(data_saver: T::Boolean).returns(T.nilable(T::Array[String])) }
92
+ def page_urls(data_saver: true)
93
+ Mangadex::AtHome.page_urls(id, data_saver: data_saver)
96
94
  end
97
95
 
98
96
  def as_json(*)
@@ -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
 
@@ -17,6 +17,7 @@ module Mangadex
17
17
  :focused_language,
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,7 +3,7 @@ module Mangadex
3
3
  module Version
4
4
  MAJOR = "5"
5
5
  MINOR = "4"
6
- TINY = "9"
6
+ TINY = "11"
7
7
  PATCH = nil
8
8
 
9
9
  STRING = [MAJOR, MINOR, TINY].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
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