Files
verdande/tools/apple-notes-til-markdown.sh
T

140 lines
5.4 KiB
Bash
Raw Normal View History

#!/bin/bash
#
# Apple Noter → en mappe med Markdown-filer.
#
# Apple har ingen eksport, der er værd at bruge: "Eksportér som PDF" er et billede
# af en note, ikke noten. Det her læser Notes.app gennem AppleScript, som er den
# eneste understøttede vej ind, og skriver én .md-fil pr. note.
#
# Det kører på din egen maskine og sender ingenting nogen steder. Resultatet er en
# mappe, du kan kigge igennem, før du beslutter dig — og et zip af den mappe er
# præcis det, Verdande importerer.
#
# ./apple-notes-til-markdown.sh ~/Desktop/noter # alle
# ./apple-notes-til-markdown.sh ~/Desktop/noter 10 # de første ti
# cd ~/Desktop && zip -r noter.zip noter
# → Indstillinger → Data → Importér noter
#
# Første gang spørger macOS, om Terminal må styre Noter. Det skal den.
set -euo pipefail
ud="${1:-$HOME/Desktop/apple-noter}"
graense="${2:-0}" # 0 betyder alle
mkdir -p "$ud"
echo "Læser Noter. Første gang kan det tage et minut, før macOS svarer."
# Én note ad gangen frem for at bygge én kæmpe streng: AppleScript bliver meget
# langsomt på lange strenge, og på et bibliotek af nogen størrelse ser det ud som
# om den er gået i stå.
antal=$(osascript -e 'tell application "Notes" to return count of notes')
echo "Fandt $antal noter."
# En prøvekørsel først er en god idé: konverteringen taber Apple Noters
# formatering, og det er billigere at opdage på ti noter end på tolv hundrede.
if [ "$graense" -gt 0 ] && [ "$graense" -lt "$antal" ]; then
antal="$graense"
echo "Tager de første $antal."
fi
i=1
while [ "$i" -le "$antal" ]; do
titel=$(osascript -e "tell application \"Notes\" to return name of note $i" 2>/dev/null || echo "")
krop=$(osascript -e "tell application \"Notes\" to return body of note $i" 2>/dev/null || echo "")
if [ -z "$titel" ]; then
titel="uden titel $i"
fi
# Tegn, der ikke kan være i et filnavn på de tre systemer, folk bruger.
fil=$(printf '%s' "$titel" | tr '/\\:*?"<>|' '-')
sti="$ud/$fil.md"
n=2
while [ -e "$sti" ]; do
sti="$ud/$fil ($n).md"
n=$((n + 1))
done
# Noter er HTML indeni. textutil følger med macOS og kan lave det om til ren
# tekst; overskrifter og fed går tabt, men ordene og linjerne overlever, og det
# er dem, man kommer efter. Titlen sættes som første linje, fordi det er sådan
# Verdande navngiver en note.
#
# To ting, en prøvekørsel på ti noter afslørede med det samme:
#
# `-inputencoding UTF-8` — uden den gætter textutil på inddataets kodning og
# gætter forkert, så "Ansøgning" kommer ud som "Ansøgning". Det er den slags,
# der ser ud som en enkelt underlig note, indtil man opdager, at det er alle
# noter med et dansk bogstav i.
#
# Og kroppen af en Apple-note begynder med notens titel. Skriver man en
# overskrift ovenover, står titlen to gange i hver eneste note.
tekst=$(printf '%s' "$krop" \
| textutil -stdin -stdout -format html -inputencoding UTF-8 -convert txt -encoding UTF-8 2>/dev/null \
|| true)
# Apples lister kommer ud som tabulator, tegn, tabulator — "\t•\tPulp Fiction".
# Det er en liste for et menneske og ingenting for en Markdown-læser, så en
# huskeliste ville ankomme som en klump linjer. Punkttegnene bliver til "- " og
# tallene til "1. ", som er det, alt andet i verden læser som en liste.
#
# Perl og ikke sed: punkttegnet er flerbyte, og BSD sed hakkede det midt over,
# så hver linje begyndte med to halve tegn. -CSD siger, at ind- og uddata er
# UTF-8, hvilket er hele forskellen.
tekst=$(printf '%s' "$tekst" | perl -CSD -pe '
s/^\t+(\d+)[.)]\t*/$1. /; # nummereret liste
s/^\t+[^\t]{1,3}\t+/- /; # punkt- eller stregliste
')
foerste=$(printf '%s' "$tekst" | head -1)
if [ "$foerste" = "$titel" ]; then
tekst=$(printf '%s' "$tekst" | tail -n +2)
fi
{
printf '# %s\n' "$titel"
printf '%s' "$tekst"
} > "$sti"
# Billeder og bilag.
#
# En note fra Apple Noter er ofte fuld af fotografier, scanninger og indsatte
# skærmbilleder, og en note, der kommer over som ord alene, er ikke den note —
# den er et referat af den. AppleScript kan gemme hver vedhæftning som en fil;
# de lægges i en undermappe, og der skrives en henvisning til hver i bunden af
# noten, så Verdande kan hægte dem på ved importen.
bilag=$(osascript -e "tell application \"Notes\" to return count of attachments of note $i" 2>/dev/null || echo 0)
if [ "${bilag:-0}" -gt 0 ]; then
mkdir -p "$ud/vedhaeftninger"
j=1
while [ "$j" -le "$bilag" ]; do
navn=$(osascript -e "tell application \"Notes\" to return name of attachment $j of note $i" 2>/dev/null || echo "")
[ -z "$navn" ] && navn="bilag-$i-$j"
# Samme rensning som titlen: et skråstreg i et filnavn laver en mappe.
navn=$(printf '%s' "$navn" | tr '/\\:*?"<>|' '-')
mål="$ud/vedhaeftninger/$i-$j-$navn"
# `save` skriver filen. Nogle vedhæftninger — links til websider, kort —
# har ingen fil bag sig og fejler her; de springes over frem for at
# stoppe hele kørslen.
if osascript -e "tell application \"Notes\" to save attachment $j of note $i in POSIX file \"$mål\"" >/dev/null 2>&1; then
printf '\n![](vedhaeftninger/%s)\n' "$(basename "$mål")" >> "$sti"
fi
j=$((j + 1))
done
fi
if [ $((i % 25)) -eq 0 ]; then
echo " $i / $antal"
fi
i=$((i + 1))
done
echo
echo "Færdig. $antal noter ligger i:"
echo " $ud"
echo
echo "Kig dem igennem, og pak dem så:"
echo " cd \"$(dirname "$ud")\" && zip -r \"$(basename "$ud").zip\" \"$(basename "$ud")\""