#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Script pour exporter un ou plusieurs blogs au format EPUB en utilisant Calibre. """ import os import re import argparse import subprocess import shutil import tempfile from datetime import datetime import locale try: import pypandoc HAS_PYPANDOC = True except ImportError: HAS_PYPANDOC = False print("Attention: pypandoc n'est pas installé. Installation requise: pip install pypandoc") from utils.utils import ( find_extract_in_content_org, find_first_level1_title, find_year_and_slug_on_filename, get_blog_template_conf ) from website_config import configs_sites def verifier_calibre(): """Vérifie si Calibre est installé.""" try: subprocess.run(['ebook-convert', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True) return True except (subprocess.CalledProcessError, FileNotFoundError): return False def extraire_date_du_contenu(content): """Extrait la date de création du contenu org.""" # Chercher #+CREATED match = re.search(r'#\+CREATED:\s*(\d{4}-\d{2}-\d{2})', content) if match: try: return datetime.strptime(match.group(1), '%Y-%m-%d') except ValueError: pass # Chercher #+post_date_published match = re.search(r'#\+post_date_published:\s*(\d{4}-\d{2}-\d{2})', content) if match: try: return datetime.strptime(match.group(1), '%Y-%m-%d') except ValueError: pass return None def extraire_date_du_fichier(filename): """Extrait la date du nom de fichier.""" try: date_str, annee, slug = find_year_and_slug_on_filename(filename) if date_str and len(date_str) >= 8: year = date_str[:4] month = date_str[4:6] day = date_str[6:8] return datetime(int(year), int(month), int(day)) except: pass return None def collecter_articles(blog_path): """Collecte tous les articles .org d'un blog.""" articles = [] # Chercher dans lang_fr et lang_en for lang_dir in ['lang_fr', 'lang_en']: lang_path = os.path.join(blog_path, lang_dir) if not os.path.exists(lang_path): continue # Lister tous les fichiers .org for filename in os.listdir(lang_path): if filename.endswith('.org'): filepath = os.path.join(lang_path, filename) try: with open(filepath, 'r', encoding='utf-8') as f: content = f.read() # Extraire les métadonnées title = find_first_level1_title(content) if not title: continue # Nettoyer le titre title = title.replace('*', '').strip() date = extraire_date_du_contenu(content) if not date: date = extraire_date_du_fichier(filename) if not date: date = datetime.fromtimestamp(os.path.getmtime(filepath)) articles.append({ 'title': title, 'content': content, 'date': date, 'filename': filename }) except Exception as e: print(f" Erreur lors de la lecture de {filepath}: {e}") # Trier par date (plus ancien en premier) articles.sort(key=lambda x: x['date']) return articles def convertir_org_en_html(org_content): """Convertit le contenu org en HTML.""" if not HAS_PYPANDOC: raise RuntimeError("pypandoc n'est pas installé") # Nettoyer le contenu (retirer les métadonnées) content_clean = find_extract_in_content_org(org_content) # Convertir avec pandoc try: html = pypandoc.convert_text(content_clean, 'html', format='org') return html except Exception as e: print(f" Erreur lors de la conversion: {e}") return f"

Erreur lors de la conversion: {e}

" def generer_html_epub(articles, blog_config): """Génère un fichier HTML combiné pour l'export EPUB.""" html_parts = [] # En-tête HTML html_parts.append(''' {}

{}

{}

Auteur: {}

Export généré le: {}

'''.format( blog_config.get('BLOG_TITLE', 'Blog'), blog_config.get('BLOG_TITLE', 'Blog'), blog_config.get('BLOG_SUBTITLE', ''), blog_config.get('AUTHOR', 'Auteur inconnu'), datetime.now().strftime('%d/%m/%Y à %H:%M:%S') )) # Ajouter chaque article for article in articles: html_parts.append(f'

{article["title"]}

') html_parts.append(f'
Publié le: {article["date"].strftime("%d/%m/%Y")}
') # Convertir le contenu org en HTML html_content = convertir_org_en_html(article['content']) html_parts.append(html_content) html_parts.append('
') # Pied de page html_parts.append('''

Export généré depuis: {}

{}

