Sauvegarder tous ses mémos vocaux Apple en une commande

Ressources


L’application Dictaphone sait exporter un enregistrement vers le Finder, mais Apple ne documente pas de commande pour sauvegarder toute la bibliothèque avec ses titres et ses dates.

Quand il y a quelques mémos, le glisser-déposer suffit. Quand il y en a des dizaines ou des centaines, une exportation reproductible devient plus utile.

Pourquoi une simple copie ne suffit pas

Sur macOS 26, les données de Dictaphone se trouvent actuellement dans ce dossier :

~/Library/Group Containers/group.com.apple.VoiceMemos.shared/Recordings/

Les fichiers audio portent généralement un identifiant plutôt que le titre affiché dans l’application. Les métadonnées utiles se trouvent dans la base SQLite CloudRecordings.db.

Une tentative d’accès depuis le terminal peut toutefois retourner Operation not permitted ou authorization denied. Ce n’est pas un problème corrigé par sudo. macOS protège les données des autres applications avec TCC, et le terminal doit disposer de l’Accès complet au disque.

Autoriser temporairement le terminal

  1. Ouvre Réglages Système, puis Confidentialité et sécurité.
  2. Ouvre Accès complet au disque.
  3. Ajoute ton terminal, par exemple Terminal, iTerm ou Ghostty.
  4. Quitte complètement le terminal avec Cmd+Q, puis rouvre-le.

Cette permission donne un accès très large. Elle peut être retirée après l’export.

Le script

Le script suivant copie les enregistrements sans supprimer ni modifier les originaux. Il retrouve les titres quand le schéma local le permet, restaure les dates et conserve sous leur nom technique les fichiers qui ne figurent pas dans la base.

Enregistre-le dans export-voice-memos.sh, puis exécute chmod +x export-voice-memos.sh.

#!/usr/bin/env bash
set -uo pipefail

SRC="$HOME/Library/Group Containers/group.com.apple.VoiceMemos.shared/Recordings"
DEST="$HOME/Downloads/VoiceMemos"
TO_MP3=0

say() { printf '%s\n' "$*"; }
die() { printf 'Erreur : %s\n' "$*" >&2; exit 1; }

while [ $# -gt 0 ]; do
  case "$1" in
    -o|--output)
      [ $# -ge 2 ] || die "dossier manquant après $1"
      DEST="$2"
      shift 2
      ;;
    --mp3)
      TO_MP3=1
      shift
      ;;
    -h|--help)
      printf 'Usage: %s [-o dossier] [--mp3]\n' "$0"
      exit 0
      ;;
    *) die "option inconnue : $1" ;;
  esac
done

[ -d "$SRC" ] || die "dossier Dictaphone introuvable"
command -v sqlite3 >/dev/null 2>&1 || die "sqlite3 est requis"

if ! find "$SRC" -maxdepth 1 -type f -print -quit >/dev/null 2>&1; then
  die "accès refusé par macOS. Autorise l'Accès complet au disque pour le terminal, puis relance-le"
fi

mkdir -p "$DEST"
work_dir=$(mktemp -d "${TMPDIR:-/tmp}/voice-memos-export.XXXXXX") || die "impossible de créer le dossier temporaire"
manifest="$work_dir/exported-source-files"
: > "$manifest"

cleanup() {
  rm -f "$manifest"
  rmdir "$work_dir" 2>/dev/null || true
}
trap cleanup EXIT

slug() {
  printf '%s' "$1" \
    | iconv -f UTF-8 -t ASCII//TRANSLIT 2>/dev/null \
    | sed 's#[/\\:]#-#g' \
    | tr '\n\t' '--' \
    | sed -E 's/[^A-Za-z0-9 ._-]//g; s/^ +| +$//g; s/ /-/g'
}

copy_named() {
  source_file="$1"
  target_base="$2"
  target="$DEST/${target_base}.m4a"
  suffix=2

  while [ -e "$target" ]; do
    if cmp -s "$source_file" "$target"; then
      return 0
    fi
    target="$DEST/${target_base}_${suffix}.m4a"
    suffix=$((suffix + 1))
  done

  cp "$source_file" "$target" || return 1
  [ -n "${stamp:-}" ] && touch -t "$stamp" "$target" 2>/dev/null
  say "Copié : $(basename "$target")"
}

