Hello all,
I have about 260 pdf i’d like to upload to a single page using editor cards.
So far, I can either successfully upload a pdf, or create an (empty) editor card, but I can’t create an editor card and feed it the pdf and description I want.
Here’s the code I have today:
import jwt # PyJWT library
import requests
import datetime
import json
import os
import re
# Configuration Ghost API
GHOST_ADMIN_API_KEY = "YOUR_ADMIN_API_KEY" # Format: key-id:secret
GHOST_ADMIN_URL = "https://mysite.ghost.io/ghost/api/admin"
UPLOAD_ENDPOINT = f"{GHOST_ADMIN_URL}/files/upload/"
PAGE_ENDPOINT = f"{GHOST_ADMIN_URL}/pages/"
PDF_FILE_PATH = "your_file.pdf" # Change this to the actual file path
PAGE_SLUG = "telechargement-des-numeros"
def create_jwt():
"""Crée un JWT valide pour l'authentification Ghost Admin API"""
key_id, secret = GHOST_ADMIN_API_KEY.split(":")
secret_bytes = bytes.fromhex(secret) # Convert secret to bytes
now = datetime.datetime.now(datetime.timezone.utc)
payload = {
"iat": int(now.timestamp()), # Issued At
"exp": int((now + datetime.timedelta(minutes=5)).timestamp()), # Expiry (5 min)
"aud": "/admin/" # Audience doit être "/admin/"
}
token = jwt.encode(payload, secret_bytes, algorithm="HS256", headers={"kid": key_id})
print("🔄 JWT généré avec succès.")
return token
def extract_description(file_name):
"""Extrait la description après la date (JJ mois AAAA)"""
match = re.search(r"\(\d{1,2} \w+ \d{4}\) - (.+)\.pdf$", file_name)
if match:
description = match.group(1)
description = description.replace("♦", "-") # Remplace les symboles inutiles
return description.strip()
else:
print("⚠️ Impossible d'extraire la description, utilisant un nom par défaut.")
return "Numéro spécial"
def upload_pdf(file_path):
"""Upload un fichier PDF vers Ghost et retourne l'URL"""
print(f"🔄 Début de l'upload du fichier : {file_path}")
token = create_jwt()
headers = {
"Authorization": f"Ghost {token}",
"X-Ghost-Version": "5.110",
}
with open(file_path, "rb") as file:
files = {"file": (file_path, file, "application/pdf")}
response = requests.post(UPLOAD_ENDPOINT, headers=headers, files=files)
if response.status_code == 201:
file_data = response.json().get("files", [{}])[0]
file_url = file_data.get("url")
print(f"✅ Fichier uploadé avec succès : {file_url}")
return file_url
else:
print(f"❌ Erreur lors de l'upload : {response.status_code} - {response.text}")
return None
def add_file_card_to_page(page_slug, file_url, file_name, description):
"""Ajoute un Editor Card de type fichier à une page Ghost"""
print(f"🔄 Récupération de la page '{page_slug}'...")
token = create_jwt()
headers = {
"Authorization": f"Ghost {token}",
"Content-Type": "application/json",
}
# Récupérer la page existante
response = requests.get(PAGE_ENDPOINT, headers=headers)
if response.status_code != 200:
print(f"❌ Erreur récupération des pages : {response.status_code} - {response.text}")
return False
pages = response.json().get("pages", [])
target_page = next((p for p in pages if p["slug"] == page_slug), None)
if not target_page:
print("❌ Page non trouvée !")
return False
page_id = target_page["id"]
updated_at = target_page["updated_at"] # ✅ Récupération de `updated_at`
print(f"✅ Page trouvée ! ID : {page_id}, Dernière mise à jour : {updated_at}")
# Vérifier si la page utilise Lexical (Ghost Editor)
if "lexical" in target_page:
content_field = "lexical"
current_content = json.loads(target_page["lexical"])
else:
print("❌ La page ne supporte pas Lexical.")
return False
print("🔄 Ajout du fichier sous forme d'Editor Card...")
# Récupérer les métadonnées du fichier
file_extension = file_name.split(".")[-1]
file_size = os.path.getsize(PDF_FILE_PATH)
# Nouvelle structure complète pour l'Editor Card
new_card = {
"type": "file",
"version": 1,
"file": {
"url": file_url,
"caption": description,
"mimeType": "application/pdf",
"extension": file_extension,
"name": file_name,
"size": file_size
},
"children": [] # ✅ Ghost semble attendre un champ `children` même vide
}
# Vérifier si c'est la première insertion ou une mise à jour
if "root" in current_content and "children" in current_content["root"]:
current_content["root"]["children"].append(new_card)
else:
# Si le contenu est vide, on initialise un nouveau document Lexical
current_content = {
"root": {
"type": "root",
"children": [new_card]
}
}
# Préparer la mise à jour avec `updated_at`
update_payload = {
"pages": [
{
"id": page_id,
"updated_at": updated_at, # ✅ Ajout de `updated_at`
content_field: json.dumps(current_content)
}
]
}
print(f"🔄 Mise à jour de la page {page_slug} avec le fichier...")
# Envoyer la mise à jour
update_response = requests.put(f"{PAGE_ENDPOINT}{page_id}/", headers=headers, json=update_payload)
if update_response.status_code == 200:
print(f"✅ Page mise à jour avec le fichier ! {file_url}")
return True
else:
print(f"❌ Erreur mise à jour de la page : {update_response.status_code} - {update_response.text}")
return False
if __name__ == "__main__":
print("🚀 Script de mise à jour Ghost démarré.")
# Extraire le nom et la description du fichier
file_name = os.path.basename(PDF_FILE_PATH)
description = extract_description(file_name)
print(f"📂 Fichier détecté : {file_name}")
print(f"📝 Description extraite : {description}")
# Upload du fichier
file_url = upload_pdf(PDF_FILE_PATH)
if file_url:
print("🔄 Ajout du fichier à la page...")
add_file_card_to_page(PAGE_SLUG, file_url, file_name, description)
else:
print("❌ Aucun fichier n'a été uploadé, arrêt du script.")
I’ve been looking around the header and API but can’t find anything; I understand file upload might be early access but any help would be more than welcome
Thanks!