or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-system.mdcommand-line-interface.mdconfiguration-management.mdcontent-entities.mdcore-download-api.mddownload-system.mdexception-handling.mdindex.mdplugin-system.mdtext-data-processing.md

client-system.mddocs/

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