q6 commited on
Commit
a0d7a22
·
1 Parent(s): 8a05955
Files changed (1) hide show
  1. Client/Scripts/test-short.py +0 -527
Client/Scripts/test-short.py DELETED
@@ -1,527 +0,0 @@
1
- import gzip
2
- import json
3
- import os
4
- import zlib
5
-
6
- import requests
7
-
8
-
9
- MAGIC = b"stealth_pngcomp"
10
- PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n"
11
- USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0"
12
- REQUEST_TIMEOUT = 45
13
- LONG = False
14
- POST_IDS = [
15
- "139395291",
16
- ]
17
-
18
-
19
- def read_dotenv_value(path, key):
20
- try:
21
- with open(path, "r") as env_file:
22
- for line in env_file:
23
- line = line.strip()
24
- if not line or line.startswith("#") or "=" not in line:
25
- continue
26
- k, v = line.split("=", 1)
27
- if k == key:
28
- return v
29
- except FileNotFoundError:
30
- return None
31
- return None
32
-
33
-
34
- def get_phpsessid():
35
- phpsessid = os.getenv("PHPSESSID")
36
- if phpsessid:
37
- return phpsessid
38
- script_dir = os.path.dirname(os.path.abspath(__file__))
39
- env_candidates = [
40
- os.path.join(script_dir, "..", ".env"),
41
- os.path.join(script_dir, "..", "..", ".env"),
42
- ]
43
- for env_path in env_candidates:
44
- phpsessid = read_dotenv_value(os.path.abspath(env_path), "PHPSESSID")
45
- if phpsessid:
46
- return phpsessid
47
- raise RuntimeError("PHPSESSID is not set in the environment or .env")
48
-
49
-
50
- def build_session(phpsessid):
51
- session = requests.Session()
52
- session.headers.update({"User-Agent": USER_AGENT, "Referer": "https://www.pixiv.net/"})
53
- session.cookies.update({"PHPSESSID": phpsessid})
54
- return session
55
-
56
-
57
- def fetch_post_pages(session, post_id):
58
- url = f"https://www.pixiv.net/ajax/illust/{post_id}/pages"
59
- headers = {"Referer": f"https://www.pixiv.net/artworks/{post_id}"}
60
- response = session.get(url, headers=headers, timeout=REQUEST_TIMEOUT)
61
- response.raise_for_status()
62
- data = response.json()
63
- return data.get("body") or []
64
-
65
-
66
- def read_exact(stream, size):
67
- chunks = []
68
- remaining = size
69
- while remaining > 0:
70
- data = stream.read(remaining)
71
- if not data:
72
- raise EOFError("Unexpected end of stream.")
73
- chunks.append(data)
74
- remaining -= len(data)
75
- return b"".join(chunks)
76
-
77
-
78
- def paeth_predictor(a, b, c):
79
- p = a + b - c
80
- pa = abs(p - a)
81
- pb = abs(p - b)
82
- pc = abs(p - c)
83
- if pa <= pb and pa <= pc:
84
- return a
85
- if pb <= pc:
86
- return b
87
- return c
88
-
89
-
90
- def unfilter_row(filter_type, raw, prev, bpp):
91
- raw = memoryview(raw)
92
- row = bytearray(len(raw))
93
- if filter_type == 0:
94
- row[:] = raw
95
- return row
96
- if filter_type == 1:
97
- for i in range(len(raw)):
98
- left = row[i - bpp] if i >= bpp else 0
99
- row[i] = (raw[i] + left) & 0xFF
100
- return row
101
- if filter_type == 2:
102
- for i in range(len(raw)):
103
- row[i] = (raw[i] + prev[i]) & 0xFF
104
- return row
105
- if filter_type == 3:
106
- for i in range(len(raw)):
107
- left = row[i - bpp] if i >= bpp else 0
108
- up = prev[i]
109
- row[i] = (raw[i] + ((left + up) // 2)) & 0xFF
110
- return row
111
- if filter_type == 4:
112
- for i in range(len(raw)):
113
- left = row[i - bpp] if i >= bpp else 0
114
- up = prev[i]
115
- up_left = prev[i - bpp] if i >= bpp else 0
116
- row[i] = (raw[i] + paeth_predictor(left, up, up_left)) & 0xFF
117
- return row
118
- raise ValueError(f"Unsupported PNG filter type: {filter_type}")
119
-
120
-
121
- def parse_ihdr(data):
122
- if len(data) != 13:
123
- raise ValueError("Invalid IHDR chunk length.")
124
- width = int.from_bytes(data[0:4], "big")
125
- height = int.from_bytes(data[4:8], "big")
126
- bit_depth = data[8]
127
- color_type = data[9]
128
- compression = data[10]
129
- filter_method = data[11]
130
- interlace = data[12]
131
- return width, height, bit_depth, color_type, compression, filter_method, interlace
132
-
133
-
134
- def stream_magic_and_length(session, url, referer):
135
- headers = {
136
- "Referer": referer,
137
- "User-Agent": USER_AGENT,
138
- "Accept-Encoding": "identity",
139
- }
140
- required_bytes = len(MAGIC) + 4
141
- required_bits = required_bytes * 8
142
- with session.get(url, headers=headers, stream=True, timeout=REQUEST_TIMEOUT) as response:
143
- response.raise_for_status()
144
- response.raw.decode_content = False
145
- stream = response.raw
146
-
147
- signature = read_exact(stream, len(PNG_SIGNATURE))
148
- if signature != PNG_SIGNATURE:
149
- raise ValueError("Not a PNG file.")
150
-
151
- width = height = None
152
- row_bytes = None
153
- bpp = None
154
- rows_needed = None
155
- rows_processed = 0
156
- prev_row = None
157
- scanline_buf = bytearray()
158
- out_bytes = bytearray()
159
- current_byte = 0
160
- bits_in_current = 0
161
- decompressor = zlib.decompressobj()
162
-
163
- while True:
164
- length_bytes = read_exact(stream, 4)
165
- chunk_length = int.from_bytes(length_bytes, "big")
166
- chunk_type = read_exact(stream, 4)
167
-
168
- if chunk_type == b"IHDR":
169
- ihdr = read_exact(stream, chunk_length)
170
- width, height, bit_depth, color_type, compression, filter_method, interlace = parse_ihdr(ihdr)
171
- if compression != 0 or filter_method != 0 or interlace != 0:
172
- raise ValueError("Unsupported PNG compression/filter/interlace.")
173
- if bit_depth != 8:
174
- raise ValueError("Unsupported PNG bit depth.")
175
- if color_type not in (4, 6):
176
- raise ValueError("PNG does not have an alpha channel.")
177
- if width <= 0 or height <= 0:
178
- raise ValueError("Invalid PNG dimensions.")
179
- channels = 2 if color_type == 4 else 4
180
- bpp = channels
181
- row_bytes = width * bpp
182
- rows_needed = (required_bits + width - 1) // width
183
- if rows_needed > height:
184
- raise ValueError("PNG too short to read header bits.")
185
- prev_row = bytearray(row_bytes)
186
- read_exact(stream, 4)
187
- continue
188
-
189
- if chunk_type == b"IDAT":
190
- if row_bytes is None:
191
- raise ValueError("IDAT before IHDR.")
192
- remaining = chunk_length
193
- while remaining > 0:
194
- chunk = read_exact(stream, min(16384, remaining))
195
- remaining -= len(chunk)
196
- output = decompressor.decompress(chunk)
197
- if not output:
198
- continue
199
- scanline_buf.extend(output)
200
-
201
- while rows_processed < rows_needed and len(scanline_buf) >= row_bytes + 1:
202
- filter_type = scanline_buf[0]
203
- raw_row = scanline_buf[1:1 + row_bytes]
204
- del scanline_buf[:1 + row_bytes]
205
- row = unfilter_row(filter_type, raw_row, prev_row, bpp)
206
- prev_row = row
207
- rows_processed += 1
208
-
209
- alpha_offset = bpp - 1
210
- for i in range(alpha_offset, row_bytes, bpp):
211
- bit = row[i] & 1
212
- current_byte = (current_byte << 1) | bit
213
- bits_in_current += 1
214
- if bits_in_current == 8:
215
- out_bytes.append(current_byte)
216
- bits_in_current = 0
217
- current_byte = 0
218
- if len(out_bytes) >= required_bytes:
219
- return {
220
- "magic": bytes(out_bytes[: len(MAGIC)]),
221
- "length_bits": int.from_bytes(
222
- out_bytes[len(MAGIC):required_bytes],
223
- "big",
224
- ),
225
- "width": width,
226
- "height": height,
227
- "rows_needed": rows_needed,
228
- "rows_processed": rows_processed,
229
- }
230
- if rows_processed >= rows_needed and len(out_bytes) >= required_bytes:
231
- return {
232
- "magic": bytes(out_bytes[: len(MAGIC)]),
233
- "length_bits": int.from_bytes(
234
- out_bytes[len(MAGIC):required_bytes],
235
- "big",
236
- ),
237
- "width": width,
238
- "height": height,
239
- "rows_needed": rows_needed,
240
- "rows_processed": rows_processed,
241
- }
242
-
243
- read_exact(stream, 4)
244
- continue
245
-
246
- read_exact(stream, chunk_length)
247
- read_exact(stream, 4)
248
- if chunk_type == b"IEND":
249
- break
250
-
251
- raise ValueError("Not enough PNG data to read header bits.")
252
-
253
-
254
- def stream_magic_length_and_payload(session, url, referer):
255
- headers = {
256
- "Referer": referer,
257
- "User-Agent": USER_AGENT,
258
- "Accept-Encoding": "identity",
259
- }
260
- required_bytes = len(MAGIC) + 4
261
- required_bits = required_bytes * 8
262
- with session.get(url, headers=headers, stream=True, timeout=REQUEST_TIMEOUT) as response:
263
- response.raise_for_status()
264
- response.raw.decode_content = False
265
- stream = response.raw
266
-
267
- signature = read_exact(stream, len(PNG_SIGNATURE))
268
- if signature != PNG_SIGNATURE:
269
- raise ValueError("Not a PNG file.")
270
-
271
- width = height = None
272
- row_bytes = None
273
- bpp = None
274
- rows_needed_header = None
275
- rows_needed_total = None
276
- rows_processed = 0
277
- prev_row = None
278
- scanline_buf = bytearray()
279
- out_bytes = bytearray()
280
- current_byte = 0
281
- bits_in_current = 0
282
- decompressor = zlib.decompressobj()
283
- length_bits = None
284
- total_bytes = None
285
-
286
- while True:
287
- length_bytes = read_exact(stream, 4)
288
- chunk_length = int.from_bytes(length_bytes, "big")
289
- chunk_type = read_exact(stream, 4)
290
-
291
- if chunk_type == b"IHDR":
292
- ihdr = read_exact(stream, chunk_length)
293
- width, height, bit_depth, color_type, compression, filter_method, interlace = parse_ihdr(ihdr)
294
- if compression != 0 or filter_method != 0 or interlace != 0:
295
- raise ValueError("Unsupported PNG compression/filter/interlace.")
296
- if bit_depth != 8:
297
- raise ValueError("Unsupported PNG bit depth.")
298
- if color_type not in (4, 6):
299
- raise ValueError("PNG does not have an alpha channel.")
300
- if width <= 0 or height <= 0:
301
- raise ValueError("Invalid PNG dimensions.")
302
- channels = 2 if color_type == 4 else 4
303
- bpp = channels
304
- row_bytes = width * bpp
305
- rows_needed_header = (required_bits + width - 1) // width
306
- if rows_needed_header > height:
307
- raise ValueError("PNG too short to read header bits.")
308
- prev_row = bytearray(row_bytes)
309
- read_exact(stream, 4)
310
- continue
311
-
312
- if chunk_type == b"IDAT":
313
- if row_bytes is None:
314
- raise ValueError("IDAT before IHDR.")
315
- remaining = chunk_length
316
- while remaining > 0:
317
- chunk = read_exact(stream, min(16384, remaining))
318
- remaining -= len(chunk)
319
- output = decompressor.decompress(chunk)
320
- if output:
321
- scanline_buf.extend(output)
322
-
323
- while len(scanline_buf) >= row_bytes + 1:
324
- filter_type = scanline_buf[0]
325
- raw_row = scanline_buf[1:1 + row_bytes]
326
- del scanline_buf[:1 + row_bytes]
327
- row = unfilter_row(filter_type, raw_row, prev_row, bpp)
328
- prev_row = row
329
- rows_processed += 1
330
-
331
- alpha_offset = bpp - 1
332
- for i in range(alpha_offset, row_bytes, bpp):
333
- bit = row[i] & 1
334
- current_byte = (current_byte << 1) | bit
335
- bits_in_current += 1
336
- if bits_in_current == 8:
337
- out_bytes.append(current_byte)
338
- bits_in_current = 0
339
- current_byte = 0
340
-
341
- if total_bytes is None and len(out_bytes) >= required_bytes:
342
- magic = bytes(out_bytes[: len(MAGIC)])
343
- length_bits = int.from_bytes(
344
- out_bytes[len(MAGIC):required_bytes],
345
- "big",
346
- )
347
- if magic != MAGIC:
348
- return {
349
- "magic": magic,
350
- "length_bits": length_bits,
351
- "width": width,
352
- "height": height,
353
- "rows_needed_header": rows_needed_header,
354
- "rows_needed_total": None,
355
- "rows_processed": rows_processed,
356
- "payload": None,
357
- }
358
- payload_len = length_bits // 8
359
- total_bytes = required_bytes + payload_len
360
- rows_needed_total = (total_bytes * 8 + width - 1) // width
361
- if rows_needed_total > height:
362
- raise ValueError("PNG too short to read payload bits.")
363
- if len(out_bytes) >= total_bytes:
364
- return {
365
- "magic": magic,
366
- "length_bits": length_bits,
367
- "width": width,
368
- "height": height,
369
- "rows_needed_header": rows_needed_header,
370
- "rows_needed_total": rows_needed_total,
371
- "rows_processed": rows_processed,
372
- "payload": bytes(out_bytes[required_bytes:total_bytes]),
373
- }
374
-
375
- if total_bytes is not None and len(out_bytes) >= total_bytes:
376
- return {
377
- "magic": bytes(out_bytes[: len(MAGIC)]),
378
- "length_bits": length_bits,
379
- "width": width,
380
- "height": height,
381
- "rows_needed_header": rows_needed_header,
382
- "rows_needed_total": rows_needed_total,
383
- "rows_processed": rows_processed,
384
- "payload": bytes(out_bytes[required_bytes:total_bytes]),
385
- }
386
-
387
- read_exact(stream, 4)
388
- continue
389
-
390
- read_exact(stream, chunk_length)
391
- read_exact(stream, 4)
392
- if chunk_type == b"IEND":
393
- break
394
-
395
- raise ValueError("Not enough PNG data to read payload bits.")
396
-
397
-
398
- def decode_payload(payload):
399
- try:
400
- json_bytes = gzip.decompress(payload)
401
- except Exception:
402
- return payload.decode("utf-8", errors="replace"), "raw"
403
- try:
404
- data = json.loads(json_bytes.decode("utf-8"))
405
- except json.JSONDecodeError:
406
- return json_bytes.decode("utf-8", errors="replace"), "text"
407
- if "Comment" in data and isinstance(data["Comment"], str):
408
- try:
409
- data["Comment"] = json.loads(data["Comment"])
410
- except json.JSONDecodeError:
411
- pass
412
- return data, "json"
413
-
414
-
415
- def read_post_ids(path):
416
- with open(path, "r") as handle:
417
- return [line.strip() for line in handle if line.strip()]
418
-
419
-
420
- def load_post_ids(args):
421
- if args:
422
- if len(args) == 1 and os.path.isfile(args[0]):
423
- return read_post_ids(args[0])
424
- return [arg for arg in args if arg.isdigit()]
425
- default_path = os.path.join(
426
- os.path.dirname(os.path.abspath(__file__)),
427
- "..",
428
- "txt logs",
429
- "test.txt",
430
- )
431
- if os.path.exists(default_path):
432
- return read_post_ids(default_path)
433
- return []
434
-
435
-
436
- def main() -> int:
437
- try:
438
- phpsessid = get_phpsessid()
439
- except Exception as exc:
440
- print(f"Failed to load PHPSESSID: {exc}")
441
- return 1
442
-
443
- post_ids = list(POST_IDS)
444
- if not post_ids:
445
- print("No post IDs provided.")
446
- return 1
447
-
448
- session = build_session(phpsessid)
449
-
450
- for post_id in post_ids:
451
- print(f"Post {post_id}")
452
- try:
453
- pages = fetch_post_pages(session, post_id)
454
- except Exception as exc:
455
- print(f" Failed to fetch pages: {exc}")
456
- continue
457
-
458
- png_pages = []
459
- for idx, page in enumerate(pages):
460
- original = page.get("urls", {}).get("original")
461
- if original and original.lower().endswith(".png"):
462
- png_pages.append((idx + 1, original))
463
-
464
- if not png_pages:
465
- print(" No PNG pages found.")
466
- continue
467
-
468
- for page_index, url in png_pages:
469
- referer = f"https://www.pixiv.net/artworks/{post_id}"
470
- if LONG:
471
- try:
472
- info = stream_magic_length_and_payload(session, url, referer)
473
- except Exception as exc:
474
- print(f" Page {page_index}: error {exc}")
475
- continue
476
-
477
- magic = info["magic"]
478
- if magic != MAGIC:
479
- magic_ascii = magic.decode("ascii", errors="replace")
480
- print(
481
- f" Page {page_index}: no stealth, "
482
- f"magic={magic_ascii!r} hex={magic.hex()}"
483
- )
484
- continue
485
-
486
- length_bits = info["length_bits"]
487
- length_bytes = length_bits // 8 if length_bits is not None else None
488
- print(
489
- f" Page {page_index}: magic ok, "
490
- f"len_bits={length_bits}, len_bytes={length_bytes}, "
491
- f"width={info['width']}, rows={info['rows_needed_total']}"
492
- )
493
- payload = info["payload"] or b""
494
- data, data_type = decode_payload(payload)
495
- if data_type == "json":
496
- print(json.dumps(data, ensure_ascii=True, sort_keys=True, indent=2))
497
- else:
498
- print(data)
499
- else:
500
- try:
501
- info = stream_magic_and_length(session, url, referer)
502
- except Exception as exc:
503
- print(f" Page {page_index}: error {exc}")
504
- continue
505
-
506
- magic = info["magic"]
507
- if magic == MAGIC:
508
- length_bits = info["length_bits"]
509
- length_bytes = length_bits // 8 if length_bits is not None else None
510
- print(
511
- f" Page {page_index}: magic ok, "
512
- f"len_bits={length_bits}, len_bytes={length_bytes}, "
513
- f"width={info['width']}, rows={info['rows_needed']}"
514
- )
515
- else:
516
- magic_ascii = magic.decode("ascii", errors="replace")
517
- print(
518
- f" Page {page_index}: magic mismatch "
519
- f"ascii={magic_ascii!r} hex={magic.hex()}, "
520
- f"width={info['width']}, rows={info['rows_needed']}"
521
- )
522
-
523
- return 0
524
-
525
-
526
- if __name__ == "__main__":
527
- raise SystemExit(main())