25_StirlingPDF
RCH-20260822-0001 — Consommation mémoire de StirlingPDF à la compression d'un PDF de 129 Mo (validation de la chaîne Komga-PDF)
| Date | 2026-08-22 |
|---|---|
| Service | StirlingPDF |
| Contexte | Test de bout en bout de la chaîne automatisée Komga-PDF depuis le poste Windows julie, entré dans le maillage Syncthing le 2026-08-10. Objectif secondaire : mesurer la marge réelle sous le plafond mémoire de 2 Go posé le 2026-08-02, jamais éprouvé sur un gros fichier. |
| Outils utilisés | plink SSH, journalctl -u komga-watch, docker stats stirling-pdf, API /api/v1/misc/compress-pdf (appelée par komga_compress_vps.py) |
| Requête | Dépôt d'un PDF de 129,2 Mo dans D:\Syncthing\komga-pdf\ ; relevé de docker stats stirling-pdf toutes les 15 s jusqu'à la fin du traitement. |
Résultats
Chaîne validée sans intervention manuelle : Syncthing dépose le fichier sur le VPS à 11:24:50 UTC, inotify déclenche, le script détecte le PDF à 11:24:53 après l'attente de 3 s, StirlingPDF rend le fichier compressé à 11:26:40 — 1 min 50 s au total, retour du fichier sur le poste julie dans la foulée. Compression : 129,2 Mo → 55,4 Mo, soit -57 %, taux identique à celui du numéro de juin 2026 du même magazine. Consommation du container : 701 Mio à t+15 s, 1001 Mio à t+30 s, pic à 1,069 Gio à t+60 s, puis redescente et stabilisation vers 1,04 Gio jusqu'à la fin.
Points clés
- Pic à 1,069 Gio sur le plafond de 2 Go : il reste environ 45 % de marge. Le plafond de 1 Go initialement envisagé le 2026-08-02 aurait provoqué un OOM kill sur ce traitement.
- La décision de porter la limite à 2 Go se trouve validée une seconde fois, pour une raison indépendante de celle qui l'avait motivée : elle avait été prise pour l'OCR (pic à ~894-969 Mio sur une page A4 300 dpi), elle sert ici à la compression d'un gros PDF.
- La consommation ne suit pas linéairement la taille du fichier : elle plafonne vers 1 Gio en une minute et s'y maintient. Le fichier de 129 Mo est pourtant le plus gros passé dans la chaîne à ce jour (précédent : 96,6 Mo le 2026-06-13).
- Une compression et un OCR lancés en parallèle dépasseraient les 2 Go. Ne pas enchaîner les deux simultanément, et ne pas redescendre la limite.
- Le taux de compression est reproductible d'un numéro à l'autre pour une même source éditoriale.
Limites
Les logs de komga-watch partent uniquement dans journald, qui a été tourné depuis le démarrage du service le 2026-07-08 : tous les traitements de juin et juillet sont irrécupérables, journalctl ne montrait rien avant ce test. Aucun historique consultable des compressions passées. Un seul fichier mesuré, donc pas de courbe taille/mémoire — le point à 129 Mo est isolé. La qualité visuelle du PDF compressé n'a pas été contrôlée dans le cadre de ce test.
Localisation des ressources
Procédure complète : page BookStack 220. Scripts VPS : /home/debian/komga_compress_vps.py et /home/debian/komga_watch_vps.sh. Service : komga-watch.service (systemd, actif depuis le 2026-07-08, PID 690). Dépôt : D:\Syncthing\komga-pdf\ → /home/debian/Documents/komga-pdf/. Sortie : /home/debian/Documents/komga-compressed/. Stack StirlingPDF : ID 89, container stirling-pdf, JAVA_TOOL_OPTIONS=-Xmx512m, limits.memory=2g. Fichier de test : La Revue du Vin de France - Septembre 2026.pdf.
Tags
#StirlingPDF #compression #memoire #komga-pdf #Syncthing #VPS
Suite / Pistes
1/ Ajouter une redirection des logs vers un fichier dans komga_watch_vps.sh, pour conserver un historique des compressions au-delà de la rotation journald.
2/ Mesurer un fichier nettement plus gros (200 Mo et au-delà) pour savoir si le plateau de 1 Gio tient ou si la consommation repart.
3/ Contrôler la qualité visuelle du PDF compressé avant de généraliser le taux de -57 % comme acceptable sur ce type de source.
RCH-20260802-0001 — Echec OCR sur images PNG — deux refus cumules d'ocrmypdf (canal alpha + DPI absent)
| Date | 2026-08-02 |
|---|---|
| Service | StirlingPDF |
| Contexte | Julien signale une erreur HTTP 500 lors d'un OCR sur une image PNG via StirlingPDF. L'interface web n'affiche aucun detail exploitable, le diagnostic passe par les logs du container. |
| Outils utilisés | docker logs stirling-pdf, API /api/v1/misc/ocr-pdf, Pillow, tesseract --list-langs |
| Requête | POST /api/v1/misc/ocr-pdf (multipart fileInput, languages=[fra], ocrType=Force-OCR) sur PNG RGBA, puis sur variantes aplaties et converties, avec mesure docker stats en parallele. |
Résultats
Deux erreurs distinctes et cumulees dans ocrmypdf 17.4.0, revelees seulement dans les logs : (1) UnsupportedImageFormatError — The input image has an alpha channel ; (2) une fois l'alpha retire, DpiError — Input file is an image, but has no resolution (DPI) in its metadata. StirlingPDF ne transmet pas l'option --image-dpi a ocrmypdf, donc tout PNG sans DPI dans ses metadonnees echoue. Deux contournements valides en test : voie interface web (Convert > Image to PDF puis OCR sur le PDF, HTTP 200) et voie scriptee (aplatissement de l'alpha sur fond blanc + DPI force a 300, HTTP 200). Texte reconnu restitue a l'identique sur une page de test en francais. Mesure memoire : pic a 894 Mio pour une seule page A4 300 dpi.
Points clés
- L'interface web ne remonte pas la cause : le diagnostic exige docker logs stirling-pdf
- Les deux erreurs sont sequentielles — corriger l'alpha seul ne suffit pas, le DPI bloque ensuite
- Convertir l'image en PDF avant l'OCR neutralise les deux problemes sans outil local
- 6 langues tesseract installees : fra, eng, deu, por, chi_sim, osd
- ocrmypdf et tesseract sont des process Python hors JVM : -Xmx ne les borne pas, seul le plafond cgroup du container s'applique
- La limite de 1 Go prevue pour l'allegement du 2026-08-02 aurait provoque un OOM kill pendant l'OCR — portee a 2 Go
Limites
Mesure realisee sur une page A4 300 dpi generee synthetiquement ; un document multi-pages ou un scan haute resolution consommera davantage. Le pic de 894 Mio est un plancher, pas un maximum.
Localisation des ressources
| URLs | ["https://spdf.juxjux.ovh", "https://spdf.juxjux.ovh/swagger-ui/index.html"] |
|---|---|
| Chemins | ["D:\\Syncthing\\Jux_univers\\Jux-scripts\\Stirling-OCR\\ocr_image.py", "D:\\Syncthing\\Jux_univers\\Jux-scripts\\Stirling-OCR\\README.md", "/home/debian/stirling-compose-new.yml", "/home/debian/stirling-compose-new.yml.orig1g"] |
| IDs | ["stack Portainer 89", "container stirling-pdf", "page BookStack 211"] |
| Auteurs / Source | — |
| Métadonnées | {"ocrmypdf": "17.4.0", "tesseract": "5.3.4", "endpoint_ocr": "POST /api/v1/misc/ocr-pdf", "pic_memoire_ocr": "894 MiB / page A4 300 dpi", "limite_memoire_retenue": "2g"} |
Tags
#StirlingPDF #OCR #ocrmypdf #tesseract #PNG #alpha #DPI #memoire #Docker
Suite / Pistes
Verifier apres l'application du compose allege (2026-08-02 19h) que compress-pdf ET l'OCR fonctionnent toujours avec memory=2g et -Xmx512m.
RCH-20260530-0001 — Mise en place API StirlingPDF pour compression PDF automatisée (workflow geo_loud)
| Date | 2026-05-30 |
|---|---|
| Service | StirlingPDF |
| Contexte | Étape 3 du workflow geo_loud : automatiser la compression des 127 PDFs lourds (>75 Mo) déplacés dans \\NASMAISON\foxy\geo_loud\. Objectif : appeler l'API StirlingPDF depuis le PC, écraser chaque fichier avec sa version compressée, mettre à jour la DB SQLite. Fichier de test : 000209_L'inde.pdf (314 Mo). |
| Outils utilisés | StirlingPDF API REST (JWT Bearer) | curl.exe | plink (PuTTY) pour SSH nginx | Python 3.14 + requests | SQLite nas_geographie.db |
| Requête API | POST https://spdf.juxjux.ovh/api/v1/misc/compress-pdf — multipart/form-data champ fileInput, header Authorization: Bearer <token> |
Résultats
Auth JWT résolue. nginx corrigé (client_max_body_size 500M). Test de compression réussi sur 000209_L'inde.pdf : 314 Mo → 311.7 Mo (0.7% de gain — PDF déjà optimisé en images JPEG). Script nas_geo_compress.py opérationnel pour traitement en batch de tous les fichiers.
Points clés
- Auth imposée : malgré
DOCKER_ENABLE_SECURITY=falsedans la stack Docker, StirlingPDF impose un compte admin au premier lancement. Compte créé : admin / Motdepasse18! - Endpoint de login :
POST /api/v1/auth/loginavec body JSON{"username":"admin","password":"Motdepasse18!"}→ réponse : JWT dans.session.access_token, valable 24h - Utilisation du token : header
Authorization: Bearer <token>sur chaque appel API - nginx : client_max_body_size par défaut = 1 Mo → erreur 413 sur gros fichiers. Correction : ajout de
client_max_body_size 500M;dans/etc/nginx/sites-enabled/spdf.juxjux.ovh(via plink/SSH), puissudo nginx -s reload - Endpoint compression :
POST /api/v1/misc/compress-pdf— multipart avec champfileInput— retourne le PDF compressé directement dans le corps de la réponse (binary) - Gain variable : PDFs avec images JPEG déjà compressées → gain faible (<1%). PDFs texte/vectoriel ou images PNG → gain potentiellement important (30–70%)
- PowerShell inadapté aux gros fichiers : concaténation de byte arrays → OutOfMemoryException sur 314 Mo. Utiliser curl.exe ou Python requests
- Spec API :
/v3/api-docsretourne le HTML de la SPA React. Documentation disponible dans la Swagger UI interactive :https://spdf.juxjux.ovh/swagger-ui/index.html(après login)
Limites
- Token JWT expire après 24h — pour des batchs très longs, prévoir un re-login automatique
- La compression StirlingPDF n'expose pas de paramètre de niveau dans cette version — niveau par défaut uniquement
- Le fichier transite en RAM sur le VPS pendant le traitement — fichiers >500 Mo potentiellement problématiques selon la RAM disponible
Script nas_geo_compress.py
Emplacement : C:\Users\eliob\.claude\nas_geo_compress.py
Lancement : py C:\Users\eliob\.claude\nas_geo_compress.py
Dépendances : pip install requests (sqlite3 et os natifs Python)
Le script ajoute automatiquement les colonnes taille_compresse_octets et date_compression à la table fichiers si elles n'existent pas encore.
"""
Étape 3 du workflow geo_loud : compression PDF via StirlingPDF.
- Lit les fichiers statut='deplace' dans nas_geographie.db
- Envoie chaque PDF à l'API StirlingPDF
- Écrase le fichier d'origine avec la version compressée
- Met à jour la DB (taille_compresse_octets, date_compression)
"""
import sqlite3
import requests
import os
from datetime import datetime
DB_PATH = r"C:\Users\eliob\Documents\nas_geographie.db"
GEO_LOUD = r"\\NASMAISON\foxy\geo_loud"
STIRLING_URL = "https://spdf.juxjux.ovh"
STIRLING_USER = "admin"
STIRLING_PASS = "Motdepasse18!"
def get_token():
r = requests.post(
f"{STIRLING_URL}/api/v1/auth/login",
json={"username": STIRLING_USER, "password": STIRLING_PASS},
timeout=30,
)
r.raise_for_status()
return r.json()["session"]["access_token"]
def init_db_columns(conn):
cur = conn.cursor()
cols = {row[1] for row in cur.execute("PRAGMA table_info(fichiers)")}
if "taille_compresse_octets" not in cols:
cur.execute("ALTER TABLE fichiers ADD COLUMN taille_compresse_octets INTEGER")
if "date_compression" not in cols:
cur.execute("ALTER TABLE fichiers ADD COLUMN date_compression TEXT")
conn.commit()
def get_pending(conn):
cur = conn.cursor()
cur.execute("""
SELECT id, nom, chemin_geo_loud, taille_octets
FROM fichiers
WHERE statut = 'deplace'
AND extension IN ('.pdf', '.PDF')
AND (date_compression IS NULL)
ORDER BY taille_octets DESC
""")
return cur.fetchall()
def compress_file(token, filepath):
with open(filepath, "rb") as f:
r = requests.post(
f"{STIRLING_URL}/api/v1/misc/compress-pdf",
headers={"Authorization": f"Bearer {token}"},
files={"fileInput": (os.path.basename(filepath), f, "application/pdf")},
timeout=600,
stream=True,
)
r.raise_for_status()
return r.content
def update_db(conn, file_id, taille_compresse):
conn.execute("""
UPDATE fichiers
SET taille_compresse_octets = ?, date_compression = ?
WHERE id = ?
""", (taille_compresse, datetime.now().isoformat(), file_id))
conn.commit()
def fmt_mo(octets):
return f"{octets / 1_048_576:.1f} Mo"
def main():
print("Connexion à StirlingPDF...")
token = get_token()
print("Token JWT OK")
conn = sqlite3.connect(DB_PATH)
init_db_columns(conn)
pending = get_pending(conn)
print(f"{len(pending)} fichier(s) PDF à compresser\n")
if not pending:
print("Rien à faire.")
conn.close()
return
total_avant = sum(r[3] for r in pending)
total_apres = 0
ok = 0
errors = []
for i, (file_id, nom, chemin_geo_loud, taille_avant) in enumerate(pending, 1):
filepath = chemin_geo_loud or os.path.join(GEO_LOUD, nom)
if not os.path.exists(filepath):
print(f"[{i}/{len(pending)}] ABSENT {nom}")
errors.append((nom, "fichier absent"))
continue
taille_reel_avant = os.path.getsize(filepath)
print(f"[{i}/{len(pending)}] {nom}")
print(f" avant : {fmt_mo(taille_reel_avant)}", end=" ", flush=True)
try:
compressed = compress_file(token, filepath)
taille_apres_compression = len(compressed)
gain = (taille_reel_avant - taille_apres_compression) / taille_reel_avant * 100
tmp = filepath + ".tmp"
with open(tmp, "wb") as f:
f.write(compressed)
os.replace(tmp, filepath)
update_db(conn, file_id, taille_apres_compression)
total_apres += taille_apres_compression
ok += 1
print(f"-> après : {fmt_mo(taille_apres_compression)} (gain {gain:.1f}%)")
except Exception as e:
print(f"-> ERREUR : {e}")
errors.append((nom, str(e)))
tmp = filepath + ".tmp"
if os.path.exists(tmp):
os.remove(tmp)
conn.close()
print(f"\n{'='*50}")
print(f"Terminé : {ok}/{len(pending)} compressés")
if ok:
gain_total = (total_avant - total_apres) / total_avant * 100
print(f"Volume avant : {fmt_mo(total_avant)}")
print(f"Volume après : {fmt_mo(total_apres)}")
print(f"Gain total : {gain_total:.1f}% ({fmt_mo(total_avant - total_apres)} récupérés)")
if errors:
print(f"\nErreurs ({len(errors)}) :")
for nom, msg in errors:
print(f" {nom} : {msg}")
if __name__ == "__main__":
main()
Localisation des ressources
| URLs | https://spdf.juxjux.ovh (UI) | https://spdf.juxjux.ovh/swagger-ui/index.html (API docs, après login) |
|---|---|
| Chemins | Script : C:\Users\eliob\.claude\nas_geo_compress.py | DB : C:\Users\eliob\Documents\nas_geographie.db | Fichiers source : \\NASMAISON\foxy\geo_loud\ | nginx vhost : /etc/nginx/sites-enabled/spdf.juxjux.ovh |
| IDs | Stack Portainer : stirlingpdf (ID 89) | Container : stirling-pdf | Port interne : 8090 |
| Métadonnées | {"fichiers_a_traiter": 127, "volume_total_go": 13.577, "test_fichier": "000209_L-inde.pdf", "test_avant_mo": 314, "test_apres_mo": 311.7, "test_gain_pct": 0.7, "nginx_max_body": "500M", "token_duree_h": 24} |
Tags
#StirlingPDF #compression #PDF #geo_loud #API #JWT #nginx #Python #NAS #workflow
Suite / Pistes
1/ Lancer le script sur l'ensemble des 127 fichiers — surveiller le gain réel par catégorie de PDF.
2/ Mettre à jour la page BookStack 210 avec les colonnes DB ajoutées (taille_compresse_octets, date_compression) et le bilan de compression une fois le batch terminé.
3/ Si le gain moyen est insuffisant, explorer d'autres niveaux de compression StirlingPDF ou un outil alternatif (Ghostscript direct).
4/ Après compression de tous les fichiers : lancer l'étape 4 (restauration) via nas_geo_loud.py restaurer.
No comments to display
No comments to display