aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/filetype/types
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/filetype/types
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/filetype/types')
-rw-r--r--.venv/lib/python3.12/site-packages/filetype/types/__init__.py118
-rw-r--r--.venv/lib/python3.12/site-packages/filetype/types/application.py22
-rw-r--r--.venv/lib/python3.12/site-packages/filetype/types/archive.py687
-rw-r--r--.venv/lib/python3.12/site-packages/filetype/types/audio.py212
-rw-r--r--.venv/lib/python3.12/site-packages/filetype/types/base.py29
-rw-r--r--.venv/lib/python3.12/site-packages/filetype/types/document.py256
-rw-r--r--.venv/lib/python3.12/site-packages/filetype/types/font.py115
-rw-r--r--.venv/lib/python3.12/site-packages/filetype/types/image.py383
-rw-r--r--.venv/lib/python3.12/site-packages/filetype/types/isobmff.py33
-rw-r--r--.venv/lib/python3.12/site-packages/filetype/types/video.py223
10 files changed, 2078 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/filetype/types/__init__.py b/.venv/lib/python3.12/site-packages/filetype/types/__init__.py
new file mode 100644
index 00000000..7d4daed0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/filetype/types/__init__.py
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+from . import archive
+from . import audio
+from . import application
+from . import document
+from . import font
+from . import image
+from . import video
+from .base import Type # noqa
+
+# Supported image types
+IMAGE = (
+ image.Dwg(),
+ image.Xcf(),
+ image.Jpeg(),
+ image.Jpx(),
+ image.Apng(),
+ image.Png(),
+ image.Gif(),
+ image.Webp(),
+ image.Tiff(),
+ image.Cr2(),
+ image.Bmp(),
+ image.Jxr(),
+ image.Psd(),
+ image.Ico(),
+ image.Heic(),
+ image.Dcm(),
+ image.Avif(),
+)
+
+# Supported video types
+VIDEO = (
+ video.M3gp(),
+ video.Mp4(),
+ video.M4v(),
+ video.Mkv(),
+ video.Mov(),
+ video.Avi(),
+ video.Wmv(),
+ video.Mpeg(),
+ video.Webm(),
+ video.Flv(),
+)
+
+# Supported audio types
+AUDIO = (
+ audio.Aac(),
+ audio.Midi(),
+ audio.Mp3(),
+ audio.M4a(),
+ audio.Ogg(),
+ audio.Flac(),
+ audio.Wav(),
+ audio.Amr(),
+ audio.Aiff(),
+)
+
+# Supported font types
+FONT = (font.Woff(), font.Woff2(), font.Ttf(), font.Otf())
+
+# Supported archive container types
+ARCHIVE = (
+ archive.Br(),
+ archive.Rpm(),
+ archive.Dcm(),
+ archive.Epub(),
+ archive.Zip(),
+ archive.Tar(),
+ archive.Rar(),
+ archive.Gz(),
+ archive.Bz2(),
+ archive.SevenZ(),
+ archive.Pdf(),
+ archive.Exe(),
+ archive.Swf(),
+ archive.Rtf(),
+ archive.Nes(),
+ archive.Crx(),
+ archive.Cab(),
+ archive.Eot(),
+ archive.Ps(),
+ archive.Xz(),
+ archive.Sqlite(),
+ archive.Deb(),
+ archive.Ar(),
+ archive.Z(),
+ archive.Lzop(),
+ archive.Lz(),
+ archive.Elf(),
+ archive.Lz4(),
+ archive.Zstd(),
+)
+
+# Supported archive container types
+APPLICATION = (
+ application.Wasm(),
+)
+
+# Supported document types
+DOCUMENT = (
+ document.Doc(),
+ document.Docx(),
+ document.Odt(),
+ document.Xls(),
+ document.Xlsx(),
+ document.Ods(),
+ document.Ppt(),
+ document.Pptx(),
+ document.Odp(),
+)
+
+
+# Expose supported type matchers
+TYPES = list(IMAGE + AUDIO + VIDEO + FONT + DOCUMENT + ARCHIVE + APPLICATION)
diff --git a/.venv/lib/python3.12/site-packages/filetype/types/application.py b/.venv/lib/python3.12/site-packages/filetype/types/application.py
new file mode 100644
index 00000000..6f02370c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/filetype/types/application.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+from .base import Type
+
+
+class Wasm(Type):
+ """Implements the Wasm image type matcher."""
+
+ MIME = 'application/wasm'
+ EXTENSION = 'wasm'
+
+ def __init__(self):
+ super(Wasm, self).__init__(
+ mime=Wasm.MIME,
+ extension=Wasm.EXTENSION
+ )
+
+ def match(self, buf):
+ return buf[:8] == bytearray([0x00, 0x61, 0x73, 0x6d,
+ 0x01, 0x00, 0x00, 0x00])
diff --git a/.venv/lib/python3.12/site-packages/filetype/types/archive.py b/.venv/lib/python3.12/site-packages/filetype/types/archive.py
new file mode 100644
index 00000000..b942ef70
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/filetype/types/archive.py
@@ -0,0 +1,687 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+import struct
+
+from .base import Type
+
+
+class Epub(Type):
+ """
+ Implements the EPUB archive type matcher.
+ """
+ MIME = 'application/epub+zip'
+ EXTENSION = 'epub'
+
+ def __init__(self):
+ super(Epub, self).__init__(
+ mime=Epub.MIME,
+ extension=Epub.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 57 and
+ buf[0] == 0x50 and buf[1] == 0x4B and
+ buf[2] == 0x3 and buf[3] == 0x4 and
+ buf[30] == 0x6D and buf[31] == 0x69 and
+ buf[32] == 0x6D and buf[33] == 0x65 and
+ buf[34] == 0x74 and buf[35] == 0x79 and
+ buf[36] == 0x70 and buf[37] == 0x65 and
+ buf[38] == 0x61 and buf[39] == 0x70 and
+ buf[40] == 0x70 and buf[41] == 0x6C and
+ buf[42] == 0x69 and buf[43] == 0x63 and
+ buf[44] == 0x61 and buf[45] == 0x74 and
+ buf[46] == 0x69 and buf[47] == 0x6F and
+ buf[48] == 0x6E and buf[49] == 0x2F and
+ buf[50] == 0x65 and buf[51] == 0x70 and
+ buf[52] == 0x75 and buf[53] == 0x62 and
+ buf[54] == 0x2B and buf[55] == 0x7A and
+ buf[56] == 0x69 and buf[57] == 0x70)
+
+
+class Zip(Type):
+ """
+ Implements the Zip archive type matcher.
+ """
+ MIME = 'application/zip'
+ EXTENSION = 'zip'
+
+ def __init__(self):
+ super(Zip, self).__init__(
+ mime=Zip.MIME,
+ extension=Zip.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x50 and buf[1] == 0x4B and
+ (buf[2] == 0x3 or buf[2] == 0x5 or
+ buf[2] == 0x7) and
+ (buf[3] == 0x4 or buf[3] == 0x6 or
+ buf[3] == 0x8))
+
+
+class Tar(Type):
+ """
+ Implements the Tar archive type matcher.
+ """
+ MIME = 'application/x-tar'
+ EXTENSION = 'tar'
+
+ def __init__(self):
+ super(Tar, self).__init__(
+ mime=Tar.MIME,
+ extension=Tar.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 261 and
+ buf[257] == 0x75 and
+ buf[258] == 0x73 and
+ buf[259] == 0x74 and
+ buf[260] == 0x61 and
+ buf[261] == 0x72)
+
+
+class Rar(Type):
+ """
+ Implements the RAR archive type matcher.
+ """
+ MIME = 'application/x-rar-compressed'
+ EXTENSION = 'rar'
+
+ def __init__(self):
+ super(Rar, self).__init__(
+ mime=Rar.MIME,
+ extension=Rar.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 6 and
+ buf[0] == 0x52 and
+ buf[1] == 0x61 and
+ buf[2] == 0x72 and
+ buf[3] == 0x21 and
+ buf[4] == 0x1A and
+ buf[5] == 0x7 and
+ (buf[6] == 0x0 or
+ buf[6] == 0x1))
+
+
+class Gz(Type):
+ """
+ Implements the GZ archive type matcher.
+ """
+ MIME = 'application/gzip'
+ EXTENSION = 'gz'
+
+ def __init__(self):
+ super(Gz, self).__init__(
+ mime=Gz.MIME,
+ extension=Gz.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 2 and
+ buf[0] == 0x1F and
+ buf[1] == 0x8B and
+ buf[2] == 0x8)
+
+
+class Bz2(Type):
+ """
+ Implements the BZ2 archive type matcher.
+ """
+ MIME = 'application/x-bzip2'
+ EXTENSION = 'bz2'
+
+ def __init__(self):
+ super(Bz2, self).__init__(
+ mime=Bz2.MIME,
+ extension=Bz2.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 2 and
+ buf[0] == 0x42 and
+ buf[1] == 0x5A and
+ buf[2] == 0x68)
+
+
+class SevenZ(Type):
+ """
+ Implements the SevenZ (7z) archive type matcher.
+ """
+ MIME = 'application/x-7z-compressed'
+ EXTENSION = '7z'
+
+ def __init__(self):
+ super(SevenZ, self).__init__(
+ mime=SevenZ.MIME,
+ extension=SevenZ.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 5 and
+ buf[0] == 0x37 and
+ buf[1] == 0x7A and
+ buf[2] == 0xBC and
+ buf[3] == 0xAF and
+ buf[4] == 0x27 and
+ buf[5] == 0x1C)
+
+
+class Pdf(Type):
+ """
+ Implements the PDF archive type matcher.
+ """
+ MIME = 'application/pdf'
+ EXTENSION = 'pdf'
+
+ def __init__(self):
+ super(Pdf, self).__init__(
+ mime=Pdf.MIME,
+ extension=Pdf.EXTENSION
+ )
+
+ def match(self, buf):
+ # Detect BOM and skip first 3 bytes
+ if (len(buf) > 3 and
+ buf[0] == 0xEF and
+ buf[1] == 0xBB and
+ buf[2] == 0xBF): # noqa E129
+ buf = buf[3:]
+
+ return (len(buf) > 3 and
+ buf[0] == 0x25 and
+ buf[1] == 0x50 and
+ buf[2] == 0x44 and
+ buf[3] == 0x46)
+
+
+class Exe(Type):
+ """
+ Implements the EXE archive type matcher.
+ """
+ MIME = 'application/x-msdownload'
+ EXTENSION = 'exe'
+
+ def __init__(self):
+ super(Exe, self).__init__(
+ mime=Exe.MIME,
+ extension=Exe.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 1 and
+ buf[0] == 0x4D and
+ buf[1] == 0x5A)
+
+
+class Swf(Type):
+ """
+ Implements the SWF archive type matcher.
+ """
+ MIME = 'application/x-shockwave-flash'
+ EXTENSION = 'swf'
+
+ def __init__(self):
+ super(Swf, self).__init__(
+ mime=Swf.MIME,
+ extension=Swf.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 2 and
+ (buf[0] == 0x43 or
+ buf[0] == 0x46) and
+ buf[1] == 0x57 and
+ buf[2] == 0x53)
+
+
+class Rtf(Type):
+ """
+ Implements the RTF archive type matcher.
+ """
+ MIME = 'application/rtf'
+ EXTENSION = 'rtf'
+
+ def __init__(self):
+ super(Rtf, self).__init__(
+ mime=Rtf.MIME,
+ extension=Rtf.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 4 and
+ buf[0] == 0x7B and
+ buf[1] == 0x5C and
+ buf[2] == 0x72 and
+ buf[3] == 0x74 and
+ buf[4] == 0x66)
+
+
+class Nes(Type):
+ """
+ Implements the NES archive type matcher.
+ """
+ MIME = 'application/x-nintendo-nes-rom'
+ EXTENSION = 'nes'
+
+ def __init__(self):
+ super(Nes, self).__init__(
+ mime=Nes.MIME,
+ extension=Nes.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x4E and
+ buf[1] == 0x45 and
+ buf[2] == 0x53 and
+ buf[3] == 0x1A)
+
+
+class Crx(Type):
+ """
+ Implements the CRX archive type matcher.
+ """
+ MIME = 'application/x-google-chrome-extension'
+ EXTENSION = 'crx'
+
+ def __init__(self):
+ super(Crx, self).__init__(
+ mime=Crx.MIME,
+ extension=Crx.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x43 and
+ buf[1] == 0x72 and
+ buf[2] == 0x32 and
+ buf[3] == 0x34)
+
+
+class Cab(Type):
+ """
+ Implements the CAB archive type matcher.
+ """
+ MIME = 'application/vnd.ms-cab-compressed'
+ EXTENSION = 'cab'
+
+ def __init__(self):
+ super(Cab, self).__init__(
+ mime=Cab.MIME,
+ extension=Cab.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ ((buf[0] == 0x4D and
+ buf[1] == 0x53 and
+ buf[2] == 0x43 and
+ buf[3] == 0x46) or
+ (buf[0] == 0x49 and
+ buf[1] == 0x53 and
+ buf[2] == 0x63 and
+ buf[3] == 0x28)))
+
+
+class Eot(Type):
+ """
+ Implements the EOT archive type matcher.
+ """
+ MIME = 'application/octet-stream'
+ EXTENSION = 'eot'
+
+ def __init__(self):
+ super(Eot, self).__init__(
+ mime=Eot.MIME,
+ extension=Eot.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 35 and
+ buf[34] == 0x4C and
+ buf[35] == 0x50 and
+ ((buf[8] == 0x02 and
+ buf[9] == 0x00 and
+ buf[10] == 0x01) or
+ (buf[8] == 0x01 and
+ buf[9] == 0x00 and
+ buf[10] == 0x00) or
+ (buf[8] == 0x02 and
+ buf[9] == 0x00 and
+ buf[10] == 0x02)))
+
+
+class Ps(Type):
+ """
+ Implements the PS archive type matcher.
+ """
+ MIME = 'application/postscript'
+ EXTENSION = 'ps'
+
+ def __init__(self):
+ super(Ps, self).__init__(
+ mime=Ps.MIME,
+ extension=Ps.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 1 and
+ buf[0] == 0x25 and
+ buf[1] == 0x21)
+
+
+class Xz(Type):
+ """
+ Implements the XS archive type matcher.
+ """
+ MIME = 'application/x-xz'
+ EXTENSION = 'xz'
+
+ def __init__(self):
+ super(Xz, self).__init__(
+ mime=Xz.MIME,
+ extension=Xz.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 5 and
+ buf[0] == 0xFD and
+ buf[1] == 0x37 and
+ buf[2] == 0x7A and
+ buf[3] == 0x58 and
+ buf[4] == 0x5A and
+ buf[5] == 0x00)
+
+
+class Sqlite(Type):
+ """
+ Implements the Sqlite DB archive type matcher.
+ """
+ MIME = 'application/x-sqlite3'
+ EXTENSION = 'sqlite'
+
+ def __init__(self):
+ super(Sqlite, self).__init__(
+ mime=Sqlite.MIME,
+ extension=Sqlite.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x53 and
+ buf[1] == 0x51 and
+ buf[2] == 0x4C and
+ buf[3] == 0x69)
+
+
+class Deb(Type):
+ """
+ Implements the DEB archive type matcher.
+ """
+ MIME = 'application/x-deb'
+ EXTENSION = 'deb'
+
+ def __init__(self):
+ super(Deb, self).__init__(
+ mime=Deb.MIME,
+ extension=Deb.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 20 and
+ buf[0] == 0x21 and
+ buf[1] == 0x3C and
+ buf[2] == 0x61 and
+ buf[3] == 0x72 and
+ buf[4] == 0x63 and
+ buf[5] == 0x68 and
+ buf[6] == 0x3E and
+ buf[7] == 0x0A and
+ buf[8] == 0x64 and
+ buf[9] == 0x65 and
+ buf[10] == 0x62 and
+ buf[11] == 0x69 and
+ buf[12] == 0x61 and
+ buf[13] == 0x6E and
+ buf[14] == 0x2D and
+ buf[15] == 0x62 and
+ buf[16] == 0x69 and
+ buf[17] == 0x6E and
+ buf[18] == 0x61 and
+ buf[19] == 0x72 and
+ buf[20] == 0x79)
+
+
+class Ar(Type):
+ """
+ Implements the AR archive type matcher.
+ """
+ MIME = 'application/x-unix-archive'
+ EXTENSION = 'ar'
+
+ def __init__(self):
+ super(Ar, self).__init__(
+ mime=Ar.MIME,
+ extension=Ar.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 6 and
+ buf[0] == 0x21 and
+ buf[1] == 0x3C and
+ buf[2] == 0x61 and
+ buf[3] == 0x72 and
+ buf[4] == 0x63 and
+ buf[5] == 0x68 and
+ buf[6] == 0x3E)
+
+
+class Z(Type):
+ """
+ Implements the Z archive type matcher.
+ """
+ MIME = 'application/x-compress'
+ EXTENSION = 'Z'
+
+ def __init__(self):
+ super(Z, self).__init__(
+ mime=Z.MIME,
+ extension=Z.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 1 and
+ ((buf[0] == 0x1F and
+ buf[1] == 0xA0) or
+ (buf[0] == 0x1F and
+ buf[1] == 0x9D)))
+
+
+class Lzop(Type):
+ """
+ Implements the Lzop archive type matcher.
+ """
+ MIME = 'application/x-lzop'
+ EXTENSION = 'lzo'
+
+ def __init__(self):
+ super(Lzop, self).__init__(
+ mime=Lzop.MIME,
+ extension=Lzop.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 7 and
+ buf[0] == 0x89 and
+ buf[1] == 0x4C and
+ buf[2] == 0x5A and
+ buf[3] == 0x4F and
+ buf[4] == 0x00 and
+ buf[5] == 0x0D and
+ buf[6] == 0x0A and
+ buf[7] == 0x1A)
+
+
+class Lz(Type):
+ """
+ Implements the Lz archive type matcher.
+ """
+ MIME = 'application/x-lzip'
+ EXTENSION = 'lz'
+
+ def __init__(self):
+ super(Lz, self).__init__(
+ mime=Lz.MIME,
+ extension=Lz.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x4C and
+ buf[1] == 0x5A and
+ buf[2] == 0x49 and
+ buf[3] == 0x50)
+
+
+class Elf(Type):
+ """
+ Implements the Elf archive type matcher
+ """
+ MIME = 'application/x-executable'
+ EXTENSION = 'elf'
+
+ def __init__(self):
+ super(Elf, self).__init__(
+ mime=Elf.MIME,
+ extension=Elf.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 52 and
+ buf[0] == 0x7F and
+ buf[1] == 0x45 and
+ buf[2] == 0x4C and
+ buf[3] == 0x46)
+
+
+class Lz4(Type):
+ """
+ Implements the Lz4 archive type matcher.
+ """
+ MIME = 'application/x-lz4'
+ EXTENSION = 'lz4'
+
+ def __init__(self):
+ super(Lz4, self).__init__(
+ mime=Lz4.MIME,
+ extension=Lz4.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x04 and
+ buf[1] == 0x22 and
+ buf[2] == 0x4D and
+ buf[3] == 0x18)
+
+
+class Br(Type):
+ """Implements the Br image type matcher."""
+
+ MIME = 'application/x-brotli'
+ EXTENSION = 'br'
+
+ def __init__(self):
+ super(Br, self).__init__(
+ mime=Br.MIME,
+ extension=Br.EXTENSION
+ )
+
+ def match(self, buf):
+ return buf[:4] == bytearray([0xce, 0xb2, 0xcf, 0x81])
+
+
+class Dcm(Type):
+ """Implements the Dcm image type matcher."""
+
+ MIME = 'application/dicom'
+ EXTENSION = 'dcm'
+
+ def __init__(self):
+ super(Dcm, self).__init__(
+ mime=Dcm.MIME,
+ extension=Dcm.EXTENSION
+ )
+
+ def match(self, buf):
+ return buf[128:131] == bytearray([0x44, 0x49, 0x43, 0x4d])
+
+
+class Rpm(Type):
+ """Implements the Rpm image type matcher."""
+
+ MIME = 'application/x-rpm'
+ EXTENSION = 'rpm'
+
+ def __init__(self):
+ super(Rpm, self).__init__(
+ mime=Rpm.MIME,
+ extension=Rpm.EXTENSION
+ )
+
+ def match(self, buf):
+ return buf[:4] == bytearray([0xed, 0xab, 0xee, 0xdb])
+
+
+class Zstd(Type):
+ """
+ Implements the Zstd archive type matcher.
+ https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md
+ """
+ MIME = 'application/zstd'
+ EXTENSION = 'zst'
+ MAGIC_SKIPPABLE_START = 0x184D2A50
+ MAGIC_SKIPPABLE_MASK = 0xFFFFFFF0
+
+ def __init__(self):
+ super(Zstd, self).__init__(
+ mime=Zstd.MIME,
+ extension=Zstd.EXTENSION
+ )
+
+ @staticmethod
+ def _to_little_endian_int(buf):
+ # return int.from_bytes(buf, byteorder='little')
+ return struct.unpack('<L', buf)[0]
+
+ def match(self, buf):
+ # Zstandard compressed data is made of one or more frames.
+ # There are two frame formats defined by Zstandard:
+ # Zstandard frames and Skippable frames.
+ # See more details from
+ # https://tools.ietf.org/id/draft-kucherawy-dispatch-zstd-00.html#rfc.section.2
+ is_zstd = (
+ len(buf) > 3 and
+ buf[0] in (0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28) and
+ buf[1] == 0xb5 and
+ buf[2] == 0x2f and
+ buf[3] == 0xfd)
+ if is_zstd:
+ return True
+ # skippable frames
+ if len(buf) < 8:
+ return False
+ magic = self._to_little_endian_int(buf[:4]) & Zstd.MAGIC_SKIPPABLE_MASK
+ if magic == Zstd.MAGIC_SKIPPABLE_START:
+ user_data_len = self._to_little_endian_int(buf[4:8])
+ if len(buf) < 8 + user_data_len:
+ return False
+ next_frame = buf[8 + user_data_len:]
+ return self.match(next_frame)
+ return False
diff --git a/.venv/lib/python3.12/site-packages/filetype/types/audio.py b/.venv/lib/python3.12/site-packages/filetype/types/audio.py
new file mode 100644
index 00000000..3d1f20c3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/filetype/types/audio.py
@@ -0,0 +1,212 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+from .base import Type
+
+
+class Midi(Type):
+ """
+ Implements the Midi audio type matcher.
+ """
+ MIME = 'audio/midi'
+ EXTENSION = 'midi'
+
+ def __init__(self):
+ super(Midi, self).__init__(
+ mime=Midi.MIME,
+ extension=Midi.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x4D and
+ buf[1] == 0x54 and
+ buf[2] == 0x68 and
+ buf[3] == 0x64)
+
+
+class Mp3(Type):
+ """
+ Implements the MP3 audio type matcher.
+ """
+ MIME = 'audio/mpeg'
+ EXTENSION = 'mp3'
+
+ def __init__(self):
+ super(Mp3, self).__init__(
+ mime=Mp3.MIME,
+ extension=Mp3.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 2 and
+ ((buf[0] == 0x49 and
+ buf[1] == 0x44 and
+ buf[2] == 0x33) or
+ (buf[0] == 0xFF and
+ buf[1] == 0xF2) or
+ (buf[0] == 0xFF and
+ buf[1] == 0xF3) or
+ (buf[0] == 0xFF and
+ buf[1] == 0xFB)))
+
+
+class M4a(Type):
+ """
+ Implements the M4A audio type matcher.
+ """
+ MIME = 'audio/mp4'
+ EXTENSION = 'm4a'
+
+ def __init__(self):
+ super(M4a, self).__init__(
+ mime=M4a.MIME,
+ extension=M4a.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 10 and
+ ((buf[4] == 0x66 and
+ buf[5] == 0x74 and
+ buf[6] == 0x79 and
+ buf[7] == 0x70 and
+ buf[8] == 0x4D and
+ buf[9] == 0x34 and
+ buf[10] == 0x41) or
+ (buf[0] == 0x4D and
+ buf[1] == 0x34 and
+ buf[2] == 0x41 and
+ buf[3] == 0x20)))
+
+
+class Ogg(Type):
+ """
+ Implements the OGG audio type matcher.
+ """
+ MIME = 'audio/ogg'
+ EXTENSION = 'ogg'
+
+ def __init__(self):
+ super(Ogg, self).__init__(
+ mime=Ogg.MIME,
+ extension=Ogg.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x4F and
+ buf[1] == 0x67 and
+ buf[2] == 0x67 and
+ buf[3] == 0x53)
+
+
+class Flac(Type):
+ """
+ Implements the FLAC audio type matcher.
+ """
+ MIME = 'audio/x-flac'
+ EXTENSION = 'flac'
+
+ def __init__(self):
+ super(Flac, self).__init__(
+ mime=Flac.MIME,
+ extension=Flac.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x66 and
+ buf[1] == 0x4C and
+ buf[2] == 0x61 and
+ buf[3] == 0x43)
+
+
+class Wav(Type):
+ """
+ Implements the WAV audio type matcher.
+ """
+ MIME = 'audio/x-wav'
+ EXTENSION = 'wav'
+
+ def __init__(self):
+ super(Wav, self).__init__(
+ mime=Wav.MIME,
+ extension=Wav.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 11 and
+ buf[0] == 0x52 and
+ buf[1] == 0x49 and
+ buf[2] == 0x46 and
+ buf[3] == 0x46 and
+ buf[8] == 0x57 and
+ buf[9] == 0x41 and
+ buf[10] == 0x56 and
+ buf[11] == 0x45)
+
+
+class Amr(Type):
+ """
+ Implements the AMR audio type matcher.
+ """
+ MIME = 'audio/amr'
+ EXTENSION = 'amr'
+
+ def __init__(self):
+ super(Amr, self).__init__(
+ mime=Amr.MIME,
+ extension=Amr.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 11 and
+ buf[0] == 0x23 and
+ buf[1] == 0x21 and
+ buf[2] == 0x41 and
+ buf[3] == 0x4D and
+ buf[4] == 0x52 and
+ buf[5] == 0x0A)
+
+
+class Aac(Type):
+ """Implements the Aac audio type matcher."""
+
+ MIME = 'audio/aac'
+ EXTENSION = 'aac'
+
+ def __init__(self):
+ super(Aac, self).__init__(
+ mime=Aac.MIME,
+ extension=Aac.EXTENSION
+ )
+
+ def match(self, buf):
+ return (buf[:2] == bytearray([0xff, 0xf1]) or
+ buf[:2] == bytearray([0xff, 0xf9]))
+
+
+class Aiff(Type):
+ """
+ Implements the AIFF audio type matcher.
+ """
+ MIME = 'audio/x-aiff'
+ EXTENSION = 'aiff'
+
+ def __init__(self):
+ super(Aiff, self).__init__(
+ mime=Aiff.MIME,
+ extension=Aiff.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 11 and
+ buf[0] == 0x46 and
+ buf[1] == 0x4F and
+ buf[2] == 0x52 and
+ buf[3] == 0x4D and
+ buf[8] == 0x41 and
+ buf[9] == 0x49 and
+ buf[10] == 0x46 and
+ buf[11] == 0x46)
diff --git a/.venv/lib/python3.12/site-packages/filetype/types/base.py b/.venv/lib/python3.12/site-packages/filetype/types/base.py
new file mode 100644
index 00000000..7c0c0d26
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/filetype/types/base.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+
+
+class Type(object):
+ """
+ Represents the file type object inherited by
+ specific file type matchers.
+ Provides convenient accessor and helper methods.
+ """
+ def __init__(self, mime, extension):
+ self.__mime = mime
+ self.__extension = extension
+
+ @property
+ def mime(self):
+ return self.__mime
+
+ @property
+ def extension(self):
+ return self.__extension
+
+ def is_extension(self, extension):
+ return self.__extension is extension
+
+ def is_mime(self, mime):
+ return self.__mime is mime
+
+ def match(self, buf):
+ raise NotImplementedError
diff --git a/.venv/lib/python3.12/site-packages/filetype/types/document.py b/.venv/lib/python3.12/site-packages/filetype/types/document.py
new file mode 100644
index 00000000..9f57e98c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/filetype/types/document.py
@@ -0,0 +1,256 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+from .base import Type
+
+
+class ZippedDocumentBase(Type):
+ def match(self, buf):
+ # start by checking for ZIP local file header signature
+ idx = self.search_signature(buf, 0, 6000)
+ if idx != 0:
+ return
+
+ return self.match_document(buf)
+
+ def match_document(self, buf):
+ raise NotImplementedError
+
+ def compare_bytes(self, buf, subslice, start_offset):
+ sl = len(subslice)
+
+ if start_offset + sl > len(buf):
+ return False
+
+ return buf[start_offset:start_offset + sl] == subslice
+
+ def search_signature(self, buf, start, rangeNum):
+ signature = b"PK\x03\x04"
+ length = len(buf)
+
+ end = start + rangeNum
+ end = length if end > length else end
+
+ if start >= end:
+ return -1
+
+ try:
+ return buf.index(signature, start, end)
+ except ValueError:
+ return -1
+
+
+class OpenDocument(ZippedDocumentBase):
+ def match_document(self, buf):
+ # Check if first file in archive is the identifying file
+ if not self.compare_bytes(buf, b"mimetype", 0x1E):
+ return
+
+ # Check content of mimetype file if it matches current mime
+ return self.compare_bytes(buf, bytes(self.mime, "ASCII"), 0x26)
+
+
+class OfficeOpenXml(ZippedDocumentBase):
+ def match_document(self, buf):
+ # Check if first file in archive is the identifying file
+ ft = self.match_filename(buf, 0x1E)
+ if ft:
+ return ft
+
+ # Otherwise check that the fist file is one of these
+ if (
+ not self.compare_bytes(buf, b"[Content_Types].xml", 0x1E)
+ and not self.compare_bytes(buf, b"_rels/.rels", 0x1E)
+ and not self.compare_bytes(buf, b"docProps", 0x1E)
+ ):
+ return
+
+ # Loop through next 3 files and check if they match
+ # NOTE: OpenOffice/Libreoffice orders ZIP entry differently, so check the 4th file
+ # https://github.com/h2non/filetype/blob/d730d98ad5c990883148485b6fd5adbdd378364a/matchers/document.go#L134
+ idx = 0
+ for i in range(4):
+ # Search for next file header
+ idx = self.search_signature(buf, idx + 4, 6000)
+ if idx == -1:
+ return
+
+ # Filename is at file header + 30
+ ft = self.match_filename(buf, idx + 30)
+ if ft:
+ return ft
+
+ def match_filename(self, buf, offset):
+ if self.compare_bytes(buf, b"word/", offset):
+ return (
+ self.mime
+ == "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
+ )
+ if self.compare_bytes(buf, b"ppt/", offset):
+ return (
+ self.mime
+ == "application/vnd.openxmlformats-officedocument.presentationml.presentation"
+ )
+ if self.compare_bytes(buf, b"xl/", offset):
+ return (
+ self.mime
+ == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+ )
+
+
+class Doc(Type):
+ """
+ Implements the Microsoft Word (Office 97-2003) document type matcher.
+ """
+
+ MIME = "application/msword"
+ EXTENSION = "doc"
+
+ def __init__(self):
+ super(Doc, self).__init__(mime=Doc.MIME, extension=Doc.EXTENSION)
+
+ def match(self, buf):
+ if len(buf) > 515 and buf[0:8] == b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1":
+ if buf[512:516] == b"\xEC\xA5\xC1\x00":
+ return True
+ if (
+ len(buf) > 2142
+ and b"\x00\x0A\x00\x00\x00MSWordDoc\x00\x10\x00\x00\x00Word.Document.8\x00\xF49\xB2q"
+ in buf[2075:2142]
+ ):
+ return True
+
+ return False
+
+
+class Docx(OfficeOpenXml):
+ """
+ Implements the Microsoft Word OOXML (Office 2007+) document type matcher.
+ """
+
+ MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
+ EXTENSION = "docx"
+
+ def __init__(self):
+ super(Docx, self).__init__(mime=Docx.MIME, extension=Docx.EXTENSION)
+
+
+class Odt(OpenDocument):
+ """
+ Implements the OpenDocument Text document type matcher.
+ """
+
+ MIME = "application/vnd.oasis.opendocument.text"
+ EXTENSION = "odt"
+
+ def __init__(self):
+ super(Odt, self).__init__(mime=Odt.MIME, extension=Odt.EXTENSION)
+
+
+class Xls(Type):
+ """
+ Implements the Microsoft Excel (Office 97-2003) document type matcher.
+ """
+
+ MIME = "application/vnd.ms-excel"
+ EXTENSION = "xls"
+
+ def __init__(self):
+ super(Xls, self).__init__(mime=Xls.MIME, extension=Xls.EXTENSION)
+
+ def match(self, buf):
+ if len(buf) > 520 and buf[0:8] == b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1":
+ if buf[512:516] == b"\xFD\xFF\xFF\xFF" and (
+ buf[518] == 0x00 or buf[518] == 0x02
+ ):
+ return True
+ if buf[512:520] == b"\x09\x08\x10\x00\x00\x06\x05\x00":
+ return True
+ if (
+ len(buf) > 2095
+ and b"\xE2\x00\x00\x00\x5C\x00\x70\x00\x04\x00\x00Calc"
+ in buf[1568:2095]
+ ):
+ return True
+
+ return False
+
+
+class Xlsx(OfficeOpenXml):
+ """
+ Implements the Microsoft Excel OOXML (Office 2007+) document type matcher.
+ """
+
+ MIME = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+ EXTENSION = "xlsx"
+
+ def __init__(self):
+ super(Xlsx, self).__init__(mime=Xlsx.MIME, extension=Xlsx.EXTENSION)
+
+
+class Ods(OpenDocument):
+ """
+ Implements the OpenDocument Spreadsheet document type matcher.
+ """
+
+ MIME = "application/vnd.oasis.opendocument.spreadsheet"
+ EXTENSION = "ods"
+
+ def __init__(self):
+ super(Ods, self).__init__(mime=Ods.MIME, extension=Ods.EXTENSION)
+
+
+class Ppt(Type):
+ """
+ Implements the Microsoft PowerPoint (Office 97-2003) document type matcher.
+ """
+
+ MIME = "application/vnd.ms-powerpoint"
+ EXTENSION = "ppt"
+
+ def __init__(self):
+ super(Ppt, self).__init__(mime=Ppt.MIME, extension=Ppt.EXTENSION)
+
+ def match(self, buf):
+ if len(buf) > 524 and buf[0:8] == b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1":
+ if buf[512:516] == b"\xA0\x46\x1D\xF0":
+ return True
+ if buf[512:516] == b"\x00\x6E\x1E\xF0":
+ return True
+ if buf[512:516] == b"\x0F\x00\xE8\x03":
+ return True
+ if buf[512:516] == b"\xFD\xFF\xFF\xFF" and buf[522:524] == b"\x00\x00":
+ return True
+ if (
+ len(buf) > 2096
+ and buf[2072:2096]
+ == b"\x00\xB9\x29\xE8\x11\x00\x00\x00MS PowerPoint 97"
+ ):
+ return True
+
+ return False
+
+
+class Pptx(OfficeOpenXml):
+ """
+ Implements the Microsoft PowerPoint OOXML (Office 2007+) document type matcher.
+ """
+
+ MIME = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
+ EXTENSION = "pptx"
+
+ def __init__(self):
+ super(Pptx, self).__init__(mime=Pptx.MIME, extension=Pptx.EXTENSION)
+
+
+class Odp(OpenDocument):
+ """
+ Implements the OpenDocument Presentation document type matcher.
+ """
+
+ MIME = "application/vnd.oasis.opendocument.presentation"
+ EXTENSION = "odp"
+
+ def __init__(self):
+ super(Odp, self).__init__(mime=Odp.MIME, extension=Odp.EXTENSION)
diff --git a/.venv/lib/python3.12/site-packages/filetype/types/font.py b/.venv/lib/python3.12/site-packages/filetype/types/font.py
new file mode 100644
index 00000000..461f5c44
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/filetype/types/font.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+from .base import Type
+
+
+class Woff(Type):
+ """
+ Implements the WOFF font type matcher.
+ """
+ MIME = 'application/font-woff'
+ EXTENSION = 'woff'
+
+ def __init__(self):
+ super(Woff, self).__init__(
+ mime=Woff.MIME,
+ extension=Woff.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 7 and
+ buf[0] == 0x77 and
+ buf[1] == 0x4F and
+ buf[2] == 0x46 and
+ buf[3] == 0x46 and
+ ((buf[4] == 0x00 and
+ buf[5] == 0x01 and
+ buf[6] == 0x00 and
+ buf[7] == 0x00) or
+ (buf[4] == 0x4F and
+ buf[5] == 0x54 and
+ buf[6] == 0x54 and
+ buf[7] == 0x4F) or
+ (buf[4] == 0x74 and
+ buf[5] == 0x72 and
+ buf[6] == 0x75 and
+ buf[7] == 0x65)))
+
+
+class Woff2(Type):
+ """
+ Implements the WOFF2 font type matcher.
+ """
+ MIME = 'application/font-woff'
+ EXTENSION = 'woff2'
+
+ def __init__(self):
+ super(Woff2, self).__init__(
+ mime=Woff2.MIME,
+ extension=Woff2.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 7 and
+ buf[0] == 0x77 and
+ buf[1] == 0x4F and
+ buf[2] == 0x46 and
+ buf[3] == 0x32 and
+ ((buf[4] == 0x00 and
+ buf[5] == 0x01 and
+ buf[6] == 0x00 and
+ buf[7] == 0x00) or
+ (buf[4] == 0x4F and
+ buf[5] == 0x54 and
+ buf[6] == 0x54 and
+ buf[7] == 0x4F) or
+ (buf[4] == 0x74 and
+ buf[5] == 0x72 and
+ buf[6] == 0x75 and
+ buf[7] == 0x65)))
+
+
+class Ttf(Type):
+ """
+ Implements the TTF font type matcher.
+ """
+ MIME = 'application/font-sfnt'
+ EXTENSION = 'ttf'
+
+ def __init__(self):
+ super(Ttf, self).__init__(
+ mime=Ttf.MIME,
+ extension=Ttf.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 4 and
+ buf[0] == 0x00 and
+ buf[1] == 0x01 and
+ buf[2] == 0x00 and
+ buf[3] == 0x00 and
+ buf[4] == 0x00)
+
+
+class Otf(Type):
+ """
+ Implements the OTF font type matcher.
+ """
+ MIME = 'application/font-sfnt'
+ EXTENSION = 'otf'
+
+ def __init__(self):
+ super(Otf, self).__init__(
+ mime=Otf.MIME,
+ extension=Otf.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 4 and
+ buf[0] == 0x4F and
+ buf[1] == 0x54 and
+ buf[2] == 0x54 and
+ buf[3] == 0x4F and
+ buf[4] == 0x00)
diff --git a/.venv/lib/python3.12/site-packages/filetype/types/image.py b/.venv/lib/python3.12/site-packages/filetype/types/image.py
new file mode 100644
index 00000000..2d4d269e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/filetype/types/image.py
@@ -0,0 +1,383 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+from .base import Type
+from .isobmff import IsoBmff
+
+
+class Jpeg(Type):
+ """
+ Implements the JPEG image type matcher.
+ """
+ MIME = 'image/jpeg'
+ EXTENSION = 'jpg'
+
+ def __init__(self):
+ super(Jpeg, self).__init__(
+ mime=Jpeg.MIME,
+ extension=Jpeg.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 2 and
+ buf[0] == 0xFF and
+ buf[1] == 0xD8 and
+ buf[2] == 0xFF)
+
+
+class Jpx(Type):
+ """
+ Implements the JPEG2000 image type matcher.
+ """
+
+ MIME = "image/jpx"
+ EXTENSION = "jpx"
+
+ def __init__(self):
+ super(Jpx, self).__init__(mime=Jpx.MIME, extension=Jpx.EXTENSION)
+
+ def match(self, buf):
+ return (
+ len(buf) > 50
+ and buf[0] == 0x00
+ and buf[1] == 0x00
+ and buf[2] == 0x00
+ and buf[3] == 0x0C
+ and buf[16:24] == b"ftypjp2 "
+ )
+
+
+class Apng(Type):
+ """
+ Implements the APNG image type matcher.
+ """
+ MIME = 'image/apng'
+ EXTENSION = 'apng'
+
+ def __init__(self):
+ super(Apng, self).__init__(
+ mime=Apng.MIME,
+ extension=Apng.EXTENSION
+ )
+
+ def match(self, buf):
+ if (len(buf) > 8 and
+ buf[:8] == bytearray([0x89, 0x50, 0x4e, 0x47,
+ 0x0d, 0x0a, 0x1a, 0x0a])):
+ # cursor in buf, skip already readed 8 bytes
+ i = 8
+ while len(buf) > i:
+ data_length = int.from_bytes(buf[i:i+4], byteorder="big")
+ i += 4
+
+ chunk_type = buf[i:i+4].decode("ascii", errors='ignore')
+ i += 4
+
+ # acTL chunk in APNG should appears first than IDAT
+ # IEND is end of PNG
+ if (chunk_type == "IDAT" or chunk_type == "IEND"):
+ return False
+ elif (chunk_type == "acTL"):
+ return True
+
+ # move to the next chunk by skipping data and crc (4 bytes)
+ i += data_length + 4
+
+ return False
+
+
+class Png(Type):
+ """
+ Implements the PNG image type matcher.
+ """
+ MIME = 'image/png'
+ EXTENSION = 'png'
+
+ def __init__(self):
+ super(Png, self).__init__(
+ mime=Png.MIME,
+ extension=Png.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x89 and
+ buf[1] == 0x50 and
+ buf[2] == 0x4E and
+ buf[3] == 0x47)
+
+
+class Gif(Type):
+ """
+ Implements the GIF image type matcher.
+ """
+ MIME = 'image/gif'
+ EXTENSION = 'gif'
+
+ def __init__(self):
+ super(Gif, self).__init__(
+ mime=Gif.MIME,
+ extension=Gif.EXTENSION,
+ )
+
+ def match(self, buf):
+ return (len(buf) > 2 and
+ buf[0] == 0x47 and
+ buf[1] == 0x49 and
+ buf[2] == 0x46)
+
+
+class Webp(Type):
+ """
+ Implements the WEBP image type matcher.
+ """
+ MIME = 'image/webp'
+ EXTENSION = 'webp'
+
+ def __init__(self):
+ super(Webp, self).__init__(
+ mime=Webp.MIME,
+ extension=Webp.EXTENSION,
+ )
+
+ def match(self, buf):
+ return (len(buf) > 13 and
+ buf[0] == 0x52 and
+ buf[1] == 0x49 and
+ buf[2] == 0x46 and
+ buf[3] == 0x46 and
+ buf[8] == 0x57 and
+ buf[9] == 0x45 and
+ buf[10] == 0x42 and
+ buf[11] == 0x50 and
+ buf[12] == 0x56 and
+ buf[13] == 0x50)
+
+
+class Cr2(Type):
+ """
+ Implements the CR2 image type matcher.
+ """
+ MIME = 'image/x-canon-cr2'
+ EXTENSION = 'cr2'
+
+ def __init__(self):
+ super(Cr2, self).__init__(
+ mime=Cr2.MIME,
+ extension=Cr2.EXTENSION,
+ )
+
+ def match(self, buf):
+ return (len(buf) > 9 and
+ ((buf[0] == 0x49 and buf[1] == 0x49 and
+ buf[2] == 0x2A and buf[3] == 0x0) or
+ (buf[0] == 0x4D and buf[1] == 0x4D and
+ buf[2] == 0x0 and buf[3] == 0x2A)) and
+ buf[8] == 0x43 and buf[9] == 0x52)
+
+
+class Tiff(Type):
+ """
+ Implements the TIFF image type matcher.
+ """
+ MIME = 'image/tiff'
+ EXTENSION = 'tif'
+
+ def __init__(self):
+ super(Tiff, self).__init__(
+ mime=Tiff.MIME,
+ extension=Tiff.EXTENSION,
+ )
+
+ def match(self, buf):
+ return (len(buf) > 9 and
+ ((buf[0] == 0x49 and buf[1] == 0x49 and
+ buf[2] == 0x2A and buf[3] == 0x0) or
+ (buf[0] == 0x4D and buf[1] == 0x4D and
+ buf[2] == 0x0 and buf[3] == 0x2A))
+ and not (buf[8] == 0x43 and buf[9] == 0x52))
+
+
+class Bmp(Type):
+ """
+ Implements the BMP image type matcher.
+ """
+ MIME = 'image/bmp'
+ EXTENSION = 'bmp'
+
+ def __init__(self):
+ super(Bmp, self).__init__(
+ mime=Bmp.MIME,
+ extension=Bmp.EXTENSION,
+ )
+
+ def match(self, buf):
+ return (len(buf) > 1 and
+ buf[0] == 0x42 and
+ buf[1] == 0x4D)
+
+
+class Jxr(Type):
+ """
+ Implements the JXR image type matcher.
+ """
+ MIME = 'image/vnd.ms-photo'
+ EXTENSION = 'jxr'
+
+ def __init__(self):
+ super(Jxr, self).__init__(
+ mime=Jxr.MIME,
+ extension=Jxr.EXTENSION,
+ )
+
+ def match(self, buf):
+ return (len(buf) > 2 and
+ buf[0] == 0x49 and
+ buf[1] == 0x49 and
+ buf[2] == 0xBC)
+
+
+class Psd(Type):
+ """
+ Implements the PSD image type matcher.
+ """
+ MIME = 'image/vnd.adobe.photoshop'
+ EXTENSION = 'psd'
+
+ def __init__(self):
+ super(Psd, self).__init__(
+ mime=Psd.MIME,
+ extension=Psd.EXTENSION,
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x38 and
+ buf[1] == 0x42 and
+ buf[2] == 0x50 and
+ buf[3] == 0x53)
+
+
+class Ico(Type):
+ """
+ Implements the ICO image type matcher.
+ """
+ MIME = 'image/x-icon'
+ EXTENSION = 'ico'
+
+ def __init__(self):
+ super(Ico, self).__init__(
+ mime=Ico.MIME,
+ extension=Ico.EXTENSION,
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x00 and
+ buf[1] == 0x00 and
+ buf[2] == 0x01 and
+ buf[3] == 0x00)
+
+
+class Heic(IsoBmff):
+ """
+ Implements the HEIC image type matcher.
+ """
+ MIME = 'image/heic'
+ EXTENSION = 'heic'
+
+ def __init__(self):
+ super(Heic, self).__init__(
+ mime=Heic.MIME,
+ extension=Heic.EXTENSION
+ )
+
+ def match(self, buf):
+ if not self._is_isobmff(buf):
+ return False
+
+ major_brand, minor_version, compatible_brands = self._get_ftyp(buf)
+ if major_brand == 'heic':
+ return True
+ if major_brand in ['mif1', 'msf1'] and 'heic' in compatible_brands:
+ return True
+ return False
+
+
+class Dcm(Type):
+
+ MIME = 'application/dicom'
+ EXTENSION = 'dcm'
+ OFFSET = 128
+
+ def __init__(self):
+ super(Dcm, self).__init__(
+ mime=Dcm.MIME,
+ extension=Dcm.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > Dcm.OFFSET + 4 and
+ buf[Dcm.OFFSET + 0] == 0x44 and
+ buf[Dcm.OFFSET + 1] == 0x49 and
+ buf[Dcm.OFFSET + 2] == 0x43 and
+ buf[Dcm.OFFSET + 3] == 0x4D)
+
+
+class Dwg(Type):
+ """Implements the Dwg image type matcher."""
+
+ MIME = 'image/vnd.dwg'
+ EXTENSION = 'dwg'
+
+ def __init__(self):
+ super(Dwg, self).__init__(
+ mime=Dwg.MIME,
+ extension=Dwg.EXTENSION
+ )
+
+ def match(self, buf):
+ return buf[:4] == bytearray([0x41, 0x43, 0x31, 0x30])
+
+
+class Xcf(Type):
+ """Implements the Xcf image type matcher."""
+
+ MIME = 'image/x-xcf'
+ EXTENSION = 'xcf'
+
+ def __init__(self):
+ super(Xcf, self).__init__(
+ mime=Xcf.MIME,
+ extension=Xcf.EXTENSION
+ )
+
+ def match(self, buf):
+ return buf[:10] == bytearray([0x67, 0x69, 0x6d, 0x70, 0x20,
+ 0x78, 0x63, 0x66, 0x20, 0x76])
+
+
+class Avif(IsoBmff):
+ """
+ Implements the AVIF image type matcher.
+ """
+ MIME = 'image/avif'
+ EXTENSION = 'avif'
+
+ def __init__(self):
+ super(Avif, self).__init__(
+ mime=Avif.MIME,
+ extension=Avif.EXTENSION
+ )
+
+ def match(self, buf):
+ if not self._is_isobmff(buf):
+ return False
+
+ major_brand, minor_version, compatible_brands = self._get_ftyp(buf)
+ if major_brand == 'avif':
+ return True
+ if major_brand in ['mif1', 'msf1'] and 'avif' in compatible_brands:
+ return True
+ return False
diff --git a/.venv/lib/python3.12/site-packages/filetype/types/isobmff.py b/.venv/lib/python3.12/site-packages/filetype/types/isobmff.py
new file mode 100644
index 00000000..2ac0ffe8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/filetype/types/isobmff.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+import codecs
+
+from .base import Type
+
+
+class IsoBmff(Type):
+ """
+ Implements the ISO-BMFF base type.
+ """
+ def __init__(self, mime, extension):
+ super(IsoBmff, self).__init__(
+ mime=mime,
+ extension=extension
+ )
+
+ def _is_isobmff(self, buf):
+ if len(buf) < 16 or buf[4:8] != b'ftyp':
+ return False
+ if len(buf) < int(codecs.encode(buf[0:4], 'hex'), 16):
+ return False
+ return True
+
+ def _get_ftyp(self, buf):
+ ftyp_len = int(codecs.encode(buf[0:4], 'hex'), 16)
+ major_brand = buf[8:12].decode(errors='ignore')
+ minor_version = int(codecs.encode(buf[12:16], 'hex'), 16)
+ compatible_brands = []
+ for i in range(16, ftyp_len, 4):
+ compatible_brands.append(buf[i:i+4].decode(errors='ignore'))
+
+ return major_brand, minor_version, compatible_brands
diff --git a/.venv/lib/python3.12/site-packages/filetype/types/video.py b/.venv/lib/python3.12/site-packages/filetype/types/video.py
new file mode 100644
index 00000000..336b2526
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/filetype/types/video.py
@@ -0,0 +1,223 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+from .base import Type
+from .isobmff import IsoBmff
+
+
+class Mp4(IsoBmff):
+ """
+ Implements the MP4 video type matcher.
+ """
+ MIME = 'video/mp4'
+ EXTENSION = 'mp4'
+
+ def __init__(self):
+ super(Mp4, self).__init__(
+ mime=Mp4.MIME,
+ extension=Mp4.EXTENSION
+ )
+
+ def match(self, buf):
+ if not self._is_isobmff(buf):
+ return False
+
+ major_brand, minor_version, compatible_brands = self._get_ftyp(buf)
+ for brand in compatible_brands:
+ if brand in ['mp41', 'mp42', 'isom']:
+ return True
+ return major_brand in ['mp41', 'mp42', 'isom']
+
+
+class M4v(Type):
+ """
+ Implements the M4V video type matcher.
+ """
+ MIME = 'video/x-m4v'
+ EXTENSION = 'm4v'
+
+ def __init__(self):
+ super(M4v, self).__init__(
+ mime=M4v.MIME,
+ extension=M4v.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 10 and
+ buf[0] == 0x0 and buf[1] == 0x0 and
+ buf[2] == 0x0 and buf[3] == 0x1C and
+ buf[4] == 0x66 and buf[5] == 0x74 and
+ buf[6] == 0x79 and buf[7] == 0x70 and
+ buf[8] == 0x4D and buf[9] == 0x34 and
+ buf[10] == 0x56)
+
+
+class Mkv(Type):
+ """
+ Implements the MKV video type matcher.
+ """
+ MIME = 'video/x-matroska'
+ EXTENSION = 'mkv'
+
+ def __init__(self):
+ super(Mkv, self).__init__(
+ mime=Mkv.MIME,
+ extension=Mkv.EXTENSION
+ )
+
+ def match(self, buf):
+ contains_ebml_element = buf.startswith(b'\x1A\x45\xDF\xA3')
+ contains_doctype_element = buf.find(b'\x42\x82\x88matroska') > -1
+ return contains_ebml_element and contains_doctype_element
+
+
+class Webm(Type):
+ """
+ Implements the WebM video type matcher.
+ """
+ MIME = 'video/webm'
+ EXTENSION = 'webm'
+
+ def __init__(self):
+ super(Webm, self).__init__(
+ mime=Webm.MIME,
+ extension=Webm.EXTENSION
+ )
+
+ def match(self, buf):
+ contains_ebml_element = buf.startswith(b'\x1A\x45\xDF\xA3')
+ contains_doctype_element = buf.find(b'\x42\x82\x84webm') > -1
+ return contains_ebml_element and contains_doctype_element
+
+
+class Mov(IsoBmff):
+ """
+ Implements the MOV video type matcher.
+ """
+ MIME = 'video/quicktime'
+ EXTENSION = 'mov'
+
+ def __init__(self):
+ super(Mov, self).__init__(
+ mime=Mov.MIME,
+ extension=Mov.EXTENSION
+ )
+
+ def match(self, buf):
+ if not self._is_isobmff(buf):
+ return False
+
+ major_brand, minor_version, compatible_brands = self._get_ftyp(buf)
+ return major_brand == 'qt '
+
+
+class Avi(Type):
+ """
+ Implements the AVI video type matcher.
+ """
+ MIME = 'video/x-msvideo'
+ EXTENSION = 'avi'
+
+ def __init__(self):
+ super(Avi, self).__init__(
+ mime=Avi.MIME,
+ extension=Avi.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 11 and
+ buf[0] == 0x52 and
+ buf[1] == 0x49 and
+ buf[2] == 0x46 and
+ buf[3] == 0x46 and
+ buf[8] == 0x41 and
+ buf[9] == 0x56 and
+ buf[10] == 0x49 and
+ buf[11] == 0x20)
+
+
+class Wmv(Type):
+ """
+ Implements the WMV video type matcher.
+ """
+ MIME = 'video/x-ms-wmv'
+ EXTENSION = 'wmv'
+
+ def __init__(self):
+ super(Wmv, self).__init__(
+ mime=Wmv.MIME,
+ extension=Wmv.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 9 and
+ buf[0] == 0x30 and
+ buf[1] == 0x26 and
+ buf[2] == 0xB2 and
+ buf[3] == 0x75 and
+ buf[4] == 0x8E and
+ buf[5] == 0x66 and
+ buf[6] == 0xCF and
+ buf[7] == 0x11 and
+ buf[8] == 0xA6 and
+ buf[9] == 0xD9)
+
+
+class Flv(Type):
+ """
+ Implements the FLV video type matcher.
+ """
+ MIME = 'video/x-flv'
+ EXTENSION = 'flv'
+
+ def __init__(self):
+ super(Flv, self).__init__(
+ mime=Flv.MIME,
+ extension=Flv.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x46 and
+ buf[1] == 0x4C and
+ buf[2] == 0x56 and
+ buf[3] == 0x01)
+
+
+class Mpeg(Type):
+ """
+ Implements the MPEG video type matcher.
+ """
+ MIME = 'video/mpeg'
+ EXTENSION = 'mpg'
+
+ def __init__(self):
+ super(Mpeg, self).__init__(
+ mime=Mpeg.MIME,
+ extension=Mpeg.EXTENSION
+ )
+
+ def match(self, buf):
+ return (len(buf) > 3 and
+ buf[0] == 0x0 and
+ buf[1] == 0x0 and
+ buf[2] == 0x1 and
+ buf[3] >= 0xb0 and
+ buf[3] <= 0xbf)
+
+
+class M3gp(Type):
+ """Implements the 3gp image type matcher."""
+
+ MIME = 'video/3gpp'
+ EXTENSION = '3gp'
+
+ def __init__(self):
+ super(M3gp, self).__init__(
+ mime=M3gp.MIME,
+ extension=M3gp.EXTENSION
+ )
+
+ def match(self, buf):
+ return buf[:7] == bytearray([0x66, 0x74, 0x79, 0x70, 0x33, 0x67, 0x70])