Compare commits
48 Commits
7c5a7fe392
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e89128e70c | |||
| 6f6c856229 | |||
| c70307e999 | |||
| defd77ece6 | |||
| 7698aabd44 | |||
| 0e98e5b6d2 | |||
| 26d35d80c6 | |||
| a1217c872a | |||
| 9800b08204 | |||
| 225e4f506c | |||
| dd70f0ebeb | |||
| 578a8408a4 | |||
| 1616348651 | |||
| 699d21fabe | |||
| 95d11ef112 | |||
| 6f52b678b7 | |||
| 660ab0d5da | |||
| 06b82addf8 | |||
| 3de89cdc68 | |||
| 88f527b8d1 | |||
| 4d3615fdcc | |||
| d38231a13d | |||
| 2045f36df0 | |||
| 3f52ec9531 | |||
| 88272fe95d | |||
| 8fd9872bf7 | |||
| c11be60cab | |||
| 03f6730ac5 | |||
| 17e2847b98 | |||
| 100005d00c | |||
| 2903f691cd | |||
| a5f42768b0 | |||
| ec682f498e | |||
| 02b5610a5a | |||
| 6c6cd36d2f | |||
| 2bd0d3db47 | |||
| e2edf189fc | |||
| a62db1fc2b | |||
| 167ee7ce09 | |||
| 2f28e02dff | |||
| 6ffef42c2c | |||
| 875a798214 | |||
| bef7bbbcdc | |||
| 8b419b1ca5 | |||
| 94ffcecf8b | |||
| 80620dbf63 | |||
| ddca1befb5 | |||
| 25614b72e0 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1 @@
|
|||||||
config.py
|
|
||||||
__pycache__
|
__pycache__
|
||||||
|
|||||||
39
README.md
Normal file
39
README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Geminer
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Geminer is a tool that was originally designed to convert a PicoCMS blog into a static version for Gemini. In fact, it can act as a markdown-based static site generator.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Markdown to Gemtext conversion
|
||||||
|
* Conversion of local links
|
||||||
|
* Give your own metadata list to gather
|
||||||
|
* Custom indexes
|
||||||
|
* Jinja2 templating
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
Geminer execution can be decomposed in two steps :
|
||||||
|
|
||||||
|
1. Parse blog posts markdown files and write gemtext translation.
|
||||||
|
2. Generate meta pages, i.e. home page and custom indexes.
|
||||||
|
|
||||||
|
During the first step, frontmatter metadata is collected from markdown posts while gemtext posts are generated. This means that while rendering the template, a post will only have access to informations about itself.
|
||||||
|
|
||||||
|
During the second step, all metadata has been gathered, which enables creation of various indexed, which requires of course access to all posts metadata.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Soon. For now you can read [the example config](example/config.py).
|
||||||
|
|
||||||
|
## Gemini capsules using Geminer
|
||||||
|
|
||||||
|
* gemini://hashtagueule.fr
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
* add parameter to give a function to treat local links
|
||||||
|
* clean the code (lots of work)
|
||||||
|
* add feed generation
|
||||||
|
* change configuration format?
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
# This is the configuration file for geminer.
|
|
||||||
# It is not intended to be executed.
|
|
||||||
|
|
||||||
# locale (for templates, for example dates rendering)
|
|
||||||
locale = "fr_FR.utf8"
|
|
||||||
|
|
||||||
# path to directory containing markdonw files to convert
|
|
||||||
md_dir = "/srv/my-site-content/posts"
|
|
||||||
|
|
||||||
# path to directory where gemini files will be exported
|
|
||||||
gmi_dir = "/srv/gemini/my-site/posts"
|
|
||||||
|
|
||||||
# path to directory where meta pages will be generated
|
|
||||||
# such as home page, tags list, tag pages, author pages...
|
|
||||||
meta_dir = "/srv/gemini/my-site"
|
|
||||||
|
|
||||||
# path to directory containing templates
|
|
||||||
tpl_dir = "~/repo/htg-content/gemini/templates"
|
|
||||||
|
|
||||||
# list of markdown files extensions
|
|
||||||
# Any file with a different extension will be ignored.
|
|
||||||
md_extensions = [
|
|
||||||
"markdown",
|
|
||||||
"mdown",
|
|
||||||
"mkdn",
|
|
||||||
"md",
|
|
||||||
"mkd",
|
|
||||||
"mdwn",
|
|
||||||
"mdtxt",
|
|
||||||
"mdtext",
|
|
||||||
"text",
|
|
||||||
"Rmd"
|
|
||||||
]
|
|
||||||
|
|
||||||
# Set following value to True if you want ".gmi" extensions on new files.
|
|
||||||
gmi_extension = False
|
|
||||||
|
|
||||||
# replacement map
|
|
||||||
# Some CMS make you use some placeholders (for instance for assets URL).
|
|
||||||
# You have to inform geminer of them here.
|
|
||||||
replace = [
|
|
||||||
("%assets_url%", "https://hashtagueule.fr/assets")
|
|
||||||
]
|
|
||||||
|
|
||||||
# md2gemini settings
|
|
||||||
# Check the documentation at https://pypi.org/project/md2gemini/
|
|
||||||
code_tag=""
|
|
||||||
img_tag="[IMG]"
|
|
||||||
indent=" "
|
|
||||||
ascii_table=False
|
|
||||||
links="copy"
|
|
||||||
plain=True
|
|
||||||
strip_html=False
|
|
||||||
base_url=""
|
|
||||||
table_tag="table"
|
|
||||||
132
example/config.py
Normal file
132
example/config.py
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# This is the configuration file for geminer.
|
||||||
|
# It is not intended to be executed.
|
||||||
|
|
||||||
|
# locale (for templates, for example dates rendering)
|
||||||
|
locale = "en_US.utf8"
|
||||||
|
|
||||||
|
# path to directory containing markdonw files to convert
|
||||||
|
md_path = "/srv/gemini/example/md"
|
||||||
|
|
||||||
|
# path to gemini blog root directory
|
||||||
|
gmi_path = "/srv/gemini/example/gmi"
|
||||||
|
|
||||||
|
# directory within gmi_path which will contains converted posts
|
||||||
|
posts_dir = "posts"
|
||||||
|
|
||||||
|
# path to directory containing templates
|
||||||
|
tpl_path = "/srv/gemini/example/templates"
|
||||||
|
|
||||||
|
# list of markdown files extensions
|
||||||
|
# Any file with a different extension will be ignored.
|
||||||
|
md_extensions = [
|
||||||
|
".markdown",
|
||||||
|
".mdown",
|
||||||
|
".mkdn",
|
||||||
|
".md",
|
||||||
|
".mkd",
|
||||||
|
".mdwn",
|
||||||
|
".mdtxt",
|
||||||
|
".mdtext",
|
||||||
|
".text",
|
||||||
|
".Rmd"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Specify gemini files extension. Set to empty string to disable extension.
|
||||||
|
# Warning: disabling could have unwanted side effects.
|
||||||
|
# Check out README for more informations.
|
||||||
|
gmi_extension = ".gmi"
|
||||||
|
|
||||||
|
# replacement map
|
||||||
|
# Some CMS make you use some placeholders (for instance for assets URL).
|
||||||
|
# You have to inform geminer of them here.
|
||||||
|
replace = [
|
||||||
|
("%assets_url%", "https://example.com/assets")
|
||||||
|
]
|
||||||
|
|
||||||
|
# md2gemini settings
|
||||||
|
# Check the documentation at https://pypi.org/project/md2gemini/l
|
||||||
|
code_tag=""
|
||||||
|
img_tag="[IMG]"
|
||||||
|
indent=" "
|
||||||
|
ascii_table=False
|
||||||
|
links="copy"
|
||||||
|
plain=True
|
||||||
|
strip_html=False
|
||||||
|
base_url=""
|
||||||
|
table_tag="table"
|
||||||
|
|
||||||
|
# default template for posts that don't specify one
|
||||||
|
default_post_template = "post"
|
||||||
|
|
||||||
|
# per-post properties to fetch in frontmatter
|
||||||
|
post_props = [
|
||||||
|
"date",
|
||||||
|
"title"
|
||||||
|
]
|
||||||
|
|
||||||
|
# indexable properties to fetch in frontmatter
|
||||||
|
# A lot of CMS will manage properties like tags ans authors, which are written
|
||||||
|
# in the frontmatter, and are used to make subgroups of posts.
|
||||||
|
# This setting enables to do the same for any frontmatter property you wish.
|
||||||
|
#
|
||||||
|
# Each indexable property generate two views:
|
||||||
|
# * per-value post index: This is a list of links to posts that have a given
|
||||||
|
# value of the property. There are as many per-value
|
||||||
|
# indexes as properties values.
|
||||||
|
# * property values index: This is a list of links to per-value indexes.
|
||||||
|
# There is only one page of this kind, and it can be
|
||||||
|
# disabled.
|
||||||
|
#
|
||||||
|
# Exemple: if the property is tags, then it will generate a tag index tags.gmi,
|
||||||
|
# which will link to some subindexes like tags/computer (assuming computer is
|
||||||
|
# an existing tag).
|
||||||
|
#
|
||||||
|
# Specify here a list of dictionnary, each one with the following keys:
|
||||||
|
# * property (mandatory): name of the property present in the frontmatter
|
||||||
|
# * list (facultative): set to True if the property is a list of values
|
||||||
|
# * item_dir (facultative): directory containing per-value posts indexes
|
||||||
|
# * item_tpl (facultative): template of the per-value posts indexes
|
||||||
|
# * index_name (facultative): filename of the property values index
|
||||||
|
# * index_tpl (facultative): template of the property values index
|
||||||
|
#
|
||||||
|
# Filenames are relative to meta_dir and extensions are automatically added.
|
||||||
|
# If filename contains an extension, it will override gmi_extension value.
|
||||||
|
# When a string value is facultative, it defaults to property name, except for
|
||||||
|
# index_name, which disables property values global index if not specified.
|
||||||
|
index_props = [
|
||||||
|
{
|
||||||
|
"property": "tags",
|
||||||
|
"list": True,
|
||||||
|
"item_dir": "tags",
|
||||||
|
"item_tpl": "tag",
|
||||||
|
"index_name": "tags",
|
||||||
|
"index_tpl": "tags_index"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"property": "author",
|
||||||
|
"list": False,
|
||||||
|
"item_dir": "authors",
|
||||||
|
"item_tpl": "author",
|
||||||
|
"index_name": "authors",
|
||||||
|
"index_tpl": "authors_index"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# custom extra pages to generate
|
||||||
|
# Each entry will generate a single page.
|
||||||
|
# This is the place to define homepage and feed page for instance.
|
||||||
|
# Templates will have to handle the full unsorted list of posts.
|
||||||
|
# "name" key is mandatory. It is the filename of the page.
|
||||||
|
# If filename contains an extension, it will override gmi_extension value.
|
||||||
|
# "tpl" key is facultative, defaults to name (without extension if any)
|
||||||
|
custom_pages = [
|
||||||
|
{
|
||||||
|
"name": "index",
|
||||||
|
"tpl": "index"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "atom.xml",
|
||||||
|
"tpl": "atom"
|
||||||
|
}
|
||||||
|
]
|
||||||
9
example/md/firt_post.md
Normal file
9
example/md/firt_post.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
title: "First post"
|
||||||
|
author: raspbeguy
|
||||||
|
date: 2020-12-09
|
||||||
|
tags: foo,bar
|
||||||
|
---
|
||||||
|
# I am a post
|
||||||
|
|
||||||
|
Look at me!
|
||||||
28
example/templates/atom.tpl
Normal file
28
example/templates/atom.tpl
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
<title>My blog</title>
|
||||||
|
<subtitle>Les gentils du net</subtitle>
|
||||||
|
<link href="gemini://example.com/atom.xml" rel="self"/>
|
||||||
|
<link href="gemini://example.com" rel="alternate"/>
|
||||||
|
<updated>{{ now().strftime('%FT%TZ') }}</updated>
|
||||||
|
<author>
|
||||||
|
<name>raspbeguy</name>
|
||||||
|
</author>
|
||||||
|
<category term="tech" />
|
||||||
|
<id>gemini://example.com</id>
|
||||||
|
{%- for post in posts|sort(attribute="date",reverse=True) %}
|
||||||
|
<entry>
|
||||||
|
<title>{{ post.title }}</title>
|
||||||
|
<id>gemini://example.com/{{ post.path }}</id>
|
||||||
|
<link href="gemini://example.com/{{ post.path }}" rel="alternate"/>
|
||||||
|
<updated>{{ post.date.strftime('%FT%TZ') }}</updated>
|
||||||
|
<published>{{ post.date.strftime('%FT%TZ') }}</published>
|
||||||
|
<author>
|
||||||
|
<name>{{ post.author.name }}</name>
|
||||||
|
</author>
|
||||||
|
{%- for tag in post.tags %}
|
||||||
|
<category term="{{ tag.name }}"/>
|
||||||
|
{%- endfor %}
|
||||||
|
</entry>
|
||||||
|
{%- endfor %}
|
||||||
|
</feed>
|
||||||
5
example/templates/author.tpl
Normal file
5
example/templates/author.tpl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Posts by {{ prop_item.name }}
|
||||||
|
|
||||||
|
{% for post in prop_item.posts|sort(attribute="date",reverse=True) -%}
|
||||||
|
=> /{{ post.path }} [{{ post.date.strftime('%d/%m/%Y') }}] {{ post.title }}
|
||||||
|
{% endfor %}
|
||||||
5
example/templates/authors_index.tpl
Normal file
5
example/templates/authors_index.tpl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Authors list
|
||||||
|
|
||||||
|
{% for author, value in prop.items()|sort(attribute='1.name') -%}
|
||||||
|
=> /authors/{{ author }}.gmi {{ value.name }} ({{ value.posts | length }} articles)
|
||||||
|
{% endfor %}
|
||||||
18
example/templates/index.tpl
Normal file
18
example/templates/index.tpl
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# My blog
|
||||||
|
|
||||||
|
## I deserve all your attention.
|
||||||
|
|
||||||
|
This is my blog. Please read it and like it.
|
||||||
|
|
||||||
|
Last posts :
|
||||||
|
{% for post in (posts|sort(attribute="date",reverse=True))[:30] -%}
|
||||||
|
=> /{{ post.path }} {{ post.date.strftime('%Y-%m-%d') }} - {{ post.title }}
|
||||||
|
{% endfor %}
|
||||||
|
=> /posts.gmi All posts
|
||||||
|
=> /tags.gmi Tag list
|
||||||
|
=> /authors.gmi Authors list
|
||||||
|
|
||||||
|
=> /atom.xml Atom feed
|
||||||
|
|
||||||
|
This capsule has been generated by Geminer.
|
||||||
|
=> https://git.gugod.fr/raspbeguy/geminer Dépôt de Geminer
|
||||||
14
example/templates/post.tpl
Normal file
14
example/templates/post.tpl
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# {{ post.title }}
|
||||||
|
|
||||||
|
by {{ post.author.name }}, on {{ post.date.strftime('%d %B %Y') }}
|
||||||
|
{% set tags = [] %}{% for tag in post.tags %}{{ tags.append(tag.name) or "" }}{% endfor %}{{ tags|join(", ") }}
|
||||||
|
|
||||||
|
{{ post.content }}
|
||||||
|
|
||||||
|
=> / Back to home
|
||||||
|
=> /authors/{{ post.author.slug }}.gmi More posts by {{ post.author.name }}
|
||||||
|
|
||||||
|
Posts having those tags:
|
||||||
|
{% for tag in post.tags|sort(attribute="name") -%}
|
||||||
|
=> /tags/{{ tag.slug }}.gmi {{ tag.name }}
|
||||||
|
{% endfor %}
|
||||||
5
example/templates/posts_list.tpl
Normal file
5
example/templates/posts_list.tpl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Posts list
|
||||||
|
|
||||||
|
{% for post in posts|sort(attribute="date",reverse=True) -%}
|
||||||
|
=> /{{ post.path }} [{{ post.date.strftime('%d/%m/%Y') }}] {{ post.title }}
|
||||||
|
{% endfor %}
|
||||||
5
example/templates/tag.tpl
Normal file
5
example/templates/tag.tpl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Posts with tag "{{ prop_item.name }}"
|
||||||
|
|
||||||
|
{% for post in prop_item.posts|sort(attribute="date",reverse=True) -%}
|
||||||
|
=> /{{ post.path }} [{{ post.date.strftime('%d/%m/%Y') }}] {{ post.title }}
|
||||||
|
{% endfor %}
|
||||||
5
example/templates/tags_index.tpl
Normal file
5
example/templates/tags_index.tpl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Tags list
|
||||||
|
|
||||||
|
{% for tag, value in prop.items()|sort(attribute='1.name') -%}
|
||||||
|
=> /tags/{{ tag }}.gmi {{ value.name }} ({{ value.posts | length }} articles)
|
||||||
|
{% endfor %}
|
||||||
179
geminer.py
179
geminer.py
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from md2gemini import md2gemini
|
from md2gemini import md2gemini
|
||||||
import frontmatter
|
import frontmatter
|
||||||
|
from slugify import slugify
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
import os
|
import os
|
||||||
import locale
|
import locale
|
||||||
@@ -12,60 +13,108 @@ import config
|
|||||||
# locale (for templates, for example dates rendering)
|
# locale (for templates, for example dates rendering)
|
||||||
locale.setlocale(locale.LC_ALL, config.locale)
|
locale.setlocale(locale.LC_ALL, config.locale)
|
||||||
|
|
||||||
md_path = os.path.expanduser(config.md_dir)
|
md_path = os.path.abspath(os.path.expanduser(config.md_path))
|
||||||
gmi_path = os.path.expanduser(config.gmi_dir)
|
gmi_path = os.path.abspath(os.path.expanduser(config.gmi_path))
|
||||||
tpl_path = os.path.expanduser(config.tpl_dir)
|
tpl_path = os.path.abspath(os.path.expanduser(config.tpl_path))
|
||||||
meta_path = os.path.expanduser(config.meta_dir)
|
|
||||||
|
|
||||||
# Initiate post meta list
|
posts_path = os.path.abspath(gmi_path + "/" + config.posts_dir)
|
||||||
posts_meta = []
|
|
||||||
|
# Initiate meta lists
|
||||||
|
posts = [] # This is a flat, unsorted list of posts
|
||||||
|
posts_prop_index = {} # This is a dict containing posts sorted by properties
|
||||||
|
|
||||||
|
for prop_dict in config.index_props:
|
||||||
|
posts_prop_index[prop_dict["property"]] = {}
|
||||||
|
|
||||||
os.chdir(md_path)
|
|
||||||
|
|
||||||
def add_ext_gmi(link):
|
def add_ext_gmi(link):
|
||||||
# Custom function to apply to links
|
# Custom function to apply to links
|
||||||
if "://" not in link: # apply only on local links
|
if "://" not in link: # apply only on local links
|
||||||
return link+".gmi"
|
return os.path.splitext(link)[0] + ".gmi"
|
||||||
else:
|
else:
|
||||||
return link
|
return link
|
||||||
|
|
||||||
|
|
||||||
# Walk through markdown directories
|
# Walk through markdown directories
|
||||||
for dirname, subdirlist, mdlist in os.walk('.'):
|
for dirname, subdirlist, mdlist in os.walk(md_path):
|
||||||
|
|
||||||
# Create same hierarchy in GMI directory
|
# Create same hierarchy in GMI directory
|
||||||
gmi_subpath = os.path.abspath(gmi_path+"/"+dirname)
|
gmi_subpath = os.path.abspath(posts_path + "/" + os.path.relpath(dirname, md_path))
|
||||||
os.makedirs(gmi_subpath, exist_ok=True)
|
os.makedirs(gmi_subpath, exist_ok=True)
|
||||||
|
|
||||||
for mdfile in mdlist:
|
for mdfile in mdlist:
|
||||||
basename, extension = os.path.splitext(mdfile)
|
basename, extension = os.path.splitext(mdfile)
|
||||||
extension = extension[1:]
|
|
||||||
|
|
||||||
# We want to ignore the file if this isn't a markdown file
|
# We want to ignore the file if this isn't a markdown file
|
||||||
if extension not in config.md_extensions:
|
if extension not in config.md_extensions:
|
||||||
print("Ignoring file {}: \"{}\" not in markdown extensions list".format(mdfile, extension))
|
print(
|
||||||
|
'Ignoring file {}: "{}" not in markdown extensions list'.format(
|
||||||
|
mdfile, extension
|
||||||
|
)
|
||||||
|
)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
post = {}
|
||||||
|
|
||||||
|
gmifile = basename + config.gmi_extension
|
||||||
|
|
||||||
|
post["path"] = config.posts_dir + "/" + os.path.relpath(dirname + "/" + gmifile, md_path)
|
||||||
|
|
||||||
# Read the Markdown file
|
# Read the Markdown file
|
||||||
with open(dirname+"/"+mdfile, 'r') as md:
|
with open(dirname + "/" + mdfile, "r") as md:
|
||||||
mdtext = md.read()
|
mdtext = md.read()
|
||||||
|
|
||||||
# Parse the YAML header
|
# Parse the YAML header
|
||||||
meta = frontmatter.parse(mdtext)[0]
|
meta = frontmatter.parse(mdtext)[0]
|
||||||
|
|
||||||
# Extract useful informations from the header
|
# Extract template
|
||||||
template = meta.get("template", None)
|
post["template"] = meta.get("template", config.default_post_template)
|
||||||
author = meta.get("author", None)
|
|
||||||
date = meta.get("date", None)
|
|
||||||
title = meta.get("title", None)
|
|
||||||
tags = meta.get("tags", None).split(',')
|
|
||||||
# For now, tags list must be a comma-separated string
|
|
||||||
# TODO: make possible to list tags as a YAML list
|
|
||||||
|
|
||||||
|
# Extract post properties
|
||||||
|
for prop in config.post_props:
|
||||||
|
post[prop] = meta.get(prop, None)
|
||||||
|
|
||||||
|
# Extract index properties
|
||||||
|
for prop_dict in config.index_props:
|
||||||
|
prop = prop_dict["property"]
|
||||||
|
prop_raw = meta.get(prop, None)
|
||||||
|
if prop_dict.get("list", False) and prop_raw:
|
||||||
|
post[prop] = [
|
||||||
|
{"name": word, "slug": slugify(word)}
|
||||||
|
for word in prop_raw.split(",")
|
||||||
|
]
|
||||||
|
for item in post[prop]:
|
||||||
|
slug = item["slug"]
|
||||||
|
if slug in posts_prop_index[prop]:
|
||||||
|
posts_prop_index[prop][slug]["posts"].append(post)
|
||||||
|
else:
|
||||||
|
posts_prop_index[prop][slug] = {
|
||||||
|
"name": item["name"],
|
||||||
|
"posts": [post],
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
post[prop] = {"name": prop_raw, "slug": slugify(prop_raw)}
|
||||||
|
slug = post[prop]["slug"]
|
||||||
|
if slug in posts_prop_index[prop]:
|
||||||
|
posts_prop_index[prop][slug]["posts"].append(post)
|
||||||
|
else:
|
||||||
|
posts_prop_index[prop][slug] = {
|
||||||
|
"name": post[prop]["name"],
|
||||||
|
"posts": [post],
|
||||||
|
}
|
||||||
|
|
||||||
|
posts.append(post)
|
||||||
|
|
||||||
|
# For now, list properties must be comma-separated strings.
|
||||||
|
# TODO: make possible to list values as a YAML list
|
||||||
|
|
||||||
|
# Replace stuff
|
||||||
for item in config.replace:
|
for item in config.replace:
|
||||||
mdtext = mdtext.replace(item[0],item[1])
|
mdtext = mdtext.replace(item[0], item[1])
|
||||||
|
|
||||||
# Convert the post into GMI
|
# Convert the post into GMI
|
||||||
gmitext = md2gemini(mdtext,
|
gmitext = md2gemini(
|
||||||
|
mdtext,
|
||||||
code_tag=config.code_tag,
|
code_tag=config.code_tag,
|
||||||
img_tag=config.img_tag,
|
img_tag=config.img_tag,
|
||||||
indent=config.indent,
|
indent=config.indent,
|
||||||
@@ -76,57 +125,61 @@ for dirname, subdirlist, mdlist in os.walk('.'):
|
|||||||
strip_html=config.strip_html,
|
strip_html=config.strip_html,
|
||||||
base_url=config.base_url,
|
base_url=config.base_url,
|
||||||
link_func=add_ext_gmi,
|
link_func=add_ext_gmi,
|
||||||
table_tag=config.table_tag
|
table_tag=config.table_tag,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
post["content"] = gmitext
|
||||||
|
|
||||||
# Read template file
|
# Read template file
|
||||||
with open(tpl_path+"/"+template+".tpl", 'r') as tpl:
|
with open(tpl_path + "/" + post["template"] + ".tpl", "r") as tpl:
|
||||||
template = Template(tpl.read())
|
template = Template(tpl.read())
|
||||||
|
|
||||||
# We need the relative path without the "./"
|
|
||||||
simpledirname = dirname[2:]
|
|
||||||
if simpledirname == "":
|
|
||||||
path = basename
|
|
||||||
else:
|
|
||||||
path = simpledirname + "/" + basename
|
|
||||||
|
|
||||||
# Integrate the GMI content in the template
|
# Integrate the GMI content in the template
|
||||||
gmitext = template.render(
|
gmitext = template.render(post=post)
|
||||||
content=gmitext,
|
|
||||||
tags=tags,
|
|
||||||
template=template,
|
|
||||||
author=author,
|
|
||||||
date=date,
|
|
||||||
title=title,
|
|
||||||
path=path
|
|
||||||
)
|
|
||||||
|
|
||||||
# Dirty fix a weird bug where some lines are CRLF-terminated
|
# Dirty fix a weird bug where some lines are CRLF-terminated
|
||||||
gmitext = gmitext.replace('\r\n','\n')
|
gmitext = gmitext.replace("\r\n", "\n")
|
||||||
|
|
||||||
# Time to write the GMI file
|
# Time to write the GMI file
|
||||||
gmifile = basename
|
with open(gmi_subpath + "/" + gmifile, "w") as gmi:
|
||||||
if config.gmi_extension:
|
|
||||||
gmifile += ".gmi"
|
|
||||||
|
|
||||||
posts_meta.append({
|
|
||||||
"title": title,
|
|
||||||
"author": author,
|
|
||||||
"date": date,
|
|
||||||
"tags": tags,
|
|
||||||
"filename": gmifile
|
|
||||||
})
|
|
||||||
|
|
||||||
print(gmi_subpath+"/"+gmifile)
|
|
||||||
with open(gmi_subpath+"/"+gmifile, 'w') as gmi:
|
|
||||||
gmi.write(gmitext)
|
gmi.write(gmitext)
|
||||||
|
|
||||||
posts_meta.sort(key=lambda p: p["date"], reverse=True)
|
# Generate custom extra pages
|
||||||
|
for page_dict in config.custom_pages:
|
||||||
with open(tpl_path+"/index.tpl", 'r') as tpl:
|
rel_path, filename = os.path.split(page_dict["name"])
|
||||||
|
if rel_path:
|
||||||
|
os.makedirs(rel_path, exist_ok=True)
|
||||||
|
basename, extension = os.path.splitext(filename)
|
||||||
|
if extension == "":
|
||||||
|
filename = basename + config.gmi_extension
|
||||||
|
else:
|
||||||
|
filename = basename + extension
|
||||||
|
filepath = os.path.join(rel_path, filename)
|
||||||
|
with open(tpl_path + "/" + page_dict.get("tpl", basename) + ".tpl", "r") as tpl:
|
||||||
template = Template(tpl.read())
|
template = Template(tpl.read())
|
||||||
|
template.globals['now'] = datetime.now
|
||||||
|
text = template.render(posts=posts)
|
||||||
|
text = text.replace("\r\n", "\n")
|
||||||
|
with open(gmi_path + "/" + filepath, "w") as gmi:
|
||||||
|
gmi.write(text)
|
||||||
|
|
||||||
indextext = template.render(posts_meta=posts_meta)
|
# Generate custom meta pages
|
||||||
|
for prop_dict in config.index_props:
|
||||||
with open(meta_path+"/index.gmi", 'w') as index:
|
prop = prop_dict["property"]
|
||||||
index.write(indextext)
|
if "index_name" in prop_dict:
|
||||||
|
with open(
|
||||||
|
tpl_path + "/" + prop_dict.get("index_tpl", prop) + ".tpl", "r"
|
||||||
|
) as tpl:
|
||||||
|
template = Template(tpl.read())
|
||||||
|
text = template.render(prop=posts_prop_index[prop])
|
||||||
|
with open(gmi_path + "/" + prop_dict["index_name"] + config.gmi_extension, "w") as gmi:
|
||||||
|
gmi.write(text)
|
||||||
|
os.makedirs(gmi_path + "/" + prop_dict.get("item_dir", prop), exist_ok=True)
|
||||||
|
with open(tpl_path + "/" + prop_dict.get("item_tpl", prop) + ".tpl", "r") as tpl:
|
||||||
|
template = Template(tpl.read())
|
||||||
|
for item in posts_prop_index[prop]:
|
||||||
|
text = template.render(prop_item=posts_prop_index[prop][item])
|
||||||
|
with open(
|
||||||
|
gmi_path + "/" + prop_dict.get("item_dir", prop) + "/" + item + config.gmi_extension, "w"
|
||||||
|
) as gmi:
|
||||||
|
gmi.write(text)
|
||||||
|
|||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Jinja2
|
||||||
|
md2gemini
|
||||||
|
python-frontmatter
|
||||||
|
python-slugify
|
||||||
Reference in New Issue
Block a user