Skip to content

Commit cf6eed2

Browse files
committed
fixes #711
1 parent 307486c commit cf6eed2

3 files changed

Lines changed: 81 additions & 5 deletions

File tree

fastcore/_modidx.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,7 @@
658658
'fastcore.xtras.bunzip': ('xtras.html#bunzip', 'fastcore/xtras.py'),
659659
'fastcore.xtras.console_help': ('xtras.html#console_help', 'fastcore/xtras.py'),
660660
'fastcore.xtras.dataclass_src': ('xtras.html#dataclass_src', 'fastcore/xtras.py'),
661+
'fastcore.xtras.detect_mime': ('xtras.html#detect_mime', 'fastcore/xtras.py'),
661662
'fastcore.xtras.dict2obj': ('xtras.html#dict2obj', 'fastcore/xtras.py'),
662663
'fastcore.xtras.dumps': ('xtras.html#dumps', 'fastcore/xtras.py'),
663664
'fastcore.xtras.exec_eval': ('xtras.html#exec_eval', 'fastcore/xtras.py'),

fastcore/xtras.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
from __future__ import annotations
77

88
# %% auto 0
9-
__all__ = ['spark_chars', 'UNSET', 'walk', 'globtastic', 'maybe_open', 'mkdir', 'image_size', 'img_bytes', 'bunzip', 'loads',
10-
'loads_multi', 'dumps', 'untar_dir', 'repo_details', 'shell', 'ssh', 'rsync_multi', 'run', 'open_file',
11-
'save_pickle', 'load_pickle', 'parse_env', 'expand_wildcards', 'dict2obj', 'obj2dict', 'repr_dict',
12-
'is_listy', 'mapped', 'IterLen', 'ReindexCollection', 'SaveReturn', 'trim_wraps', 'save_iter', 'asave_iter',
13-
'friendly_name', 'n_friendly_names', 'exec_eval', 'get_source_link', 'truncstr', 'sparkline',
9+
__all__ = ['spark_chars', 'UNSET', 'walk', 'globtastic', 'maybe_open', 'mkdir', 'image_size', 'img_bytes', 'detect_mime',
10+
'bunzip', 'loads', 'loads_multi', 'dumps', 'untar_dir', 'repo_details', 'shell', 'ssh', 'rsync_multi', 'run',
11+
'open_file', 'save_pickle', 'load_pickle', 'parse_env', 'expand_wildcards', 'dict2obj', 'obj2dict',
12+
'repr_dict', 'is_listy', 'mapped', 'IterLen', 'ReindexCollection', 'SaveReturn', 'trim_wraps', 'save_iter',
13+
'asave_iter', 'friendly_name', 'n_friendly_names', 'exec_eval', 'get_source_link', 'truncstr', 'sparkline',
1414
'modify_exception', 'round_multiple', 'set_num_threads', 'join_path_file', 'autostart', 'EventTimer',
1515
'stringfmt_names', 'PartialFormatter', 'partial_format', 'utc2local', 'local2utc', 'trace', 'modified_env',
1616
'ContextManagers', 'shufflish', 'console_help', 'hl_md', 'type2str', 'dataclass_src', 'Unset', 'nullable_dc',
@@ -134,6 +134,30 @@ def img_bytes(img, fmt='PNG'):
134134
img.save(buf, format=fmt)
135135
return buf.getvalue()
136136

137+
# %% ../nbs/03_xtras.ipynb
138+
_sigs = {
139+
(b'%PDF', 0): 'application/pdf',
140+
(b'RIFF', 0): lambda d: 'audio/wav' if d[8:12]==b'WAVE' else 'video/avi' if d[8:12]==b'AVI ' else None,
141+
(b'ID3', 0): 'audio/mp3',
142+
(b'\xff\xfb', 0): 'audio/mp3',
143+
(b'\xff\xf3', 0): 'audio/mp3',
144+
(b'FORM', 0): lambda d: 'audio/aiff' if d[8:12]==b'AIFF' else None,
145+
(b'OggS', 0): 'audio/ogg',
146+
(b'fLaC', 0): 'audio/flac',
147+
(b'ftyp', 4): lambda d: 'video/3gpp' if d[8:11]==b'3gp' else 'video/mp4',
148+
(b'\x1a\x45\xdf', 0): 'video/webm',
149+
(b'FLV', 0): 'video/x-flv',
150+
(b'\x30\x26\xb2\x75', 0): 'video/wmv',
151+
(b'\x00\x00\x01\xb3', 0): 'video/mpeg',
152+
}
153+
154+
def detect_mime(data):
155+
"Get the MIME type for bytes `data`, covering common PDF, audio, video, and image types"
156+
import mimetypes
157+
for (sig,pos),mime in _sigs.items():
158+
if data[pos:pos+len(sig)]==sig: return mime(data) if callable(mime) else mime
159+
return mimetypes.types_map.get(f'.{imghdr.what(None, h=data)}')
160+
137161
# %% ../nbs/03_xtras.ipynb
138162
def bunzip(fn):
139163
"bunzip `fn`, raising exception if output already exists"

nbs/03_xtras.ipynb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,57 @@
505505
"IPImage(ib)"
506506
]
507507
},
508+
{
509+
"cell_type": "code",
510+
"execution_count": null,
511+
"metadata": {},
512+
"outputs": [],
513+
"source": [
514+
"#| export\n",
515+
"_sigs = {\n",
516+
" (b'%PDF', 0): 'application/pdf',\n",
517+
" (b'RIFF', 0): lambda d: 'audio/wav' if d[8:12]==b'WAVE' else 'video/avi' if d[8:12]==b'AVI ' else None,\n",
518+
" (b'ID3', 0): 'audio/mp3',\n",
519+
" (b'\\xff\\xfb', 0): 'audio/mp3',\n",
520+
" (b'\\xff\\xf3', 0): 'audio/mp3',\n",
521+
" (b'FORM', 0): lambda d: 'audio/aiff' if d[8:12]==b'AIFF' else None,\n",
522+
" (b'OggS', 0): 'audio/ogg',\n",
523+
" (b'fLaC', 0): 'audio/flac',\n",
524+
" (b'ftyp', 4): lambda d: 'video/3gpp' if d[8:11]==b'3gp' else 'video/mp4',\n",
525+
" (b'\\x1a\\x45\\xdf', 0): 'video/webm',\n",
526+
" (b'FLV', 0): 'video/x-flv',\n",
527+
" (b'\\x30\\x26\\xb2\\x75', 0): 'video/wmv',\n",
528+
" (b'\\x00\\x00\\x01\\xb3', 0): 'video/mpeg',\n",
529+
"}\n",
530+
"\n",
531+
"def detect_mime(data):\n",
532+
" \"Get the MIME type for bytes `data`, covering common PDF, audio, video, and image types\"\n",
533+
" import mimetypes\n",
534+
" for (sig,pos),mime in _sigs.items():\n",
535+
" if data[pos:pos+len(sig)]==sig: return mime(data) if callable(mime) else mime\n",
536+
" return mimetypes.types_map.get(f'.{imghdr.what(None, h=data)}')"
537+
]
538+
},
539+
{
540+
"cell_type": "code",
541+
"execution_count": null,
542+
"metadata": {},
543+
"outputs": [
544+
{
545+
"data": {
546+
"text/plain": [
547+
"'image/png'"
548+
]
549+
},
550+
"execution_count": null,
551+
"metadata": {},
552+
"output_type": "execute_result"
553+
}
554+
],
555+
"source": [
556+
"detect_mime(ib)"
557+
]
558+
},
508559
{
509560
"cell_type": "code",
510561
"execution_count": null,

0 commit comments

Comments
 (0)