DB="$SRC/CloudRecordings.db"
title_col=""

if [ -f "$DB" ]; then
  for column in ZCUSTOMLABEL ZENCRYPTEDTITLE ZTITLE ZNAME; do
    if sqlite3 "$DB" "SELECT $column FROM ZCLOUDRECORDING LIMIT 1;" >/dev/null 2>&1; then
      title_col="$column"
      break
    fi
  done
fi

exported=0
fallback=0

if [ -n "$title_col" ]; then
  while IFS=$'\t' read -r label path apple_date; do
    [ -n "$path" ] || continue
    source_name=$(basename "$path")
    source_file="$SRC/$source_name"
    [ -f "$source_file" ] || continue

    day=""
    stamp=""
    case "$apple_date" in
      ''|*[!0-9.]*) ;;
      *)
        unix_date=$(awk "BEGIN { printf \"%d\", $apple_date + 978307200 }")
        day=$(date -r "$unix_date" +%Y-%m-%d 2>/dev/null || true)
        stamp=$(date -r "$unix_date" +%Y%m%d%H%M.%S 2>/dev/null || true)
        ;;
    esac

    name=$(slug "${label:-memo}")
    [ -n "$name" ] || name="memo"
    [ -n "$day" ] && name="${day}_${name}"

    if copy_named "$source_file" "$name"; then
      printf '%s\n' "$source_name" >> "$manifest"
      exported=$((exported + 1))
    fi
  done < <(
    sqlite3 -separator $'\t' "$DB" \
      "SELECT COALESCE($title_col,''), ZPATH, ZDATE
       FROM ZCLOUDRECORDING
       WHERE ZPATH IS NOT NULL;" 2>/dev/null
  )
fi

while IFS= read -r -d '' source_file; do
  source_name=$(basename "$source_file")
  if ! grep -Fqx "$source_name" "$manifest"; then
    cp -np "$source_file" "$DEST/$source_name" && fallback=$((fallback + 1))
  fi
done < <(
  find "$SRC" -maxdepth 1 -type f \
    \( -iname '*.m4a' -o -iname '*.caf' -o -iname '*.wav' \) -print0
)

if [ "$TO_MP3" -eq 1 ]; then
  command -v ffmpeg >/dev/null 2>&1 || die "ffmpeg est requis pour --mp3"
  for source_file in "$DEST"/*.m4a; do
    [ -e "$source_file" ] || continue
    ffmpeg -nostdin -loglevel error -y -i "$source_file" \
      -codec:a libmp3lame -q:a 2 "${source_file%.m4a}.mp3"
  done
fi

say "Terminé : $exported fichier(s) nommé(s), $fallback fichier(s) de secours"
say "Dossier : $DEST"

Lancer l’export

./export-voice-memos.sh
./export-voice-memos.sh -o "$HOME/Documents/Memos"
./export-voice-memos.sh --mp3

Le format obtenu est de la forme 2026-08-31_Titre-du-memo.m4a. Si deux enregistrements produisent le même nom mais n’ont pas le même contenu, un suffixe numérique évite l’écrasement.

L’option MP3 nécessite ffmpeg, installable avec brew install ffmpeg. Les fichiers M4A d’origine restent également présents dans le dossier d’export.

Limites à connaître

Le chemin et le schéma SQLite sont des détails internes de macOS. Apple peut les modifier lors d’une mise à jour. Le script cherche plusieurs noms de colonnes connus et conserve tous les fichiers audio en secours, mais il faut vérifier le résultat avant de considérer l’export comme une sauvegarde.

Certains mémos synchronisés avec iCloud peuvent aussi ne pas être téléchargés localement. Ouvre Dictaphone et laisse la synchronisation se terminer avant l’export.

Enfin, une copie sur le même Mac ne protège pas contre la panne ou le vol de la machine. Une fois le résultat vérifié, copie le dossier vers un autre disque ou vers ton système de sauvegarde habituel.