#!/usr/bin/env python3 """ WordPress Import Workflow Liest Markdown-Dateien aus URLs oder lokalen Dateien und erstellt WordPress-Beiträge basierend auf einer YAML-Konfigurationsdatei. """ import os import sys import yaml import requests import markdown from pathlib import Path from dotenv import load_dotenv from typing import Dict, Any, List, Optional from wordpress_api import WordPressAPI # Lade Umgebungsvariablen load_dotenv() def download_markdown(url: str) -> Optional[str]: """ Lädt Markdown-Inhalt von einer URL herunter Args: url: URL zur Markdown-Datei Returns: Markdown-Inhalt als String oder None bei Fehler """ try: response = requests.get(url, timeout=30) response.raise_for_status() return response.text except requests.exceptions.RequestException as e: print(f"Fehler beim Herunterladen von {url}: {e}") return None def read_local_markdown(file_path: str) -> Optional[str]: """ Liest Markdown-Inhalt aus einer lokalen Datei Args: file_path: Pfad zur lokalen Markdown-Datei Returns: Markdown-Inhalt als String oder None bei Fehler """ try: with open(file_path, 'r', encoding='utf-8') as f: return f.read() except IOError as e: print(f"Fehler beim Lesen von {file_path}: {e}") return None def markdown_to_html(markdown_text: str, extensions: Optional[List[str]] = None) -> str: """ Konvertiert Markdown zu HTML Args: markdown_text: Markdown-Text extensions: Liste der Markdown-Erweiterungen Returns: HTML-String """ if extensions is None: extensions = ['extra', 'codehilite', 'toc'] return markdown.markdown(markdown_text, extensions=extensions) def process_featured_image(wp_api: WordPressAPI, image_path: str, check_duplicate: bool = True) -> Optional[int]: """ Verarbeitet und lädt ein Beitragsbild hoch Args: wp_api: WordPress API Client image_path: Pfad zum Bild (lokal oder URL) check_duplicate: Prüfung auf Duplikate Returns: Media-ID oder None """ # Prüfe ob URL oder lokaler Pfad if image_path.startswith('http://') or image_path.startswith('https://'): # Download Bild try: response = requests.get(image_path, timeout=30) response.raise_for_status() # Temporäre Datei erstellen filename = os.path.basename(image_path.split('?')[0]) temp_path = f"/tmp/{filename}" with open(temp_path, 'wb') as f: f.write(response.content) media_id = wp_api.upload_media(temp_path, check_duplicate=check_duplicate) # Temporäre Datei löschen os.remove(temp_path) return media_id except Exception as e: print(f"Fehler beim Verarbeiten des Bilds von URL: {e}") return None else: # Lokale Datei if os.path.exists(image_path): return wp_api.upload_media(image_path, check_duplicate=check_duplicate) else: print(f"Bilddatei nicht gefunden: {image_path}") return None def process_post(wp_api: WordPressAPI, post_config: Dict[str, Any], global_settings: Dict[str, Any]) -> Optional[int]: """ Verarbeitet einen einzelnen Beitrag aus der Konfiguration Args: wp_api: WordPress API Client post_config: Beitrags-Konfiguration global_settings: Globale Einstellungen Returns: Post-ID oder None """ title = post_config.get('title') if not title: print("Fehler: Titel fehlt in der Beitragskonfiguration") return None print(f"\n{'='*60}") print(f"Verarbeite Beitrag: {title}") print(f"{'='*60}") # Markdown-Inhalt abrufen markdown_content = None if 'markdown_url' in post_config: print(f"Lade Markdown von URL: {post_config['markdown_url']}") markdown_content = download_markdown(post_config['markdown_url']) elif 'markdown_file' in post_config: print(f"Lese lokale Markdown-Datei: {post_config['markdown_file']}") markdown_content = read_local_markdown(post_config['markdown_file']) elif 'content' in post_config: markdown_content = post_config['content'] if not markdown_content: print(f"Fehler: Kein Inhalt für Beitrag '{title}'") return None # Markdown zu HTML konvertieren extensions = global_settings.get('markdown_extensions', ['extra', 'codehilite', 'toc']) html_content = markdown_to_html(markdown_content, extensions) # Kategorien verarbeiten category_ids = [] if 'categories' in post_config: for cat_name in post_config['categories']: cat_id = wp_api.get_or_create_category(cat_name) if cat_id: category_ids.append(cat_id) # Tags verarbeiten tag_ids = [] if 'tags' in post_config: for tag_name in post_config['tags']: tag_id = wp_api.get_or_create_tag(tag_name) if tag_id: tag_ids.append(tag_id) # Beitragsbild verarbeiten featured_media_id = None if 'featured_image' in post_config: skip_duplicate_media = global_settings.get('skip_duplicate_media', True) featured_media_id = process_featured_image( wp_api, post_config['featured_image'], check_duplicate=skip_duplicate_media ) # Status status = post_config.get('status', global_settings.get('default_status', 'draft')) # Excerpt excerpt = post_config.get('excerpt', '') # Beitrag erstellen skip_duplicates = global_settings.get('skip_duplicates', True) post_id = wp_api.create_post( title=title, content=html_content, status=status, featured_media=featured_media_id, categories=category_ids if category_ids else None, tags=tag_ids if tag_ids else None, excerpt=excerpt, check_duplicate=skip_duplicates ) return post_id def main(): """Hauptfunktion des Workflows""" # Konfigurationsdatei laden config_file = sys.argv[1] if len(sys.argv) > 1 else 'posts.yaml' if not os.path.exists(config_file): print(f"Fehler: Konfigurationsdatei '{config_file}' nicht gefunden") print("Verwendung: python workflow.py [config.yaml]") sys.exit(1) print(f"Lade Konfiguration aus: {config_file}") with open(config_file, 'r', encoding='utf-8') as f: config = yaml.safe_load(f) # WordPress-Credentials aus Umgebungsvariablen wp_url = os.getenv('WORDPRESS_URL') wp_username = os.getenv('WORDPRESS_USERNAME') wp_password = os.getenv('WORDPRESS_APP_PASSWORD') if not all([wp_url, wp_username, wp_password]): print("Fehler: WordPress-Credentials fehlen in .env-Datei") print("Benötigt: WORDPRESS_URL, WORDPRESS_USERNAME, WORDPRESS_APP_PASSWORD") sys.exit(1) print(f"\nVerbinde mit WordPress: {wp_url}") # WordPress API initialisieren wp_api = WordPressAPI(wp_url, wp_username, wp_password) # Globale Einstellungen global_settings = config.get('settings', {}) # Beiträge verarbeiten posts = config.get('posts', []) if not posts: print("Warnung: Keine Beiträge in der Konfiguration gefunden") return print(f"\nVerarbeite {len(posts)} Beitrag/Beiträge...\n") success_count = 0 error_count = 0 for post_config in posts: try: post_id = process_post(wp_api, post_config, global_settings) if post_id: success_count += 1 else: error_count += 1 except Exception as e: print(f"Fehler bei der Verarbeitung: {e}") error_count += 1 # Zusammenfassung print(f"\n{'='*60}") print(f"ZUSAMMENFASSUNG") print(f"{'='*60}") print(f"Erfolgreich: {success_count}") print(f"Fehler: {error_count}") print(f"Gesamt: {len(posts)}") print(f"{'='*60}\n") if __name__ == '__main__': main()