diff --git a/backend/app/api/notes.py b/backend/app/api/notes.py
index edf6130..b93360e 100644
--- a/backend/app/api/notes.py
+++ b/backend/app/api/notes.py
@@ -9,7 +9,7 @@ from app.core.database import get_session
from app.core.redis import enqueue
from app.models.notes import NoteItem, NoteAttachment
from app.schemas.notes import NoteCreate, NoteUpdate, NoteResponse
-from app.services.media import save_image, save_audio, delete_media, ALLOWED_IMAGE_TYPES, ALLOWED_AUDIO_TYPES
+from app.services.media import save_image, save_audio, delete_media, ALLOWED_IMAGE_TYPES, ALLOWED_AUDIO_PREFIXES
router = APIRouter()
@@ -130,10 +130,11 @@ async def add_attachment(
if not note:
raise HTTPException(404, "Note introuvable")
- if file.content_type in ALLOWED_IMAGE_TYPES:
+ ct = (file.content_type or "").lower().split(";")[0].strip()
+ if ct in ALLOWED_IMAGE_TYPES:
media = await save_image(file, context="note")
file_type = "image"
- elif file.content_type in ALLOWED_AUDIO_TYPES:
+ elif ct in ALLOWED_AUDIO_PREFIXES:
media = await save_audio(file)
file_type = "audio"
else:
diff --git a/backend/app/services/media.py b/backend/app/services/media.py
index 4872e7b..c4dfb44 100644
--- a/backend/app/services/media.py
+++ b/backend/app/services/media.py
@@ -13,7 +13,7 @@ ALLOWED_IMAGE_TYPES = {
"image/jpeg", "image/jpg", "image/png", "image/webp", "image/svg+xml",
"image/heic", "image/heif",
}
-ALLOWED_AUDIO_TYPES = {"audio/webm", "audio/mp4"}
+ALLOWED_AUDIO_PREFIXES = {"audio/webm", "audio/mp4", "audio/ogg", "audio/x-m4a"}
MAX_ORIG_SIZE = (500, 500)
@@ -70,7 +70,8 @@ async def save_image(file: UploadFile, context: str = "note") -> dict:
async def save_audio(file: UploadFile) -> dict:
- if file.content_type not in ALLOWED_AUDIO_TYPES:
+ ct = (file.content_type or "").lower().split(";")[0].strip()
+ if ct not in ALLOWED_AUDIO_PREFIXES:
raise HTTPException(status_code=400, detail=f"Format audio non supporté : {file.content_type}")
file_id = str(uuid.uuid4())
diff --git a/frontend/src/pages/NotesPage.tsx b/frontend/src/pages/NotesPage.tsx
index a800163..1fd48db 100644
--- a/frontend/src/pages/NotesPage.tsx
+++ b/frontend/src/pages/NotesPage.tsx
@@ -41,19 +41,23 @@ function NoteCard({ note, onEdit, onDelete, onAddPhoto, onAddAudio, onDeleteAtt
async function startRecord() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
- const recorder = new MediaRecorder(stream)
+ // Choisir le format supporté par le navigateur (Safari → mp4, Chrome/Firefox → webm)
+ const mimeType = ['audio/webm', 'audio/mp4', 'audio/ogg'].find(t => MediaRecorder.isTypeSupported(t)) ?? ''
+ const recorder = mimeType ? new MediaRecorder(stream, { mimeType }) : new MediaRecorder(stream)
chunksRef.current = []
recorder.ondataavailable = e => { if (e.data.size > 0) chunksRef.current.push(e.data) }
recorder.onstop = () => {
stream.getTracks().forEach(t => t.stop())
- const blob = new Blob(chunksRef.current, { type: 'audio/webm' })
- onAddAudio(new File([blob], 'enregistrement.webm', { type: 'audio/webm' }))
+ const type = recorder.mimeType || mimeType || 'audio/webm'
+ const ext = type.includes('mp4') ? 'm4a' : 'webm'
+ const blob = new Blob(chunksRef.current, { type })
+ onAddAudio(new File([blob], `enregistrement.${ext}`, { type }))
}
recorder.start()
recorderRef.current = recorder
setRecording(true)
} catch {
- // micro non disponible
+ // micro non disponible ou permission refusée
}
}
@@ -121,7 +125,7 @@ function NoteCard({ note, onEdit, onDelete, onAddPhoto, onAddAudio, onDeleteAtt
{note.tags.map(t => (
{t}
))}
- {note.gps_lat && 📍}
+ {note.gps_lat != null && }
{/* Actions */}