89 lines
3.2 KiB
Python
89 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|||
|
|
"""Pakker en Apple Noter-eksport i portioner, der kan importeres.
|
||
|
|
|
||
|
|
Eksporten af tolv hundrede noter fylder knap to hundrede megabyte, og næsten
|
||
|
|
alt sammen er billeder: teksten selv er under fire. Importen tager 64 MB ad
|
||
|
|
gangen, og tunnelen foran den tager mindre — så det skal deles.
|
||
|
|
|
||
|
|
Delt efter hvad en note *vejer med sine billeder*, ikke efter antal. De fleste
|
||
|
|
noter har ingen, så de fylder én portion til sammen; de få med et fotoalbum i
|
||
|
|
får hver deres. En note og dens billeder ligger altid i samme portion, for
|
||
|
|
linkene i teksten er relative, og en note, hvis billeder kom med i næste
|
||
|
|
portion, ville pege på ingenting.
|
||
|
|
|
||
|
|
./pak-noter-i-portioner.py ~/Desktop/apple-noter [MB]
|
||
|
|
"""
|
||
|
|
|
||
|
|
import os
|
||
|
|
import re
|
||
|
|
import sys
|
||
|
|
import zipfile
|
||
|
|
|
||
|
|
# Under importens 64 MB og under det, en tunnel plejer at tage. Hellere en
|
||
|
|
# portion mere end en, der ryger på målstregen.
|
||
|
|
DEFAULT_LIMIT_MB = 40
|
||
|
|
|
||
|
|
LINK = re.compile(r"!\[[^\]]*\]\((vedhaeftninger/[^)]+)\)")
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> int:
|
||
|
|
src = os.path.expanduser(sys.argv[1] if len(sys.argv) > 1 else "~/Desktop/apple-noter")
|
||
|
|
limit = int(float(sys.argv[2] if len(sys.argv) > 2 else DEFAULT_LIMIT_MB) * 1024 * 1024)
|
||
|
|
out_dir = src.rstrip("/") + "-portioner"
|
||
|
|
os.makedirs(out_dir, exist_ok=True)
|
||
|
|
|
||
|
|
notes = sorted(f for f in os.listdir(src) if f.endswith(".md"))
|
||
|
|
if not notes:
|
||
|
|
print(f"Ingen .md-filer i {src}")
|
||
|
|
return 1
|
||
|
|
|
||
|
|
# Hvad hver note vejer, og hvad den trækker med sig.
|
||
|
|
weighed = []
|
||
|
|
for name in notes:
|
||
|
|
path = os.path.join(src, name)
|
||
|
|
text = open(path, encoding="utf-8", errors="replace").read()
|
||
|
|
files = []
|
||
|
|
size = os.path.getsize(path)
|
||
|
|
for rel in LINK.findall(text):
|
||
|
|
f = os.path.join(src, rel)
|
||
|
|
if os.path.exists(f):
|
||
|
|
files.append(rel)
|
||
|
|
size += os.path.getsize(f)
|
||
|
|
weighed.append((name, files, size))
|
||
|
|
|
||
|
|
# En note, der alene er større end grænsen, får sin egen portion frem for at
|
||
|
|
# blive sprunget over: den skal med, og en portion på 25 MB er stadig en, der
|
||
|
|
# kan sendes.
|
||
|
|
batches, current, current_size = [], [], 0
|
||
|
|
for item in weighed:
|
||
|
|
if current and current_size + item[2] > limit:
|
||
|
|
batches.append(current)
|
||
|
|
current, current_size = [], 0
|
||
|
|
current.append(item)
|
||
|
|
current_size += item[2]
|
||
|
|
if current:
|
||
|
|
batches.append(current)
|
||
|
|
|
||
|
|
print(f"{len(notes)} noter → {len(batches)} portioner\n")
|
||
|
|
for i, batch in enumerate(batches, start=1):
|
||
|
|
zip_path = os.path.join(out_dir, f"noter-{i:02d}.zip")
|
||
|
|
seen = set()
|
||
|
|
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
|
||
|
|
for name, files, _ in batch:
|
||
|
|
z.write(os.path.join(src, name), name)
|
||
|
|
for rel in files:
|
||
|
|
if rel in seen:
|
||
|
|
continue
|
||
|
|
seen.add(rel)
|
||
|
|
z.write(os.path.join(src, rel), rel)
|
||
|
|
mb = os.path.getsize(zip_path) / 1024 / 1024
|
||
|
|
print(f" {os.path.basename(zip_path)} {len(batch):4d} noter {len(seen):3d} bilag {mb:6.1f} MB")
|
||
|
|
|
||
|
|
print(f"\nLigger i:\n {out_dir}")
|
||
|
|
print("\nImportér én ad gangen under Indstillinger → Data → Noter.")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
sys.exit(main())
|