0
# Client System
1
2
Multi-client architecture with web scraping and mobile API clients, automatic domain switching, retry mechanisms, and Cloudflare bypass capabilities. Provides robust access to JMComic content through multiple access methods.
3
4
## Types
5
6
```python { .api }
7
from typing import Dict, Any, List, Optional, Union, Callable, Iterator
8
```
9
10
## Capabilities
11
12
### Client Implementations
13
14
Core client implementations providing different access methods to JMComic content.
15
16
```python { .api }
17
class AbstractJmClient:
18
"""
19
Base client with retry logic, domain switching, and common functionality.
20
21
Attributes:
22
- domains: List[str] - List of available domains
23
- current_domain: str - Currently active domain
24
- retry_count: int - Number of retry attempts
25
- timeout: int - Request timeout in seconds
26
- headers: Dict[str, str] - HTTP headers for requests
27
28
Methods:
29
- switch_domain(): Switch to next available domain
30
- retry_request(func, *args, **kwargs): Retry request with domain switching
31
- get_current_url(path): Build URL with current domain
32
"""
33
34
class JmHtmlClient(AbstractJmClient):
35
"""
36
Web scraping client for HTML pages with Cloudflare bypass.
37
38
Provides access to JMComic through web scraping with automatic
39
Cloudflare anti-bot bypass mechanisms and HTML parsing.
40
41
Methods:
42
- get_album_detail(album_id): Get album details from web page
43
- get_photo_detail(photo_id): Get photo details from web page
44
- search_album(keyword, page=1): Search for albums
45
- get_category_page(category_id, page=1): Browse category pages
46
- login(username, password): Login with credentials
47
- get_favorites(page=1): Get user favorites
48
"""
49
50
def get_album_detail(self, album_id: Union[str, int]) -> 'JmAlbumDetail':
51
"""
52
Get album details by scraping the web page.
53
54
Parameters:
55
- album_id: str or int - Album ID
56
57
Returns:
58
JmAlbumDetail - Album with metadata and episode list
59
"""
60
61
def get_photo_detail(self, photo_id: Union[str, int]) -> 'JmPhotoDetail':
62
"""
63
Get photo details by scraping the web page.
64
65
Parameters:
66
- photo_id: str or int - Photo ID
67
68
Returns:
69
JmPhotoDetail - Photo with metadata and image list
70
"""
71
72
def search_album(self, keyword: str, page: int = 1) -> 'JmSearchPage':
73
"""
74
Search for albums using web interface.
75
76
Parameters:
77
- keyword: str - Search keyword
78
- page: int - Page number (default: 1)
79
80
Returns:
81
JmSearchPage - Search results with albums and pagination
82
"""
83
84
class JmApiClient(AbstractJmClient):
85
"""
86
Mobile API client with encryption/decryption support.
87
88
Provides access to JMComic mobile API with built-in encryption
89
handling and API response decoding.
90
91
Methods:
92
- get_album_detail(album_id): Get album via mobile API
93
- get_photo_detail(photo_id): Get photo via mobile API
94
- search_album(keyword, page=1): Search via mobile API
95
- login_api(username, password): API login
96
- get_favorites_api(page=1): Get favorites via API
97
"""
98
99
def get_album_detail(self, album_id: Union[str, int]) -> 'JmAlbumDetail':
100
"""
101
Get album details via mobile API.
102
103
Parameters:
104
- album_id: str or int - Album ID
105
106
Returns:
107
JmAlbumDetail - Album from API response
108
"""
109
110
def get_photo_detail(self, photo_id: Union[str, int]) -> 'JmPhotoDetail':
111
"""
112
Get photo details via mobile API.
113
114
Parameters:
115
- photo_id: str or int - Photo ID
116
117
Returns:
118
JmPhotoDetail - Photo from API response
119
"""
120
121
class PhotoConcurrentFetcherProxy:
122
"""
123
Concurrent photo fetching proxy for improved performance.
124
125
Wraps client instances to provide concurrent fetching of multiple
126
photos with thread pooling and error handling.
127
128
Attributes:
129
- client: JmcomicClient - Underlying client instance
130
- max_workers: int - Maximum concurrent workers
131
132
Methods:
133
- fetch_photos(photo_ids): Fetch multiple photos concurrently
134
- fetch_photos_from_album(album): Fetch all photos in album concurrently
135
"""
136
137
def __init__(self, client: 'JmcomicClient', max_workers: int = 4):
138
"""
139
Initialize concurrent fetcher proxy.
140
141
Parameters:
142
- client: JmcomicClient - Client to wrap
143
- max_workers: int - Maximum concurrent workers
144
"""
145
146
def fetch_photos(self, photo_ids: List[Union[str, int]]) -> List['JmPhotoDetail']:
147
"""
148
Fetch multiple photos concurrently.
149
150
Parameters:
151
- photo_ids: list - List of photo IDs
152
153
Returns:
154
List[JmPhotoDetail] - List of fetched photos
155
"""
156
157
def fetch_photos_from_album(self, album: 'JmAlbumDetail') -> List['JmPhotoDetail']:
158
"""
159
Fetch all photos from an album concurrently.
160
161
Parameters:
162
- album: JmAlbumDetail - Album containing photos to fetch
163
164
Returns:
165
List[JmPhotoDetail] - List of fetched photos
166
"""
167
```
168
169
### Client Interfaces
170
171
Interface definitions that combine multiple client capabilities into unified APIs.
172
173
```python { .api }
174
class JmDetailClient:
175
"""
176
Interface for album and photo detail fetching operations.
177
178
Methods:
179
- get_album_detail(album_id): Get detailed album information
180
- get_photo_detail(photo_id): Get detailed photo information
181
"""
182
183
class JmUserClient:
184
"""
185
Interface for user operations including login and favorites.
186
187
Methods:
188
- login(username, password): User authentication
189
- get_favorites(page=1): Get user's favorite albums
190
- add_favorite(album_id): Add album to favorites
191
- remove_favorite(album_id): Remove album from favorites
192
"""
193
194
class JmImageClient:
195
"""
196
Interface for image downloading and processing operations.
197
198
Methods:
199
- download_image(image_detail): Download individual image
200
- get_image_bytes(image_url): Get image data as bytes
201
- process_image(image_data): Process/decrypt image data
202
"""
203
204
class JmSearchAlbumClient:
205
"""
206
Interface for search functionality and filtering.
207
208
Methods:
209
- search_album(keyword, page=1): Search for albums
210
- search_by_tag(tag, page=1): Search albums by tag
211
- search_by_author(author, page=1): Search albums by author
212
"""
213
214
class JmCategoryClient:
215
"""
216
Interface for category browsing and navigation.
217
218
Methods:
219
- get_category_page(category_id, page=1): Browse category
220
- get_categories(): Get available categories
221
- get_popular_albums(page=1): Get popular albums
222
"""
223
224
class JmcomicClient(JmDetailClient, JmUserClient, JmImageClient,
225
JmSearchAlbumClient, JmCategoryClient):
226
"""
227
Main client interface combining all capabilities.
228
229
Provides unified access to all JMComic functionality through
230
a single interface that combines all specialized client interfaces.
231
"""
232
```
233
234
### Response Wrappers
235
236
Classes that wrap and process HTTP responses from different client types.
237
238
```python { .api }
239
class JmResp:
240
"""
241
Base HTTP response wrapper with error handling.
242
243
Attributes:
244
- status_code: int - HTTP status code
245
- content: bytes - Response content
246
- text: str - Response text
247
- headers: Dict[str, str] - Response headers
248
- url: str - Request URL
249
250
Methods:
251
- json(): Parse response as JSON
252
- raise_for_status(): Raise exception for HTTP errors
253
- is_success(): Check if response is successful
254
"""
255
256
def json(self) -> Dict[str, Any]:
257
"""
258
Parse response content as JSON.
259
260
Returns:
261
dict - Parsed JSON data
262
263
Raises:
264
JsonResolveFailException - If JSON parsing fails
265
"""
266
267
def raise_for_status(self):
268
"""
269
Raise exception for HTTP error status codes.
270
271
Raises:
272
ResponseUnexpectedException - For HTTP errors
273
"""
274
275
class JmImageResp(JmResp):
276
"""
277
Image response with transfer and processing capabilities.
278
279
Additional Methods:
280
- save_to_file(filepath): Save image to file
281
- get_image_data(): Get processed image data
282
- decrypt_if_needed(): Decrypt scrambled images
283
"""
284
285
def save_to_file(self, filepath: str) -> bool:
286
"""
287
Save image response to file.
288
289
Parameters:
290
- filepath: str - Target file path
291
292
Returns:
293
bool - True if save successful
294
"""
295
296
def get_image_data(self) -> bytes:
297
"""
298
Get processed image data (decrypted if needed).
299
300
Returns:
301
bytes - Image data ready for use
302
"""
303
304
class JmJsonResp(JmResp):
305
"""
306
JSON response parser and validator.
307
308
Additional Methods:
309
- validate_structure(): Validate JSON structure
310
- extract_data(path): Extract data using JSONPath
311
"""
312
313
class JmApiResp(JmResp):
314
"""
315
Encrypted API response decoder for mobile API.
316
317
Additional Methods:
318
- decrypt_response(): Decrypt encrypted API response
319
- parse_api_data(): Parse decrypted API data
320
"""
321
322
class JmAlbumCommentResp(JmResp):
323
"""
324
Album comment submission response handler.
325
326
Additional Methods:
327
- get_comment_id(): Get submitted comment ID
328
- is_comment_accepted(): Check if comment was accepted
329
"""
330
```
331
332
Usage examples:
333
334
```python
335
# Create and use HTML client
336
html_client = JmHtmlClient()
337
album = html_client.get_album_detail("123456")
338
search_results = html_client.search_album("keyword")
339
340
# Create and use API client
341
api_client = JmApiClient()
342
album = api_client.get_album_detail("123456")
343
344
# Use concurrent fetcher
345
concurrent_fetcher = PhotoConcurrentFetcherProxy(html_client, max_workers=8)
346
photos = concurrent_fetcher.fetch_photos(["456789", "012345", "678901"])
347
348
# Handle responses
349
try:
350
response = html_client.get_raw_response("/some/path")
351
response.raise_for_status()
352
data = response.json()
353
except ResponseUnexpectedException as e:
354
print(f"HTTP error: {e}")
355
except JsonResolveFailException as e:
356
print(f"JSON parsing error: {e}")
357
358
# Process image response
359
image_response = html_client.download_image_response(image_detail)
360
image_response.decrypt_if_needed()
361
image_response.save_to_file("image.jpg")
362
```
363
364
## Client Selection and Configuration
365
366
The client system automatically selects the best client based on configuration and availability:
367
368
```python
369
# Configure client preferences in JmOption
370
option = JmOption.default()
371
option.client['preferred_client'] = 'html' # or 'api'
372
option.client['fallback_enabled'] = True
373
option.client['retry_count'] = 3
374
375
# Client will automatically switch between HTML and API clients
376
# based on availability and success rates
377
downloader = JmDownloader(option)
378
album = downloader.download_album("123456")
379
```
380
381
## Error Handling and Retry Logic
382
383
All clients implement comprehensive error handling:
384
385
- **Domain Switching**: Automatically tries different domains on failure
386
- **Client Fallback**: Falls back from API to HTML client if needed
387
- **Retry Logic**: Configurable retry attempts with exponential backoff
388
- **Cloudflare Handling**: Automatic detection and bypass of anti-bot measures