diff --git a/build.py b/build.py index 9950425..423d785 100644 --- a/build.py +++ b/build.py @@ -1,187 +1,6 @@ import logging -from datetime import datetime -from pathlib import Path -from shutil import copyfile -from PIL import Image -from src.py.utils import ensure_dir, copy_assets, load_yaml, load_theme_config -from src.py.css_generator import generate_css_variables, generate_fonts_css, generate_google_fonts_link -from src.py.image_processor import process_images, copy_original_images, convert_and_resize_image, generate_favicons_from_logo, generate_favicon_ico -from src.py.html_generator import render_template, render_gallery_images, generate_gallery_json_from_images, generate_robots_txt, generate_sitemap_xml - -# Configure logging to display only the messages -logging.basicConfig(level=logging.INFO, format='%(message)s') - -# Define key directories used throughout the script -SRC_DIR = Path.cwd() -BUILD_DIR = SRC_DIR / "output" -TEMPLATE_DIR = SRC_DIR / "src/templates" -IMG_DIR = SRC_DIR / "config/photos" -JS_DIR = SRC_DIR / "src/public/js" -STYLE_DIR = SRC_DIR / "src/public/style" -GALLERY_FILE = SRC_DIR / "config/gallery.yaml" -SITE_FILE = SRC_DIR / "config/site.yaml" -THEMES_DIR = SRC_DIR / "config/themes" - -def build(): - logging.info("🚀 Starting build...") - ensure_dir(BUILD_DIR) - copy_assets(JS_DIR, STYLE_DIR, BUILD_DIR) - - # Defining build vars - build_date = datetime.now().strftime("%Y%m%d%H%M%S") - build_date_version = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - site_vars = load_yaml(SITE_FILE) - gallery_vars = load_yaml(GALLERY_FILE) - build_section = site_vars.get("build", {}) - theme_name = site_vars.get("build", {}).get("theme", "default") - theme_vars, theme_dir = load_theme_config(theme_name, THEMES_DIR) - fonts_dir = theme_dir / "fonts" - theme_css_path = theme_dir / "theme.css" - canonical_url = site_vars.get("info", {}).get("canonical", "").rstrip("/") - canonical_home = f"{canonical_url}/" - canonical_legals = f"{canonical_url}/legals/" - - # Copying theme.css if existing - if theme_css_path.exists(): - dest_theme_css = BUILD_DIR / "style" / "theme.css" - dest_theme_css.parent.mkdir(parents=True, exist_ok=True) - copyfile(theme_css_path, dest_theme_css) - theme_css = f'' - logging.info(f"[✓] Theme CSS found, copied to build folder: {dest_theme_css}") - else: - theme_css = "" - logging.warning(f"[~] No theme.css found in {theme_css_path}, skipping theme CSS injection.") - - preload_links = generate_fonts_css(fonts_dir, BUILD_DIR / "style" / "fonts.css", fonts_cfg=theme_vars.get("fonts")) - generate_css_variables(theme_vars.get("colors", {}), BUILD_DIR / "style" / "colors.css") - generate_favicons_from_logo(theme_vars, theme_dir, BUILD_DIR / "img" / "favicon") - generate_favicon_ico(theme_vars, theme_dir, BUILD_DIR / "favicon.ico") - - # Converting and resizing images if enabled - convert_images = build_section.get("convert_images", True) - resize_images = build_section.get("resize_images", True) - logging.info(f"[~] convert_images = {convert_images}") - logging.info(f"[~] resize_images = {resize_images}") - - hero_images = gallery_vars.get("hero", {}).get("images", []) - gallery_images = gallery_vars.get("gallery", {}).get("images", []) - - if convert_images: - process_images(hero_images, resize_images, IMG_DIR, BUILD_DIR) - process_images(gallery_images, resize_images, IMG_DIR, BUILD_DIR) - else: - copy_original_images(hero_images, IMG_DIR, BUILD_DIR) - copy_original_images(gallery_images, IMG_DIR, BUILD_DIR) - - if "hero" not in site_vars: - site_vars["hero"] = {} # Initialize an empty hero section - - # Adding menu - menu_html = "\n".join( - f'
{item['paragraph']}
" for item in ip_paragraphs) - legals_context = { - "hoster_name": legals_vars.get("hoster_name", ""), - "hoster_adress": legals_vars.get("hoster_adress", ""), - "hoster_contact": legals_vars.get("hoster_contact", ""), - "intellectual_property": paragraphs_html, - } - legals_body = render_template(TEMPLATE_DIR / "legals.html", legals_context) - legals_html = f"\n{signature}\n\n{head}\n{legals_body}\n{footer}\n" - output_legals = BUILD_DIR / "legals" / "index.html" - output_legals.parent.mkdir(parents=True, exist_ok=True) - with open(output_legals, "w", encoding="utf-8") as f: - f.write(legals_html) - logging.info(f"[✓] Legals page generated: {output_legals}") - else: - logging.warning("[~] No legals section found in site.yaml") - - # Hero carrousel generator - if hero_images: - generate_gallery_json_from_images(hero_images, BUILD_DIR) - else: - logging.warning("[~] No hero images found, skipping JSON generation.") - - # Sitemap and robot.txt generator - site_info = site_vars.get("info", {}) - canonical_url = site_info.get("canonical", "").rstrip("/") - if canonical_url: - allowed_pages = ["/", "/legals/"] - generate_robots_txt(canonical_url, allowed_pages, BUILD_DIR) - generate_sitemap_xml(canonical_url, allowed_pages, BUILD_DIR) - else: - logging.warning("[~] No canonical URL found in site.yaml info section, skipping robots.txt and sitemap.xml generation.") - - logging.info("✅ Build complete.") +from src.py.site_builder import build if __name__ == "__main__": - build() - \ No newline at end of file + logging.basicConfig(level=logging.INFO, format="%(message)s") + build() \ No newline at end of file diff --git a/config/gallery.yaml b/config/gallery.yaml index 6826862..fc1920d 100644 --- a/config/gallery.yaml +++ b/config/gallery.yaml @@ -1,7 +1,4 @@ -# Use gallery.py to automatically add photos stored in your /config/photos/gallery folder -# Add tags to your photos as shown below -# remove the # before [] if you removed all images to use gallery.py again hero: images: [] gallery: - images: [] \ No newline at end of file + images: [] diff --git a/gallery.py b/gallery.py index 54d815d..18efc02 100644 --- a/gallery.py +++ b/gallery.py @@ -1,113 +1,7 @@ -import yaml -import os -from pathlib import Path - -# YAML file paths -GALLERY_YAML = "config/gallery.yaml" - -# Image directories -GALLERY_DIR = Path("config/photos/gallery") -HERO_DIR = Path("config/photos/hero") - -def load_yaml(path): - print(f"[→] Loading {path}...") - if not os.path.exists(path): - print(f"[✗] File not found: {path}") - return {} - with open(path, "r", encoding="utf-8") as f: - data = yaml.safe_load(f) or {} - images = data.get("images", []) or [] - print(f"[✓] Loaded {len(images)} image(s) from {path}") - return data - -def save_yaml(data, path): - with open(path, "w", encoding="utf-8") as f: - yaml.dump(data, f, sort_keys=False, allow_unicode=True) - print(f"[✓] Saved updated YAML to {path}") - -def get_all_image_paths(directory): - return sorted([ - str(p.relative_to(directory.parent)).replace("\\", "/") - for p in directory.rglob("*") - if p.suffix.lower() in [".jpg", ".jpeg", ".png", ".webp"] - ]) - -def update_gallery(): - print("\n=== Updating gallery.yaml (gallery section) ===") - gallery = load_yaml(GALLERY_YAML) - - # Access the 'gallery' section within the gallery data, or initialize it if it doesn't exist - gallery_section = gallery.get("gallery", {}) - - # Access the 'images' list within the 'gallery' section, or initialize it if it doesn't exist - gallery_images = gallery_section.get("images", []) - - all_images = set(get_all_image_paths(GALLERY_DIR)) - known_images = {img["src"] for img in gallery_images} - - # Add new images - new_images = [ - {"src": path, "tags": []} - for path in all_images - if path not in known_images - ] - if new_images: - gallery_images.extend(new_images) - print(f"[✓] Added {len(new_images)} new image(s) to gallery.yaml (gallery)") - - # Remove deleted images - deleted_images = known_images - all_images - if deleted_images: - gallery_images = [img for img in gallery_images if img["src"] not in deleted_images] - print(f"[✓] Removed {len(deleted_images)} deleted image(s) from gallery.yaml (gallery)") - - # Update the 'gallery' section with the modified 'images' list - gallery_section["images"] = gallery_images - gallery["gallery"] = gallery_section - - save_yaml(gallery, GALLERY_YAML) - - if not new_images and not deleted_images: - print("[✓] No changes to gallery.yaml (gallery)") - -def update_hero(): - print("\n=== Updating gallery.yaml (hero section) ===") - gallery = load_yaml(GALLERY_YAML) - - # Access the 'hero' section within the gallery data, or initialize it if it doesn't exist - hero_section = gallery.get("hero", {}) - - # Access the 'images' list within the 'hero' section, or initialize it if it doesn't exist - hero_images = hero_section.get("images", []) - - all_images = set(get_all_image_paths(HERO_DIR)) - known_images = {img["src"] for img in hero_images} - - # Add new images - new_images = [ - {"src": path} - for path in all_images - if path not in known_images - ] - if new_images: - hero_images.extend(new_images) - print(f"[✓] Added {len(new_images)} new image(s) to gallery.yaml (hero)") - - # Remove deleted images - deleted_images = known_images - all_images - if deleted_images: - hero_images = [img for img in hero_images if img["src"] not in deleted_images] - print(f"[✓] Removed {len(deleted_images)} deleted image(s) from gallery.yaml (hero)") - - # Update the 'hero' section with the modified 'images' list - hero_section["images"] = hero_images - gallery["hero"] = hero_section - - save_yaml(gallery, GALLERY_YAML) - - if not new_images and not deleted_images: - print("[✓] No changes to gallery.yaml (hero)") +import logging +from src.py.gallery_builder import update_gallery, update_hero if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, format="%(message)s") update_gallery() - update_hero() + update_hero() \ No newline at end of file diff --git a/src/py/gallery_builder.py b/src/py/gallery_builder.py new file mode 100644 index 0000000..eaff225 --- /dev/null +++ b/src/py/gallery_builder.py @@ -0,0 +1,109 @@ +import yaml +import os +from pathlib import Path + +# YAML file paths +GALLERY_YAML = "config/gallery.yaml" + +# Image directories +GALLERY_DIR = Path("config/photos/gallery") +HERO_DIR = Path("config/photos/hero") + +def load_yaml(path): + print(f"[→] Loading {path}...") + if not os.path.exists(path): + print(f"[✗] File not found: {path}") + return {} + with open(path, "r", encoding="utf-8") as f: + data = yaml.safe_load(f) or {} + images = data.get("images", []) or [] + print(f"[✓] Loaded {len(images)} image(s) from {path}") + return data + +def save_yaml(data, path): + with open(path, "w", encoding="utf-8") as f: + yaml.dump(data, f, sort_keys=False, allow_unicode=True) + print(f"[✓] Saved updated YAML to {path}") + +def get_all_image_paths(directory): + return sorted([ + str(p.relative_to(directory.parent)).replace("\\", "/") + for p in directory.rglob("*") + if p.suffix.lower() in [".jpg", ".jpeg", ".png", ".webp"] + ]) + +def update_gallery(): + print("\n=== Updating gallery.yaml (gallery section) ===") + gallery = load_yaml(GALLERY_YAML) + + # Access the 'gallery' section within the gallery data, or initialize it if it doesn't exist + gallery_section = gallery.get("gallery", {}) + + # Access the 'images' list within the 'gallery' section, or initialize it if it doesn't exist + gallery_images = gallery_section.get("images", []) + + all_images = set(get_all_image_paths(GALLERY_DIR)) + known_images = {img["src"] for img in gallery_images} + + # Add new images + new_images = [ + {"src": path, "tags": []} + for path in all_images + if path not in known_images + ] + if new_images: + gallery_images.extend(new_images) + print(f"[✓] Added {len(new_images)} new image(s) to gallery.yaml (gallery)") + + # Remove deleted images + deleted_images = known_images - all_images + if deleted_images: + gallery_images = [img for img in gallery_images if img["src"] not in deleted_images] + print(f"[✓] Removed {len(deleted_images)} deleted image(s) from gallery.yaml (gallery)") + + # Update the 'gallery' section with the modified 'images' list + gallery_section["images"] = gallery_images + gallery["gallery"] = gallery_section + + save_yaml(gallery, GALLERY_YAML) + + if not new_images and not deleted_images: + print("[✓] No changes to gallery.yaml (gallery)") + +def update_hero(): + print("\n=== Updating gallery.yaml (hero section) ===") + gallery = load_yaml(GALLERY_YAML) + + # Access the 'hero' section within the gallery data, or initialize it if it doesn't exist + hero_section = gallery.get("hero", {}) + + # Access the 'images' list within the 'hero' section, or initialize it if it doesn't exist + hero_images = hero_section.get("images", []) + + all_images = set(get_all_image_paths(HERO_DIR)) + known_images = {img["src"] for img in hero_images} + + # Add new images + new_images = [ + {"src": path} + for path in all_images + if path not in known_images + ] + if new_images: + hero_images.extend(new_images) + print(f"[✓] Added {len(new_images)} new image(s) to gallery.yaml (hero)") + + # Remove deleted images + deleted_images = known_images - all_images + if deleted_images: + hero_images = [img for img in hero_images if img["src"] not in deleted_images] + print(f"[✓] Removed {len(deleted_images)} deleted image(s) from gallery.yaml (hero)") + + # Update the 'hero' section with the modified 'images' list + hero_section["images"] = hero_images + gallery["hero"] = hero_section + + save_yaml(gallery, GALLERY_YAML) + + if not new_images and not deleted_images: + print("[✓] No changes to gallery.yaml (hero)") diff --git a/src/py/html_generator.py b/src/py/html_generator.py index 4eca005..2ac932e 100644 --- a/src/py/html_generator.py +++ b/src/py/html_generator.py @@ -36,16 +36,30 @@ def generate_gallery_json_from_images(images, output_dir): def generate_robots_txt(canonical_url, allowed_paths, output_dir): robots_lines = ["User-agent: *"] - for path in allowed_paths: - robots_lines.append(f"Allow: {path}") + + # Block everything by default robots_lines.append("Disallow: /") + + # Explicitly allow certain paths + for path in allowed_paths: + if not path.startswith("/"): + path = "/" + path + robots_lines.append(f"Allow: {path}") + robots_lines.append("") - robots_lines.append(f"Sitemap: {canonical_url}/sitemap.xml") + robots_lines.append(f"Sitemap: {canonical_url.rstrip('/')}/sitemap.xml") + content = "\n".join(robots_lines) - output_path = output_dir / "robots.txt" - with open(output_path, "w", encoding="utf-8") as f: - f.write(content) - logging.info(f"[✓] robots.txt generated at {output_path}") + output_path = Path(output_dir) / "robots.txt" + + try: + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, "w", encoding="utf-8") as f: + f.write(content) + logging.info(f"[✓] robots.txt generated at {output_path}") + + except Exception as e: + logging.error(f"[✗] Failed to write robots.txt: {e}") def generate_sitemap_xml(canonical_url, allowed_paths, output_dir): urlset_start = '\n{item['paragraph']}
" for item in ip_paragraphs) + legals_context = { + "hoster_name": legals_vars.get("hoster_name", ""), + "hoster_adress": legals_vars.get("hoster_adress", ""), + "hoster_contact": legals_vars.get("hoster_contact", ""), + "intellectual_property": paragraphs_html, + } + legals_body = render_template(TEMPLATE_DIR / "legals.html", legals_context) + legals_html = f"\n{signature}\n\n{head}\n{legals_body}\n{footer}\n" + output_legals = BUILD_DIR / "legals" / "index.html" + output_legals.parent.mkdir(parents=True, exist_ok=True) + with open(output_legals, "w", encoding="utf-8") as f: + f.write(legals_html) + logging.info(f"[✓] Legals page generated: {output_legals}") + else: + logging.warning("[~] No legals section found in site.yaml") + + # Hero carrousel generator + if hero_images: + generate_gallery_json_from_images(hero_images, BUILD_DIR) + else: + logging.warning("[~] No hero images found, skipping JSON generation.") + + # Sitemap and robot.txt generator + site_info = site_vars.get("info", {}) + canonical_url = site_info.get("canonical", "").rstrip("/") + if canonical_url: + allowed_pages = ["/", "/legals/"] + generate_robots_txt(canonical_url, allowed_pages, BUILD_DIR) + generate_sitemap_xml(canonical_url, allowed_pages, BUILD_DIR) + else: + logging.warning("[~] No canonical URL found in site.yaml info section, skipping robots.txt and sitemap.xml generation.") + + logging.info("✅ Build complete.") + \ No newline at end of file