167 lines
3.8 KiB
Python
167 lines
3.8 KiB
Python
"""
|
|
Media type detection and handling utilities.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
import mimetypes
|
|
|
|
# Initialize mimetypes database
|
|
mimetypes.init()
|
|
|
|
# Media type categories
|
|
VIDEO_TYPES = {"video/mp4", "video/webm", "video/quicktime", "video/x-msvideo", "video/avi"}
|
|
IMAGE_TYPES = {"image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml"}
|
|
AUDIO_TYPES = {"audio/mpeg", "audio/wav", "audio/ogg", "audio/flac", "audio/aac", "audio/mp3"}
|
|
|
|
# File extension mappings
|
|
EXTENSION_TO_CATEGORY = {
|
|
# Video
|
|
".mp4": "video",
|
|
".webm": "video",
|
|
".mov": "video",
|
|
".avi": "video",
|
|
".mkv": "video",
|
|
# Image
|
|
".jpg": "image",
|
|
".jpeg": "image",
|
|
".png": "image",
|
|
".gif": "image",
|
|
".webp": "image",
|
|
".svg": "image",
|
|
# Audio
|
|
".mp3": "audio",
|
|
".wav": "audio",
|
|
".ogg": "audio",
|
|
".flac": "audio",
|
|
".aac": "audio",
|
|
".m4a": "audio",
|
|
}
|
|
|
|
|
|
def detect_media_type(path: Path) -> str:
|
|
"""
|
|
Detect the media category for a file.
|
|
|
|
Args:
|
|
path: Path to the file
|
|
|
|
Returns:
|
|
Category string: "video", "image", "audio", or "unknown"
|
|
"""
|
|
if not path:
|
|
return "unknown"
|
|
|
|
# Try extension first
|
|
ext = path.suffix.lower()
|
|
if ext in EXTENSION_TO_CATEGORY:
|
|
return EXTENSION_TO_CATEGORY[ext]
|
|
|
|
# Try mimetypes
|
|
mime_type, _ = mimetypes.guess_type(str(path))
|
|
if mime_type:
|
|
if mime_type in VIDEO_TYPES or mime_type.startswith("video/"):
|
|
return "video"
|
|
if mime_type in IMAGE_TYPES or mime_type.startswith("image/"):
|
|
return "image"
|
|
if mime_type in AUDIO_TYPES or mime_type.startswith("audio/"):
|
|
return "audio"
|
|
|
|
return "unknown"
|
|
|
|
|
|
def get_mime_type(path: Path) -> str:
|
|
"""
|
|
Get the MIME type for a file.
|
|
|
|
Args:
|
|
path: Path to the file
|
|
|
|
Returns:
|
|
MIME type string or "application/octet-stream"
|
|
"""
|
|
mime_type, _ = mimetypes.guess_type(str(path))
|
|
return mime_type or "application/octet-stream"
|
|
|
|
|
|
def get_media_extension(media_type: str) -> str:
|
|
"""
|
|
Get the typical file extension for a media type.
|
|
|
|
Args:
|
|
media_type: Media category or MIME type
|
|
|
|
Returns:
|
|
File extension with dot (e.g., ".mp4")
|
|
"""
|
|
if media_type == "video":
|
|
return ".mp4"
|
|
if media_type == "image":
|
|
return ".png"
|
|
if media_type == "audio":
|
|
return ".mp3"
|
|
|
|
# Try as MIME type
|
|
ext = mimetypes.guess_extension(media_type)
|
|
return ext or ""
|
|
|
|
|
|
def is_streamable(path: Path) -> bool:
|
|
"""
|
|
Check if a file type is streamable (video/audio).
|
|
|
|
Args:
|
|
path: Path to the file
|
|
|
|
Returns:
|
|
True if the file can be streamed
|
|
"""
|
|
media_type = detect_media_type(path)
|
|
return media_type in ("video", "audio")
|
|
|
|
|
|
def needs_conversion(path: Path, target_format: str = "mp4") -> bool:
|
|
"""
|
|
Check if a video file needs format conversion.
|
|
|
|
Args:
|
|
path: Path to the file
|
|
target_format: Target format (default mp4)
|
|
|
|
Returns:
|
|
True if conversion is needed
|
|
"""
|
|
media_type = detect_media_type(path)
|
|
if media_type != "video":
|
|
return False
|
|
|
|
ext = path.suffix.lower().lstrip(".")
|
|
return ext != target_format
|
|
|
|
|
|
def get_video_src(
|
|
content_hash: str,
|
|
original_path: Optional[Path] = None,
|
|
is_ios: bool = False,
|
|
) -> str:
|
|
"""
|
|
Get the appropriate video source URL.
|
|
|
|
For iOS devices, prefer MP4 format.
|
|
|
|
Args:
|
|
content_hash: Content hash for the video
|
|
original_path: Optional original file path
|
|
is_ios: Whether the client is iOS
|
|
|
|
Returns:
|
|
URL path for the video source
|
|
"""
|
|
if is_ios:
|
|
return f"/cache/{content_hash}/mp4"
|
|
|
|
if original_path and original_path.suffix.lower() in (".mp4", ".webm"):
|
|
return f"/cache/{content_hash}/raw"
|
|
|
|
return f"/cache/{content_hash}/mp4"
|