MeUtils 2025.3.3.18.41.24__py3-none-any.whl → 2025.3.5.19.55.22__py3-none-any.whl

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.
Files changed (64) hide show
  1. {MeUtils-2025.3.3.18.41.24.dist-info → MeUtils-2025.3.5.19.55.22.dist-info}/METADATA +264 -264
  2. {MeUtils-2025.3.3.18.41.24.dist-info → MeUtils-2025.3.5.19.55.22.dist-info}/RECORD +61 -33
  3. examples/_openaisdk/open_router.py +2 -1
  4. examples/_openaisdk/openai_files.py +16 -5
  5. examples/_openaisdk/openai_images.py +1 -0
  6. examples/_openaisdk/openai_moon.py +22 -19
  7. examples/sh/__init__.py +11 -0
  8. meutils/apis/baidu/bdaitpzs.py +9 -17
  9. meutils/apis/chatglm/glm_video_api.py +2 -2
  10. meutils/apis/images/edits.py +7 -2
  11. meutils/apis/jimeng/common.py +1 -1
  12. meutils/apis/oneapi/common.py +4 -4
  13. meutils/apis/proxy/ips.py +2 -0
  14. meutils/caches/common.py +4 -0
  15. meutils/data/VERSION +1 -1
  16. meutils/data/oneapi/NOTICE.html +12 -0
  17. meutils/data/oneapi/__init__.py +1 -1
  18. meutils/data/oneapi/index.html +275 -0
  19. meutils/io/_openai_files.py +31 -0
  20. meutils/io/openai_files.py +138 -0
  21. meutils/io/parsers/__init__.py +10 -0
  22. meutils/io/parsers/fileparser/PDF/346/212/275/345/217/226.py +58 -0
  23. meutils/io/parsers/fileparser/__init__.py +11 -0
  24. meutils/io/parsers/fileparser/common.py +91 -0
  25. meutils/io/parsers/fileparser/demo.py +41 -0
  26. meutils/io/parsers/fileparser/filetype/__init__.py +10 -0
  27. meutils/io/parsers/fileparser/filetype/__main__.py +37 -0
  28. meutils/io/parsers/fileparser/filetype/filetype.py +98 -0
  29. meutils/io/parsers/fileparser/filetype/helpers.py +140 -0
  30. meutils/io/parsers/fileparser/filetype/match.py +155 -0
  31. meutils/io/parsers/fileparser/filetype/types/__init__.py +118 -0
  32. meutils/io/parsers/fileparser/filetype/types/application.py +22 -0
  33. meutils/io/parsers/fileparser/filetype/types/archive.py +687 -0
  34. meutils/io/parsers/fileparser/filetype/types/audio.py +212 -0
  35. meutils/io/parsers/fileparser/filetype/types/base.py +29 -0
  36. meutils/io/parsers/fileparser/filetype/types/document.py +256 -0
  37. meutils/io/parsers/fileparser/filetype/types/font.py +115 -0
  38. meutils/io/parsers/fileparser/filetype/types/image.py +383 -0
  39. meutils/io/parsers/fileparser/filetype/types/isobmff.py +33 -0
  40. meutils/io/parsers/fileparser/filetype/types/video.py +223 -0
  41. meutils/io/parsers/fileparser/filetype/utils.py +84 -0
  42. meutils/io/parsers/fileparser/filetype.py +41 -0
  43. meutils/io/parsers/fileparser/mineru.py +48 -0
  44. meutils/io/parsers/fileparser/pdf.py +30 -0
  45. meutils/io/parsers/fileparser//350/241/250/346/240/274/346/212/275/345/217/226.py +118 -0
  46. meutils/llm/check_utils.py +33 -2
  47. meutils/llm/clients.py +1 -0
  48. meutils/llm/completions/chat_gemini.py +72 -0
  49. meutils/llm/completions/chat_plus.py +78 -0
  50. meutils/llm/completions/{agents/file.py → chat_spark.py} +46 -26
  51. meutils/llm/completions/qwenllm.py +57 -16
  52. meutils/llm/completions/yuanbao.py +29 -3
  53. meutils/llm/openai_utils/common.py +2 -2
  54. meutils/schemas/oneapi/common.py +22 -19
  55. meutils/schemas/openai_types.py +65 -29
  56. meutils/schemas/yuanbao_types.py +6 -7
  57. meutils/types.py +2 -0
  58. meutils/data/oneapi/NOTICE.md +0 -1
  59. meutils/data/oneapi/_NOTICE.md +0 -140
  60. meutils/llm/completions/gemini.py +0 -69
  61. {MeUtils-2025.3.3.18.41.24.dist-info → MeUtils-2025.3.5.19.55.22.dist-info}/LICENSE +0 -0
  62. {MeUtils-2025.3.3.18.41.24.dist-info → MeUtils-2025.3.5.19.55.22.dist-info}/WHEEL +0 -0
  63. {MeUtils-2025.3.3.18.41.24.dist-info → MeUtils-2025.3.5.19.55.22.dist-info}/entry_points.txt +0 -0
  64. {MeUtils-2025.3.3.18.41.24.dist-info → MeUtils-2025.3.5.19.55.22.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,687 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from __future__ import absolute_import
4
+
5
+ import struct
6
+
7
+ from .base import Type
8
+
9
+
10
+ class Epub(Type):
11
+ """
12
+ Implements the EPUB archive type matcher.
13
+ """
14
+ MIME = 'application/epub+zip'
15
+ EXTENSION = 'epub'
16
+
17
+ def __init__(self):
18
+ super(Epub, self).__init__(
19
+ mime=Epub.MIME,
20
+ extension=Epub.EXTENSION
21
+ )
22
+
23
+ def match(self, buf):
24
+ return (len(buf) > 57 and
25
+ buf[0] == 0x50 and buf[1] == 0x4B and
26
+ buf[2] == 0x3 and buf[3] == 0x4 and
27
+ buf[30] == 0x6D and buf[31] == 0x69 and
28
+ buf[32] == 0x6D and buf[33] == 0x65 and
29
+ buf[34] == 0x74 and buf[35] == 0x79 and
30
+ buf[36] == 0x70 and buf[37] == 0x65 and
31
+ buf[38] == 0x61 and buf[39] == 0x70 and
32
+ buf[40] == 0x70 and buf[41] == 0x6C and
33
+ buf[42] == 0x69 and buf[43] == 0x63 and
34
+ buf[44] == 0x61 and buf[45] == 0x74 and
35
+ buf[46] == 0x69 and buf[47] == 0x6F and
36
+ buf[48] == 0x6E and buf[49] == 0x2F and
37
+ buf[50] == 0x65 and buf[51] == 0x70 and
38
+ buf[52] == 0x75 and buf[53] == 0x62 and
39
+ buf[54] == 0x2B and buf[55] == 0x7A and
40
+ buf[56] == 0x69 and buf[57] == 0x70)
41
+
42
+
43
+ class Zip(Type):
44
+ """
45
+ Implements the Zip archive type matcher.
46
+ """
47
+ MIME = 'application/zip'
48
+ EXTENSION = 'zip'
49
+
50
+ def __init__(self):
51
+ super(Zip, self).__init__(
52
+ mime=Zip.MIME,
53
+ extension=Zip.EXTENSION
54
+ )
55
+
56
+ def match(self, buf):
57
+ return (len(buf) > 3 and
58
+ buf[0] == 0x50 and buf[1] == 0x4B and
59
+ (buf[2] == 0x3 or buf[2] == 0x5 or
60
+ buf[2] == 0x7) and
61
+ (buf[3] == 0x4 or buf[3] == 0x6 or
62
+ buf[3] == 0x8))
63
+
64
+
65
+ class Tar(Type):
66
+ """
67
+ Implements the Tar archive type matcher.
68
+ """
69
+ MIME = 'application/x-tar'
70
+ EXTENSION = 'tar'
71
+
72
+ def __init__(self):
73
+ super(Tar, self).__init__(
74
+ mime=Tar.MIME,
75
+ extension=Tar.EXTENSION
76
+ )
77
+
78
+ def match(self, buf):
79
+ return (len(buf) > 261 and
80
+ buf[257] == 0x75 and
81
+ buf[258] == 0x73 and
82
+ buf[259] == 0x74 and
83
+ buf[260] == 0x61 and
84
+ buf[261] == 0x72)
85
+
86
+
87
+ class Rar(Type):
88
+ """
89
+ Implements the RAR archive type matcher.
90
+ """
91
+ MIME = 'application/x-rar-compressed'
92
+ EXTENSION = 'rar'
93
+
94
+ def __init__(self):
95
+ super(Rar, self).__init__(
96
+ mime=Rar.MIME,
97
+ extension=Rar.EXTENSION
98
+ )
99
+
100
+ def match(self, buf):
101
+ return (len(buf) > 6 and
102
+ buf[0] == 0x52 and
103
+ buf[1] == 0x61 and
104
+ buf[2] == 0x72 and
105
+ buf[3] == 0x21 and
106
+ buf[4] == 0x1A and
107
+ buf[5] == 0x7 and
108
+ (buf[6] == 0x0 or
109
+ buf[6] == 0x1))
110
+
111
+
112
+ class Gz(Type):
113
+ """
114
+ Implements the GZ archive type matcher.
115
+ """
116
+ MIME = 'application/gzip'
117
+ EXTENSION = 'gz'
118
+
119
+ def __init__(self):
120
+ super(Gz, self).__init__(
121
+ mime=Gz.MIME,
122
+ extension=Gz.EXTENSION
123
+ )
124
+
125
+ def match(self, buf):
126
+ return (len(buf) > 2 and
127
+ buf[0] == 0x1F and
128
+ buf[1] == 0x8B and
129
+ buf[2] == 0x8)
130
+
131
+
132
+ class Bz2(Type):
133
+ """
134
+ Implements the BZ2 archive type matcher.
135
+ """
136
+ MIME = 'application/x-bzip2'
137
+ EXTENSION = 'bz2'
138
+
139
+ def __init__(self):
140
+ super(Bz2, self).__init__(
141
+ mime=Bz2.MIME,
142
+ extension=Bz2.EXTENSION
143
+ )
144
+
145
+ def match(self, buf):
146
+ return (len(buf) > 2 and
147
+ buf[0] == 0x42 and
148
+ buf[1] == 0x5A and
149
+ buf[2] == 0x68)
150
+
151
+
152
+ class SevenZ(Type):
153
+ """
154
+ Implements the SevenZ (7z) archive type matcher.
155
+ """
156
+ MIME = 'application/x-7z-compressed'
157
+ EXTENSION = '7z'
158
+
159
+ def __init__(self):
160
+ super(SevenZ, self).__init__(
161
+ mime=SevenZ.MIME,
162
+ extension=SevenZ.EXTENSION
163
+ )
164
+
165
+ def match(self, buf):
166
+ return (len(buf) > 5 and
167
+ buf[0] == 0x37 and
168
+ buf[1] == 0x7A and
169
+ buf[2] == 0xBC and
170
+ buf[3] == 0xAF and
171
+ buf[4] == 0x27 and
172
+ buf[5] == 0x1C)
173
+
174
+
175
+ class Pdf(Type):
176
+ """
177
+ Implements the PDF archive type matcher.
178
+ """
179
+ MIME = 'application/pdf'
180
+ EXTENSION = 'pdf'
181
+
182
+ def __init__(self):
183
+ super(Pdf, self).__init__(
184
+ mime=Pdf.MIME,
185
+ extension=Pdf.EXTENSION
186
+ )
187
+
188
+ def match(self, buf):
189
+ # Detect BOM and skip first 3 bytes
190
+ if (len(buf) > 3 and
191
+ buf[0] == 0xEF and
192
+ buf[1] == 0xBB and
193
+ buf[2] == 0xBF): # noqa E129
194
+ buf = buf[3:]
195
+
196
+ return (len(buf) > 3 and
197
+ buf[0] == 0x25 and
198
+ buf[1] == 0x50 and
199
+ buf[2] == 0x44 and
200
+ buf[3] == 0x46)
201
+
202
+
203
+ class Exe(Type):
204
+ """
205
+ Implements the EXE archive type matcher.
206
+ """
207
+ MIME = 'application/x-msdownload'
208
+ EXTENSION = 'exe'
209
+
210
+ def __init__(self):
211
+ super(Exe, self).__init__(
212
+ mime=Exe.MIME,
213
+ extension=Exe.EXTENSION
214
+ )
215
+
216
+ def match(self, buf):
217
+ return (len(buf) > 1 and
218
+ buf[0] == 0x4D and
219
+ buf[1] == 0x5A)
220
+
221
+
222
+ class Swf(Type):
223
+ """
224
+ Implements the SWF archive type matcher.
225
+ """
226
+ MIME = 'application/x-shockwave-flash'
227
+ EXTENSION = 'swf'
228
+
229
+ def __init__(self):
230
+ super(Swf, self).__init__(
231
+ mime=Swf.MIME,
232
+ extension=Swf.EXTENSION
233
+ )
234
+
235
+ def match(self, buf):
236
+ return (len(buf) > 2 and
237
+ (buf[0] == 0x43 or
238
+ buf[0] == 0x46) and
239
+ buf[1] == 0x57 and
240
+ buf[2] == 0x53)
241
+
242
+
243
+ class Rtf(Type):
244
+ """
245
+ Implements the RTF archive type matcher.
246
+ """
247
+ MIME = 'application/rtf'
248
+ EXTENSION = 'rtf'
249
+
250
+ def __init__(self):
251
+ super(Rtf, self).__init__(
252
+ mime=Rtf.MIME,
253
+ extension=Rtf.EXTENSION
254
+ )
255
+
256
+ def match(self, buf):
257
+ return (len(buf) > 4 and
258
+ buf[0] == 0x7B and
259
+ buf[1] == 0x5C and
260
+ buf[2] == 0x72 and
261
+ buf[3] == 0x74 and
262
+ buf[4] == 0x66)
263
+
264
+
265
+ class Nes(Type):
266
+ """
267
+ Implements the NES archive type matcher.
268
+ """
269
+ MIME = 'application/x-nintendo-nes-rom'
270
+ EXTENSION = 'nes'
271
+
272
+ def __init__(self):
273
+ super(Nes, self).__init__(
274
+ mime=Nes.MIME,
275
+ extension=Nes.EXTENSION
276
+ )
277
+
278
+ def match(self, buf):
279
+ return (len(buf) > 3 and
280
+ buf[0] == 0x4E and
281
+ buf[1] == 0x45 and
282
+ buf[2] == 0x53 and
283
+ buf[3] == 0x1A)
284
+
285
+
286
+ class Crx(Type):
287
+ """
288
+ Implements the CRX archive type matcher.
289
+ """
290
+ MIME = 'application/x-google-chrome-extension'
291
+ EXTENSION = 'crx'
292
+
293
+ def __init__(self):
294
+ super(Crx, self).__init__(
295
+ mime=Crx.MIME,
296
+ extension=Crx.EXTENSION
297
+ )
298
+
299
+ def match(self, buf):
300
+ return (len(buf) > 3 and
301
+ buf[0] == 0x43 and
302
+ buf[1] == 0x72 and
303
+ buf[2] == 0x32 and
304
+ buf[3] == 0x34)
305
+
306
+
307
+ class Cab(Type):
308
+ """
309
+ Implements the CAB archive type matcher.
310
+ """
311
+ MIME = 'application/vnd.ms-cab-compressed'
312
+ EXTENSION = 'cab'
313
+
314
+ def __init__(self):
315
+ super(Cab, self).__init__(
316
+ mime=Cab.MIME,
317
+ extension=Cab.EXTENSION
318
+ )
319
+
320
+ def match(self, buf):
321
+ return (len(buf) > 3 and
322
+ ((buf[0] == 0x4D and
323
+ buf[1] == 0x53 and
324
+ buf[2] == 0x43 and
325
+ buf[3] == 0x46) or
326
+ (buf[0] == 0x49 and
327
+ buf[1] == 0x53 and
328
+ buf[2] == 0x63 and
329
+ buf[3] == 0x28)))
330
+
331
+
332
+ class Eot(Type):
333
+ """
334
+ Implements the EOT archive type matcher.
335
+ """
336
+ MIME = 'application/octet-stream'
337
+ EXTENSION = 'eot'
338
+
339
+ def __init__(self):
340
+ super(Eot, self).__init__(
341
+ mime=Eot.MIME,
342
+ extension=Eot.EXTENSION
343
+ )
344
+
345
+ def match(self, buf):
346
+ return (len(buf) > 35 and
347
+ buf[34] == 0x4C and
348
+ buf[35] == 0x50 and
349
+ ((buf[8] == 0x02 and
350
+ buf[9] == 0x00 and
351
+ buf[10] == 0x01) or
352
+ (buf[8] == 0x01 and
353
+ buf[9] == 0x00 and
354
+ buf[10] == 0x00) or
355
+ (buf[8] == 0x02 and
356
+ buf[9] == 0x00 and
357
+ buf[10] == 0x02)))
358
+
359
+
360
+ class Ps(Type):
361
+ """
362
+ Implements the PS archive type matcher.
363
+ """
364
+ MIME = 'application/postscript'
365
+ EXTENSION = 'ps'
366
+
367
+ def __init__(self):
368
+ super(Ps, self).__init__(
369
+ mime=Ps.MIME,
370
+ extension=Ps.EXTENSION
371
+ )
372
+
373
+ def match(self, buf):
374
+ return (len(buf) > 1 and
375
+ buf[0] == 0x25 and
376
+ buf[1] == 0x21)
377
+
378
+
379
+ class Xz(Type):
380
+ """
381
+ Implements the XS archive type matcher.
382
+ """
383
+ MIME = 'application/x-xz'
384
+ EXTENSION = 'xz'
385
+
386
+ def __init__(self):
387
+ super(Xz, self).__init__(
388
+ mime=Xz.MIME,
389
+ extension=Xz.EXTENSION
390
+ )
391
+
392
+ def match(self, buf):
393
+ return (len(buf) > 5 and
394
+ buf[0] == 0xFD and
395
+ buf[1] == 0x37 and
396
+ buf[2] == 0x7A and
397
+ buf[3] == 0x58 and
398
+ buf[4] == 0x5A and
399
+ buf[5] == 0x00)
400
+
401
+
402
+ class Sqlite(Type):
403
+ """
404
+ Implements the Sqlite DB archive type matcher.
405
+ """
406
+ MIME = 'application/x-sqlite3'
407
+ EXTENSION = 'sqlite'
408
+
409
+ def __init__(self):
410
+ super(Sqlite, self).__init__(
411
+ mime=Sqlite.MIME,
412
+ extension=Sqlite.EXTENSION
413
+ )
414
+
415
+ def match(self, buf):
416
+ return (len(buf) > 3 and
417
+ buf[0] == 0x53 and
418
+ buf[1] == 0x51 and
419
+ buf[2] == 0x4C and
420
+ buf[3] == 0x69)
421
+
422
+
423
+ class Deb(Type):
424
+ """
425
+ Implements the DEB archive type matcher.
426
+ """
427
+ MIME = 'application/x-deb'
428
+ EXTENSION = 'deb'
429
+
430
+ def __init__(self):
431
+ super(Deb, self).__init__(
432
+ mime=Deb.MIME,
433
+ extension=Deb.EXTENSION
434
+ )
435
+
436
+ def match(self, buf):
437
+ return (len(buf) > 20 and
438
+ buf[0] == 0x21 and
439
+ buf[1] == 0x3C and
440
+ buf[2] == 0x61 and
441
+ buf[3] == 0x72 and
442
+ buf[4] == 0x63 and
443
+ buf[5] == 0x68 and
444
+ buf[6] == 0x3E and
445
+ buf[7] == 0x0A and
446
+ buf[8] == 0x64 and
447
+ buf[9] == 0x65 and
448
+ buf[10] == 0x62 and
449
+ buf[11] == 0x69 and
450
+ buf[12] == 0x61 and
451
+ buf[13] == 0x6E and
452
+ buf[14] == 0x2D and
453
+ buf[15] == 0x62 and
454
+ buf[16] == 0x69 and
455
+ buf[17] == 0x6E and
456
+ buf[18] == 0x61 and
457
+ buf[19] == 0x72 and
458
+ buf[20] == 0x79)
459
+
460
+
461
+ class Ar(Type):
462
+ """
463
+ Implements the AR archive type matcher.
464
+ """
465
+ MIME = 'application/x-unix-archive'
466
+ EXTENSION = 'ar'
467
+
468
+ def __init__(self):
469
+ super(Ar, self).__init__(
470
+ mime=Ar.MIME,
471
+ extension=Ar.EXTENSION
472
+ )
473
+
474
+ def match(self, buf):
475
+ return (len(buf) > 6 and
476
+ buf[0] == 0x21 and
477
+ buf[1] == 0x3C and
478
+ buf[2] == 0x61 and
479
+ buf[3] == 0x72 and
480
+ buf[4] == 0x63 and
481
+ buf[5] == 0x68 and
482
+ buf[6] == 0x3E)
483
+
484
+
485
+ class Z(Type):
486
+ """
487
+ Implements the Z archive type matcher.
488
+ """
489
+ MIME = 'application/x-compress'
490
+ EXTENSION = 'Z'
491
+
492
+ def __init__(self):
493
+ super(Z, self).__init__(
494
+ mime=Z.MIME,
495
+ extension=Z.EXTENSION
496
+ )
497
+
498
+ def match(self, buf):
499
+ return (len(buf) > 1 and
500
+ ((buf[0] == 0x1F and
501
+ buf[1] == 0xA0) or
502
+ (buf[0] == 0x1F and
503
+ buf[1] == 0x9D)))
504
+
505
+
506
+ class Lzop(Type):
507
+ """
508
+ Implements the Lzop archive type matcher.
509
+ """
510
+ MIME = 'application/x-lzop'
511
+ EXTENSION = 'lzo'
512
+
513
+ def __init__(self):
514
+ super(Lzop, self).__init__(
515
+ mime=Lzop.MIME,
516
+ extension=Lzop.EXTENSION
517
+ )
518
+
519
+ def match(self, buf):
520
+ return (len(buf) > 7 and
521
+ buf[0] == 0x89 and
522
+ buf[1] == 0x4C and
523
+ buf[2] == 0x5A and
524
+ buf[3] == 0x4F and
525
+ buf[4] == 0x00 and
526
+ buf[5] == 0x0D and
527
+ buf[6] == 0x0A and
528
+ buf[7] == 0x1A)
529
+
530
+
531
+ class Lz(Type):
532
+ """
533
+ Implements the Lz archive type matcher.
534
+ """
535
+ MIME = 'application/x-lzip'
536
+ EXTENSION = 'lz'
537
+
538
+ def __init__(self):
539
+ super(Lz, self).__init__(
540
+ mime=Lz.MIME,
541
+ extension=Lz.EXTENSION
542
+ )
543
+
544
+ def match(self, buf):
545
+ return (len(buf) > 3 and
546
+ buf[0] == 0x4C and
547
+ buf[1] == 0x5A and
548
+ buf[2] == 0x49 and
549
+ buf[3] == 0x50)
550
+
551
+
552
+ class Elf(Type):
553
+ """
554
+ Implements the Elf archive type matcher
555
+ """
556
+ MIME = 'application/x-executable'
557
+ EXTENSION = 'elf'
558
+
559
+ def __init__(self):
560
+ super(Elf, self).__init__(
561
+ mime=Elf.MIME,
562
+ extension=Elf.EXTENSION
563
+ )
564
+
565
+ def match(self, buf):
566
+ return (len(buf) > 52 and
567
+ buf[0] == 0x7F and
568
+ buf[1] == 0x45 and
569
+ buf[2] == 0x4C and
570
+ buf[3] == 0x46)
571
+
572
+
573
+ class Lz4(Type):
574
+ """
575
+ Implements the Lz4 archive type matcher.
576
+ """
577
+ MIME = 'application/x-lz4'
578
+ EXTENSION = 'lz4'
579
+
580
+ def __init__(self):
581
+ super(Lz4, self).__init__(
582
+ mime=Lz4.MIME,
583
+ extension=Lz4.EXTENSION
584
+ )
585
+
586
+ def match(self, buf):
587
+ return (len(buf) > 3 and
588
+ buf[0] == 0x04 and
589
+ buf[1] == 0x22 and
590
+ buf[2] == 0x4D and
591
+ buf[3] == 0x18)
592
+
593
+
594
+ class Br(Type):
595
+ """Implements the Br image type matcher."""
596
+
597
+ MIME = 'application/x-brotli'
598
+ EXTENSION = 'br'
599
+
600
+ def __init__(self):
601
+ super(Br, self).__init__(
602
+ mime=Br.MIME,
603
+ extension=Br.EXTENSION
604
+ )
605
+
606
+ def match(self, buf):
607
+ return buf[:4] == bytearray([0xce, 0xb2, 0xcf, 0x81])
608
+
609
+
610
+ class Dcm(Type):
611
+ """Implements the Dcm image type matcher."""
612
+
613
+ MIME = 'application/dicom'
614
+ EXTENSION = 'dcm'
615
+
616
+ def __init__(self):
617
+ super(Dcm, self).__init__(
618
+ mime=Dcm.MIME,
619
+ extension=Dcm.EXTENSION
620
+ )
621
+
622
+ def match(self, buf):
623
+ return buf[128:131] == bytearray([0x44, 0x49, 0x43, 0x4d])
624
+
625
+
626
+ class Rpm(Type):
627
+ """Implements the Rpm image type matcher."""
628
+
629
+ MIME = 'application/x-rpm'
630
+ EXTENSION = 'rpm'
631
+
632
+ def __init__(self):
633
+ super(Rpm, self).__init__(
634
+ mime=Rpm.MIME,
635
+ extension=Rpm.EXTENSION
636
+ )
637
+
638
+ def match(self, buf):
639
+ return buf[:4] == bytearray([0xed, 0xab, 0xee, 0xdb])
640
+
641
+
642
+ class Zstd(Type):
643
+ """
644
+ Implements the Zstd archive type matcher.
645
+ https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md
646
+ """
647
+ MIME = 'application/zstd'
648
+ EXTENSION = 'zst'
649
+ MAGIC_SKIPPABLE_START = 0x184D2A50
650
+ MAGIC_SKIPPABLE_MASK = 0xFFFFFFF0
651
+
652
+ def __init__(self):
653
+ super(Zstd, self).__init__(
654
+ mime=Zstd.MIME,
655
+ extension=Zstd.EXTENSION
656
+ )
657
+
658
+ @staticmethod
659
+ def _to_little_endian_int(buf):
660
+ # return int.from_bytes(buf, byteorder='little')
661
+ return struct.unpack('<L', buf)[0]
662
+
663
+ def match(self, buf):
664
+ # Zstandard compressed data is made of one or more frames.
665
+ # There are two frame formats defined by Zstandard:
666
+ # Zstandard frames and Skippable frames.
667
+ # See more details from
668
+ # https://tools.ietf.org/id/draft-kucherawy-dispatch-zstd-00.html#rfc.section.2
669
+ is_zstd = (
670
+ len(buf) > 3 and
671
+ buf[0] in (0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28) and
672
+ buf[1] == 0xb5 and
673
+ buf[2] == 0x2f and
674
+ buf[3] == 0xfd)
675
+ if is_zstd:
676
+ return True
677
+ # skippable frames
678
+ if len(buf) < 8:
679
+ return False
680
+ magic = self._to_little_endian_int(buf[:4]) & Zstd.MAGIC_SKIPPABLE_MASK
681
+ if magic == Zstd.MAGIC_SKIPPABLE_START:
682
+ user_data_len = self._to_little_endian_int(buf[4:8])
683
+ if len(buf) < 8 + user_data_len:
684
+ return False
685
+ next_frame = buf[8 + user_data_len:]
686
+ return self.match(next_frame)
687
+ return False