'''.format( blog_config.get('NDD', ''), blog_config.get('DESCRIPTION', '') )) return '\n'.join(html_parts) def exporter_epub(blogs, output_dir='exports/epub', titre=None): """Exporte un ou plusieurs blogs en EPUB.""" # Vérifier que Calibre est installé if not verifier_calibre(): print("Erreur: Calibre n'est pas installé ou ebook-convert n'est pas dans le PATH.") print("Installation: https://calibre-ebook.com/download") return if not HAS_PYPANDOC: print("Erreur: pypandoc n'est pas installé.") print("Installation: pip install pypandoc") return sources_dir = "sources" if not os.path.exists(sources_dir): print(f"Erreur: Le dossier {sources_dir} n'existe pas") return # Créer le dossier de sortie os.makedirs(output_dir, exist_ok=True) # Collecter les articles de tous les blogs all_articles = [] blog_configs = {} blog_titles = [] for blog_name in blogs: blog_path = os.path.join(sources_dir, blog_name) if not os.path.exists(blog_path): print(f"⚠️ Blog '{blog_name}' introuvable dans {sources_dir}") continue print(f"📚 Collecte des articles de {blog_name}...") blog_config = get_blog_template_conf(blog_name) blog_configs[blog_name] = blog_config articles = collecter_articles(blog_path) print(f" {len(articles)} articles trouvés") for article in articles: article['blog_name'] = blog_name all_articles.append(article) blog_titles.append(blog_config.get('BLOG_TITLE', blog_name)) if not all_articles: print("Aucun article trouvé.") return # Trier tous les articles par date all_articles.sort(key=lambda x: x['date']) print(f"\n📖 Total: {len(all_articles)} articles") # Déterminer le titre et l'auteur pour l'EPUB if len(blogs) == 1: config = blog_configs[blogs[0]] epub_title = titre or config.get('BLOG_TITLE', blogs[0]) epub_author = config.get('AUTHOR', 'Auteur inconnu') else: epub_title = titre or ' - '.join(blog_titles) # Prendre le premier auteur ou combiner si différents auteurs = list(set([blog_configs[b].get('AUTHOR', '') for b in blogs])) epub_author = auteurs[0] if len(auteurs) == 1 else ' & '.join(auteurs) # Générer le HTML combiné print(f"\n🔄 Génération du fichier HTML...") # Pour plusieurs blogs, utiliser la config du premier main_config = blog_configs[blogs[0]] if blogs else {} html_content = generer_html_epub(all_articles, main_config) # Créer un fichier temporaire HTML with tempfile.NamedTemporaryFile(mode='w', suffix='.html', encoding='utf-8', delete=False) as tmp_html: tmp_html.write(html_content) tmp_html_path = tmp_html.name # Générer le nom du fichier EPUB safe_title = re.sub(r'[^\w\s-]', '', epub_title).strip() safe_title = re.sub(r'[-\s]+', '-', safe_title) epub_filename = f"{safe_title}.epub" epub_path = os.path.join(output_dir, epub_filename) # Convertir en EPUB avec Calibre print(f"\n📦 Conversion en EPUB avec Calibre...") print(f" Titre: {epub_title}") print(f" Auteur: {epub_author}") print(f" Fichier: {epub_path}") try: cmd = [ 'ebook-convert', tmp_html_path, epub_path, '--title', epub_title, '--authors', epub_author, '--language', 'fr', '--page-breaks-before', '/', '--insert-blank-line', '--smarten-punctuation', '--margin-top', '50', '--margin-bottom', '50', '--margin-left', '50', '--margin-right', '50', ] # Ajouter la description si disponible if main_config.get('DESCRIPTION'): cmd.extend(['--comments', main_config.get('DESCRIPTION')]) result = subprocess.run(cmd, capture_output=True, text=True, check=True) print(f"\n✅ EPUB généré avec succès: {epub_path}") # Nettoyer le fichier temporaire os.unlink(tmp_html_path) except subprocess.CalledProcessError as e: print(f"\n❌ Erreur lors de la conversion:") print(f" {e.stderr}") os.unlink(tmp_html_path) return except Exception as e: print(f"\n❌ Erreur: {e}") if os.path.exists(tmp_html_path): os.unlink(tmp_html_path) return def main(): """Fonction principale.""" parser = argparse.ArgumentParser( description='Exporte un ou plusieurs blogs au format EPUB avec Calibre' ) parser.add_argument( 'blogs', nargs='+', help='Noms des blogs à exporter (ex: tykayn_blog cipherbliss_blog)' ) parser.add_argument( '--output', '-o', default='exports/epub', help='Dossier de sortie pour les fichiers EPUB (défaut: exports/epub)' ) parser.add_argument( '--titre', '-t', help='Titre personnalisé pour l\'EPUB (par défaut: titre du blog ou combinaison)' ) args = parser.parse_args() exporter_epub(args.blogs, args.output, args.titre) if __name__ == "__main__": main()