Standalone Shonar Desktop: vendor portable sources + local engine; decouple from ~/Projects/Shonar
- shared/ = portable Android-origin sources vendored from deferred/desktop-server (app/build.gradle.kts srcDir repointed; PlaybackController.kt excluded as Android-only) - backend/ = bundled-lite engine (SQLite + inline queue); .venv symlinked from the old checkout, PYTHONPATH pins THIS backend's code over any editable install - repoRoot() resolves this project dir (env SHONAR_REPO still wins); desktop-dev.sh watches shared/ + backend/ - Verified: :app:compileKotlin + :app:test green (23 tests); engine boots on :8010, self-migrates, /healthz ok
This commit is contained in:
commit
76c867fca4
136 changed files with 21099 additions and 0 deletions
74
backend/shonar/services/media.py
Normal file
74
backend/shonar/services/media.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
"""Audio format validation: declared MIME type vs magic bytes.
|
||||
|
||||
Families decouple declared MIME aliases (audio/mp4 vs audio/m4a) from what
|
||||
the bytes actually are. The original file is stored exactly as uploaded —
|
||||
validation never rewrites it.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
FAMILY_BY_MIME = {
|
||||
"audio/mp4": "mp4",
|
||||
"audio/m4a": "mp4",
|
||||
"audio/aac": "aac",
|
||||
"audio/wav": "wav",
|
||||
"audio/x-wav": "wav",
|
||||
"audio/ogg": "ogg",
|
||||
"audio/opus": "ogg",
|
||||
"audio/webm": "webm",
|
||||
"audio/mpeg": "mpeg",
|
||||
}
|
||||
|
||||
EXT_BY_FAMILY = {
|
||||
"mp4": ".m4a",
|
||||
"aac": ".aac",
|
||||
"wav": ".wav",
|
||||
"ogg": ".ogg",
|
||||
"webm": ".webm",
|
||||
"mpeg": ".mp3",
|
||||
}
|
||||
|
||||
|
||||
def sniff_audio_family(data: bytes) -> str | None:
|
||||
"""Return the audio family from magic bytes, or None if unrecognized."""
|
||||
if len(data) >= 12:
|
||||
if data[4:8] == b"ftyp":
|
||||
return "mp4"
|
||||
if data[:4] == b"RIFF" and data[8:12] == b"WAVE":
|
||||
return "wav"
|
||||
if data[:4] == b"OggS":
|
||||
return "ogg"
|
||||
if data[:4] == b"\x1a\x45\xdf\xa3":
|
||||
return "webm"
|
||||
if data[:3] == b"ID3":
|
||||
return "mpeg"
|
||||
if len(data) >= 2 and data[0] == 0xFF and (data[1] & 0xF6) == 0xF0:
|
||||
# ADTS frame sync: AAC (also accepted as mpeg-family audio)
|
||||
return "aac"
|
||||
return None
|
||||
|
||||
|
||||
def declared_family(mime_type: str) -> str | None:
|
||||
return FAMILY_BY_MIME.get(mime_type.lower().split(";")[0].strip())
|
||||
|
||||
|
||||
def is_compatible(mime_type: str, data: bytes) -> bool:
|
||||
"""True when the declared MIME matches the sniffed magic bytes.
|
||||
|
||||
mpeg and aac are treated as one family: Android records AAC in ADTS or
|
||||
in MP4 containers and MIME reporting around these is inconsistent.
|
||||
"""
|
||||
declared = declared_family(mime_type)
|
||||
sniffed = sniff_audio_family(data)
|
||||
if declared is None or sniffed is None:
|
||||
return False
|
||||
if {declared, sniffed} == {"mpeg", "aac"}:
|
||||
return True
|
||||
return declared == sniffed
|
||||
|
||||
|
||||
def extension_for(mime_type: str, data: bytes) -> str:
|
||||
sniffed = sniff_audio_family(data)
|
||||
if sniffed is not None:
|
||||
return EXT_BY_FAMILY[sniffed]
|
||||
return EXT_BY_FAMILY.get(declared_family(mime_type) or "", ".bin")
|
||||
Loading…
Add table
Add a link
Reference in a new issue