(backend) add mimetype detection logic from drive

Reused the logic developed by the team working on drive.
This is usefull for our own upload file backend (that will
come in later commits).

Dockefile was updated to add a required system dependency.

We might want to put this shared logic in a lib.
This commit is contained in:
Florent Chehab
2026-02-26 10:34:52 +01:00
committed by aleb_the_flash
parent 124a8bf8d9
commit 047da94494
4 changed files with 236 additions and 0 deletions
+67
View File
@@ -7,6 +7,8 @@ Utils functions used in the core app
import hashlib
import json
import logging
import mimetypes
import random
import secrets
import string
@@ -18,6 +20,7 @@ from django.core.files.storage import default_storage
import aiohttp
import botocore
import magic
from asgiref.sync import async_to_sync
from livekit.api import ( # pylint: disable=E0611
AccessToken,
@@ -29,6 +32,8 @@ from livekit.api import ( # pylint: disable=E0611
VideoGrants,
)
logger = logging.getLogger(__name__)
def generate_color(identity: str) -> str:
"""Generates a consistent HSL color based on a given identity string.
@@ -344,3 +349,65 @@ def generate_room_slug():
for size in sizes
]
return "-".join(parts)
def detect_mimetype(file_buffer: bytes, filename: str | None = None) -> str:
"""
Detect MIME type using multiple methods for better accuracy.
This function combines:
1. Magic bytes detection (python-magic) - most reliable for actual file content
2. File extension detection (mimetypes) - useful as fallback or for validation
Args:
file_buffer: The file content buffer (first bytes of the file)
filename: Optional filename to extract extension from
Returns:
str: The detected MIME type
Notes:
Originally from https://github.com/suitenumerique/drive/blob/564822d31f071c6dfacd112ef4b7146c73077cd9/src/backend/core/api/utils.py#L166 # pylint:disable=line-too-long
"""
# Initialize magic detector
mime_detector = magic.Magic(mime=True)
# Method 1: Detect from file content (magic bytes) - most reliable
mimetype_from_content = mime_detector.from_buffer(file_buffer)
# If we have a filename, try extension-based detection as well
mimetype_from_extension = None
if filename:
# Use mimetypes module to guess from extension
# Use guess_file_type (Python 3.13+) instead of deprecated guess_type
mimetype_from_extension, _ = mimetypes.guess_file_type(filename, strict=False)
logger.info("detect_mimetype: mimetype_from_content: %s", mimetype_from_content)
logger.info("detect_mimetype: mimetype_from_extension: %s", mimetype_from_extension)
# Strategy: Prefer content-based detection, but use extension if:
# 1. Content detection returns generic types (application/octet-stream, text/plain)
# 2. Content detection fails or returns None
# 3. Extension detection provides a more specific type
# Generic/unreliable MIME types that we should try to improve
generic_types = {
"application/octet-stream",
"application/x-ole-storage", # used by .xls, .doc and .ppt
"application/zip",
"text/plain",
}
# If content detection gives us a generic type and we have extension info
if mimetype_from_content in generic_types and mimetype_from_extension:
# Use extension-based detection if it's more specific
if mimetype_from_extension not in generic_types:
return mimetype_from_extension
# If content detection failed, returned None or is a generic type, use extension if available
if not mimetype_from_content or mimetype_from_content in generic_types:
if mimetype_from_extension:
return mimetype_from_extension
# Default to content-based detection (most reliable)
return mimetype_from_content or "application/octet-stream"