MIME Type Reference — Python source
A searchable map of common file extensions to their MIME types - text, image, audio, video, application, font, and multipart. Look up by extension or by type, filter by category, and copy the exact Content-Type string for any format.
This is the Python implementation — the same logic the interactive tool runs, in a shareable, citable form.
"""MIME / Content-Type reference — pure, deterministic data + lookups.
Language: Python (3.9+; uses `Literal`, `Final`, and PEP 585 generics).
CosmoDev polyglot showcase port of the ``mime-types`` tool, ported from
``src/lib/mime-types.ts``. Display source — part of CosmoDev's polyglot tool
pages.
A curated set of common file extensions mapped to their standard MIME types,
grouped by the seven top-level media types (RFC 2046): text, image, audio,
video, application, font, and multipart. Each entry's ``type`` always equals
the top-level segment of its own ``mime``, so the category filter and the
Content-Type never disagree.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Final, Literal, Optional
# The seven top-level media types a curated entry can belong to. ``Literal``
# plays the role of the TypeScript string union: mypy will flag any other value.
MimeCategory = Literal[
"text",
"image",
"audio",
"video",
"application",
"font",
"multipart",
]
@dataclass(frozen=True)
class MimeEntry:
"""One curated extension ↔ MIME mapping (immutable value object)."""
ext: str
mime: str
type: MimeCategory
name: str
@dataclass(frozen=True)
class MimeFilter:
"""Optional constraints for :func:`filter`.
``None`` fields are ignored, mirroring ``undefined`` fields in the
TypeScript source.
"""
type: Optional[MimeCategory] = None
query: Optional[str] = None
#: Display order of the categories (used by the UI filter).
MIME_CATEGORIES: Final[tuple[MimeCategory, ...]] = (
"text",
"image",
"audio",
"video",
"application",
"font",
"multipart",
)
#: The curated extension ↔ MIME table. One entry is one (extension, type) pair,
#: so an extension with several MIME types (e.g. ``.js``) appears more than
#: once, and a MIME with several extensions (e.g. ``image/jpeg``) appears once
#: per extension. Values follow IANA / MDN conventions. Frozen tuple keeps the
#: registry immutable at runtime.
MIME: Final[tuple[MimeEntry, ...]] = (
# ── text ──────────────────────────────────────────────────────────────
MimeEntry("txt", "text/plain", "text", "Plain text"),
MimeEntry("html", "text/html", "text", "HTML document"),
MimeEntry("htm", "text/html", "text", "HTML document"),
MimeEntry("xhtml", "application/xhtml+xml", "application", "XHTML"),
MimeEntry("css", "text/css", "text", "CSS stylesheet"),
MimeEntry("csv", "text/csv", "text", "Comma-separated values"),
MimeEntry("xml", "application/xml", "application", "XML document"),
MimeEntry("js", "text/javascript", "text", "JavaScript"),
MimeEntry("js", "application/javascript", "application", "JavaScript (legacy)"),
MimeEntry("mjs", "text/javascript", "text", "JavaScript module"),
MimeEntry("json", "application/json", "application", "JSON"),
MimeEntry("md", "text/markdown", "text", "Markdown"),
MimeEntry("yaml", "text/yaml", "text", "YAML"),
MimeEntry("yml", "text/yaml", "text", "YAML"),
MimeEntry("ics", "text/calendar", "text", "iCalendar"),
MimeEntry("rtf", "application/rtf", "application", "Rich text"),
MimeEntry("tsv", "text/tab-separated-values", "text", "Tab-separated values"),
# ── image ─────────────────────────────────────────────────────────────
MimeEntry("jpg", "image/jpeg", "image", "JPEG image"),
MimeEntry("jpeg", "image/jpeg", "image", "JPEG image"),
MimeEntry("png", "image/png", "image", "PNG image"),
MimeEntry("gif", "image/gif", "image", "GIF image"),
MimeEntry("webp", "image/webp", "image", "WebP image"),
MimeEntry("avif", "image/avif", "image", "AVIF image"),
MimeEntry("svg", "image/svg+xml", "image", "SVG image"),
MimeEntry("bmp", "image/bmp", "image", "Bitmap image"),
MimeEntry("ico", "image/x-icon", "image", "Icon"),
MimeEntry("tif", "image/tiff", "image", "TIFF image"),
MimeEntry("tiff", "image/tiff", "image", "TIFF image"),
MimeEntry("heic", "image/heic", "image", "HEIC image"),
# ── audio ─────────────────────────────────────────────────────────────
MimeEntry("mp3", "audio/mpeg", "audio", "MP3 audio"),
MimeEntry("wav", "audio/wav", "audio", "WAV audio"),
MimeEntry("ogg", "audio/ogg", "audio", "Ogg audio"),
MimeEntry("oga", "audio/ogg", "audio", "Ogg audio"),
MimeEntry("flac", "audio/flac", "audio", "FLAC audio"),
MimeEntry("aac", "audio/aac", "audio", "AAC audio"),
MimeEntry("m4a", "audio/mp4", "audio", "M4A audio"),
MimeEntry("weba", "audio/webm", "audio", "WebM audio"),
MimeEntry("mid", "audio/midi", "audio", "MIDI"),
MimeEntry("midi", "audio/midi", "audio", "MIDI"),
# ── video ─────────────────────────────────────────────────────────────
MimeEntry("mp4", "video/mp4", "video", "MP4 video"),
MimeEntry("webm", "video/webm", "video", "WebM video"),
MimeEntry("avi", "video/x-msvideo", "video", "AVI video"),
MimeEntry("mov", "video/quicktime", "video", "QuickTime video"),
MimeEntry("mkv", "video/x-matroska", "video", "Matroska video"),
MimeEntry("mpeg", "video/mpeg", "video", "MPEG video"),
MimeEntry("mpg", "video/mpeg", "video", "MPEG video"),
MimeEntry("ogv", "video/ogg", "video", "Ogg video"),
MimeEntry("m4v", "video/mp4", "video", "M4V video"),
# ── application ───────────────────────────────────────────────────────
MimeEntry("pdf", "application/pdf", "application", "PDF document"),
MimeEntry("zip", "application/zip", "application", "ZIP archive"),
MimeEntry("gz", "application/gzip", "application", "Gzip archive"),
MimeEntry("tar", "application/x-tar", "application", "Tar archive"),
MimeEntry("rar", "application/vnd.rar", "application", "RAR archive"),
MimeEntry("7z", "application/x-7z-compressed", "application", "7-Zip archive"),
MimeEntry("bin", "application/octet-stream", "application", "Binary data"),
MimeEntry("exe", "application/x-msdownload", "application", "Windows executable"),
MimeEntry("jar", "application/java-archive", "application", "Java archive"),
MimeEntry("wasm", "application/wasm", "application", "WebAssembly"),
MimeEntry("doc", "application/msword", "application", "Word document"),
MimeEntry(
"docx",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application",
"Word document (OOXML)",
),
MimeEntry("xls", "application/vnd.ms-excel", "application", "Excel spreadsheet"),
MimeEntry(
"xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application",
"Excel spreadsheet (OOXML)",
),
MimeEntry("ppt", "application/vnd.ms-powerpoint", "application", "PowerPoint"),
MimeEntry(
"pptx",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application",
"PowerPoint (OOXML)",
),
# ── font ──────────────────────────────────────────────────────────────
MimeEntry("woff", "font/woff", "font", "WOFF font"),
MimeEntry("woff2", "font/woff2", "font", "WOFF2 font"),
MimeEntry("ttf", "font/ttf", "font", "TrueType font"),
MimeEntry("otf", "font/otf", "font", "OpenType font"),
MimeEntry("eot", "application/vnd.ms-fontobject", "application", "Embedded OpenType"),
# ── multipart ─────────────────────────────────────────────────────────
MimeEntry("mht", "multipart/related", "multipart", "MHTML web archive"),
MimeEntry("mhtml", "multipart/related", "multipart", "MHTML web archive"),
)
def normalize_ext(ext: str) -> str:
"""Lowercase an extension and drop a leading dot so ``.JSON`` == ``json``.
``lstrip(".")`` removes one or more leading dots, matching the TypeScript
``/^\\.+/`` regex.
"""
return ext.strip().lower().lstrip(".")
def lookup_by_ext(ext: str) -> list[MimeEntry]:
"""Every entry whose extension matches.
The dot is optional and matching is case-insensitive, so ``.js``, ``JS``,
and ``js`` are equivalent. Returns ``[]`` for an unknown/empty extension.
"""
e = normalize_ext(ext)
if not e:
return []
return [m for m in MIME if m.ext == e]
def lookup_by_mime(mime: str) -> list[MimeEntry]:
"""Every entry whose MIME matches, case-insensitively, tolerating
surrounding whitespace.
One MIME commonly maps to several extensions (e.g. ``image/jpeg`` →
``jpg``, ``jpeg``), so all are returned. Returns ``[]`` for unknown/empty.
"""
m = mime.strip().lower()
if not m:
return []
return [e for e in MIME if e.mime.lower() == m]
def filter(opts: MimeFilter = MimeFilter()) -> list[MimeEntry]: # noqa: A001
"""Filter the full table by category and/or a free-text query.
Omitted (``None``) options are ignored; an empty query returns the
category-filtered set unchanged. An unknown category value yields nothing
(it is not silently coerced).
``opts.query or ""`` collapses both ``None`` and ``""`` to the empty query,
matching the TypeScript ``(opts.query ?? "")`` coalescing.
"""
q = (opts.query or "").strip().lower()
out: list[MimeEntry] = []
for e in MIME:
if opts.type is not None and e.type != opts.type:
continue
if q:
if not (q in e.ext.lower() or q in e.mime.lower() or q in e.name.lower()):
continue
out.append(e)
return out
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →