Files
hydrogen/classes.py
2025-06-11 13:48:08 +03:00

159 lines
7.2 KiB
Python

from datetime import datetime
import os
import shutil
from collections import defaultdict
import markdown
import frontmatter
from pathlib import Path
from logger import logger
from config import Config
from jinja_env import env, content_item_template, index_template, categories_index_template
class ContentItem:
def render_content(self):
logger.debug(f"Rendering {self.source_filename} to {self.target_filename}")
try:
logger.debug(f"Title: {self.title}")
if self.image_file and self.image_file.exists():
logger.debug(f"Copying {self.image_file} to {Path(Config.OUTPUT_DIR) / self.image_file}")
shutil.copyfile(self.image_file, Path(Config.OUTPUT_DIR) / self.image_file)
self.image_file = f"{self.image_file.stem}.jpg"
if self.css_file and self.css_file.exists():
css_targetfile = Path(f"{Config.OUTPUT_DIR}/{Config.STATIC_DIR}/css/{self.css_file.name}")
logger.debug(f"Copying {self.css_file} to {css_targetfile}")
shutil.copyfile(self.css_file, css_targetfile)
self.css_file = css_targetfile
if self.js_file and self.js_file.exists():
shutil.copyfile(self.js_file, Path(Config.OUTPUT_DIR) / self.js_file)
with self.target_filename.open("w", encoding="utf-8") as f:
f.write(content_item_template.render(content_item = self, page_title = self.title))
except Exception as e:
logger.error(e)
def parse_content(self):
logger.debug(f"Parsing file {self.source_filename}")
try:
self.source_filename = Path(self.source_filename)
self.subdir = self.source_filename.parent
self.slug = self.source_filename.stem
self.target_filename = Path(f"{Config.OUTPUT_DIR}/{self.source_filename.parent}/{self.source_filename.stem}.html")
self.data = frontmatter.load(self.source_filename)
self.url = "<a href=" + f"{self.subdir}/{self.slug}.html" + "> ...read more</a>"
self.preview = self.data.content.replace('\n', '<br>')[:300] + self.url
self.title = self.data.get("title", self.slug)
self.omit_second_title = self.data.get("omit_second_title", False)
self.date = self.data.get("date", "2000-01-01T00:00:00+03:00")
self.categories = self.data.get("categories", str([]))
self.hidden = self.data.get("hidden", str(False))
self.data.content = self.data.content.replace('\n', ' \n')
self.html = markdown.markdown(self.data.content)
cover_image_path = Path(self.source_filename.parent) / Path(self.source_filename.stem + ".jpg")
self.image_file = cover_image_path if cover_image_path.exists() else None
css_filepath = Path(f"{self.source_filename.parent}/{self.source_filename.stem}.css")
self.css_file = css_filepath if css_filepath.exists() else None
self.js_file = Path(self.source_filename.stem + ".js") if Path(self.source_filename.stem + ".js").exists else None
except Exception as e:
logger.error(e)
def create_content(self):
with open(self.source_filename, mode="w", encoding="utf-8") as f:
f.writelines([
"---\n",
f"title: {self.source_filename.stem}\n",
f"date: '{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}'\n",
"description: ''\n",
"author: ''\n",
"categories: ['default']\n",
"hidden: False\n",
"omit_second_title: False\n",
"---\n",
"\n\n\n"
])
def __init__(self, filename):
self.source_filename = filename
class Site:
def __init__(self):
self.output_dir = Path(Config.OUTPUT_DIR)
logger.info("Initializing new site")
self.content_dir = Path(Config.CONTENT_DIR)
self.static_dir = Path(Config.STATIC_DIR)
self.templates_dir = Path(Config.TEMPLATES_DIR)
self.images_dir = Path(f"{Config.STATIC_DIR}/images")
self.css_dir = Path(f"{Config.STATIC_DIR}/css")
self.js_dir = Path(f"{Config.STATIC_DIR}/js")
self.output_dir = Path(Config.OUTPUT_DIR)
self.content_items = []
self.categories = defaultdict(list)
def init_site(self):
# Create directories
for subdir in [self.content_dir, self.static_dir, self.templates_dir,
self.images_dir, self.css_dir, self.js_dir]:
os.makedirs(subdir, exist_ok=True)
# Create templates from literals
import templates
template_names = [t for t in dir(templates) if not t.startswith('_')]
for template_name in template_names:
template_content = getattr(templates, template_name)
with open(self.templates_dir / f"{template_name}.html", "w", encoding="utf8") as f:
f.write(template_content)
# Create static/about.md
def get_content_items(self):
logger.debug(f"Scanning {Path(Config.CONTENT_DIR)}")
for md_file in Path(Config.CONTENT_DIR).glob("*.md"):
logger.debug(f"Loading {md_file}")
content_item = ContentItem(md_file)
content_item.parse_content()
self.content_items.append(content_item)
def map_categories(self):
for content_item in self.content_items:
for category in content_item.data.get("categories"):
self.categories[category].append(content_item.slug)
print(self.categories)
def build(self):
# Recreate the output dir if needed
if self.output_dir.exists():
shutil.rmtree(self.output_dir)
self.output_dir.mkdir()
# Create public subdirs
subdirs = [self.images_dir, self.css_dir, self.js_dir, self.content_dir]
for subdir in subdirs:
subdir = self.output_dir / subdir
subdir.mkdir(parents=True, exist_ok=True)
# Copy static files if exist
if self.static_dir.exists():
shutil.copytree(self.static_dir, self.output_dir / self.static_dir, dirs_exist_ok=True)
# Parse the content files
logger.debug("Getting content items")
self.get_content_items()
logger.debug(f"Content items: {self.content_items}")
for content_item in self.content_items:
content_item.render_content()
# Build categories map
self.map_categories()
for category in self.categories:
with (self.output_dir / "categories.html").open(mode="w", encoding="utf-8") as f:
f.write(categories_index_template.render(page_title = Config.MAIN_PAGE_TITLE, categories_list = self.categories))
# Render the index file
with (self.output_dir / "index.html").open("w", encoding="utf-8") as f:
f.write(index_template.render(page_title = Config.MAIN_PAGE_TITLE, content_items=self.content_items))
# Render the about file
about_content = ContentItem(Path('static/about.md'))
about_content.parse_content()
about_content.render_content()
logger.info(f"Created {len(self.content_items)} content items.")