Browse Source

Add Atom feed generation!

master
mieum 1 year ago
parent
commit
f5e0885277
  1. 26
      README.md
  2. 81
      twindexer.py

26
README.md

@ -10,7 +10,7 @@ Twindexer was built to generate gemlog indexes in which the posts are categorize
* Titles, labels, and folder names of tags can all be different
* Index files can be named an located anywhere in the capsule
* Manage multiple capsules using any number of separate config files, which can be located anywhere, even inside your capsule!
* Optionally generate a link to an atom feed for every index (although twindexer does not yet generate the feeds itself!)
* Optionally generate an atom feed for each index.
## Installation and Use
@ -30,8 +30,12 @@ The configuration file is in YAML format, and can be nammed and located anywhere
```
baseURL:
gemroot:
capsule:
baseURL:
gemroot:
author:
email:
license:
gemlog:
title:
index:
@ -61,8 +65,11 @@ taglist:
...
```
### Global Variables
* `baseURL`: the base url of capsule (eg. gemini://example.com/~user/), used for feed generation (not yet implemented)
* `baseURL`: the base url of capsule (eg. gemini://example.com/~user/), used for feed generation
* `gemroot`: root directory of capsule (on machine where twindexer runs).
* `author`: your name/pseudonym (used for feed generation)
* `email`: your email address (used for feed generation, can be formatted however you like, e.g. mieum(at)rawtext.club)
* `license`: e.g. CC-BY-SA 4.0 (used for feed generation
### Gemlog Variables
* `gemlog`: section containing key-value pairs pertaining to gemlog index
@ -91,8 +98,12 @@ TIP: values can be reused using the anchor(&) and alias(*) notation. See sample
This is the configuration live on my capsule at the time of writing this. An updated version can be found on my capsule itself at [rawtext.club/~mieum/twindexer.yml](rawtext.club/~mieum/twindexer.yml)
```YAML
baseURL: gemini://rawtext.club/~mieum/
gemroot: ~/var/twindexer/
capsule:
baseURL: gemini://rawtext.club/~mieum/
gemroot: ~/var/twindexer/
author: mieum
email: mieum(at)rawtext.club
license: 'CC-BY-NC-SA 4.0'
gemlog:
title: mieum's rawtext gemlog
index: gemlog.gmi
@ -194,8 +205,5 @@ taglist:
Some things I am working on implementing:
* atom feed generation for each index
* gempub archive generation for each index and entire capsule
* management of non-tag pages (feed generation, indexing, etc.)
## Disclaimer

81
twindexer.py

@ -6,6 +6,8 @@ import os
import os.path
import stat
from itertools import islice
import yaml
gemindex = []
@ -30,9 +32,48 @@ def get_date(filename):
basename = os.path.basename(filename)
return basename[0:10]
# find path relative to target
def relate_paths(source, target):
return os.path.relpath(target, os.path.dirname(source))
# generate absolute url for posts (to use in feed)
def absolution(url, index_path, post_path):
paths = [index_path, post_path]
common = os.path.commonprefix(paths)
rel = os.path.relpath(post_path, common)
return url + rel
# generate an atom feed from indexes
def atomizer(cap, entries, tag, gemlog=False):
gr = os.path.expanduser(cap['gemroot'])
url = cap['baseURL']
if gemlog:
local_path = gr + os.path.dirname(tag['index'])
gemini_path = url + os.path.dirname(tag['index'])
else:
local_path = gr + tag['folder']
gemini_path = url + tag['folder']
atompath = local_path + "atom.xml"
atomurl = gemini_path + "atom.xml"
feed_header = "<?xml version='1.0' encoding='UTF-8'?>\n<feed xmlns='http://www.w3.org/2005/Atom'>\n"
feed_id = " <id>" + gemini_path + "</id>\n"
feed_title = " <title>" + tag['title'] + "</title>\n"
feed_updated = " <updated>" + entries[0]['date'] + "T00:00:00+00:00" + "</updated>\n"
feed_link = " <link href='" + atomurl + "' rel='self'/>\n <link href='" + gemini_path + "' rel='alternate'/>\n"
feed_generator = " <generator uri='gemini://namu.blue/~mieum/git/twindexer/'>twindexer</generator>\n"
feed_author = " <author>\n <name>" + cap['author'] + "</name>\n <email>" + cap['email'] + "</email>\n <uri>" + url + "</uri>\n </author>\n"
feed_rights = " <rights>" + cap['license'] + "</rights>\n"
feed_preable = [feed_header, feed_id, feed_title, feed_updated, feed_link, feed_author, feed_rights, feed_generator]
print("(twindexer) Writing feed for '" + tag['title'] + "'")
with open(atompath, 'w') as atom:
for line in feed_preable:
atom.write(line)
for entry in islice(entries, 10):
entry_url = absolution(url, gr, entry['path'])
atom.write(" <entry>\n <title>" + entry['title'] + "</title>\n <updated>" + entry['date'] + "T00:00:00+00:00" + "</updated>\n <id>" + entry_url + "</id>\n <link href='" + entry_url + "' rel='alternate'/>\n </entry>\n")
atom.write("</feed>")
atom.close()
# find all world readable gemtext files
def find_files(directory):
files = []
@ -48,8 +89,10 @@ def find_files(directory):
def sort_time(d):
return d['date']
def inventory_posts(tl, gr):
print("[twindexer] Taking inventory")
def inventory_posts(cap, tl):
print("(twindexer) Taking inventory")
gr = os.path.expanduser(cap['gemroot'])
url = cap['baseURL']
for tag in tl:
path = gr + tl[tag]['folder']
posts = find_files(path)
@ -63,14 +106,15 @@ def inventory_posts(tl, gr):
continue
# sort and write tag index and clear the list
tag_index.sort(key=sort_time, reverse=True)
write_tag_index(gr, tag_index, tl[tag])
write_tag_index(cap, tag_index, tl[tag])
tag_index.clear()
# write indexes for individual tags
def write_tag_index(gr, posts, tag):
def write_tag_index(cap, posts, tag):
gr = os.path.expanduser(cap['gemroot'])
path = gr + tag['folder']
index = gr + tag['index']
print("[twindexer] Writing index for posts in " + tag['folder'])
print("(twindexer) Writing index for '" + tag['title'] + "'")
with open(index, 'w') as outfile:
if 'header' in tag:
if os.path.isfile(os.path.expanduser(tag['header'])):
@ -89,17 +133,21 @@ def write_tag_index(gr, posts, tag):
outfile.write("=> " + postpath + " " + post['date'] + " " + sep + " " + post['title'] + "\n")
if 'feed' in tag:
if tag['feed'] == True:
outfile.write("\n=> atom.xml" + " Feed\n")
atomizer(cap, posts, tag)
feed_path = relate_paths(index, os.path.expanduser(path + "atom.xml"))
outfile.write("\n=> " + feed_path + " feed\n")
if 'footer' in tag:
if os.path.isfile(os.path.expanduser(tag['footer'])):
with open(os.path.expanduser(tag['footer'])) as infile:
outfile.write(infile.read())
else:
outfile.write(tag['footer'])
outfile.close()
def write_gemlog_index(gr, gl, tl):
print("[twindexer] Writing gemlog index")
def write_gemlog_index(cap, gl, tl):
print("(twindexer) Writing index for '" + gl['title'] + "'")
gemindex.sort(key=sort_time, reverse=True)
gr = os.path.expanduser(cap['gemroot'])
gempath = gr + gl['index']
with open(os.path.expanduser(gempath), 'w') as outfile:
if 'header' in gl:
@ -120,13 +168,15 @@ def write_gemlog_index(gr, gl, tl):
outfile.write("=> " + postpath + " " + post['date'] + " [" + post['tag'] + "] " + post['title'] + "\n")
if 'feed' in gl:
if gl['feed'] == True:
outfile.write("\n=> atom.xml Feed\n")
atomizer(cap, gemindex, gl, gemlog=True)
outfile.write("\n=> atom.xml feed\n")
if 'footer' in gl:
if os.path.isfile(os.path.expanduser(gl['footer'])):
with open(os.path.expanduser(gl['footer']), 'r') as infile:
outfile.write(infile.read())
else:
outfile.write(gl['footer'])
outfile.close()
def main():
parser = argparse.ArgumentParser(
@ -151,18 +201,17 @@ to run using the '-c' flag.
with open(os.path.expanduser(args.config)) as c:
config = yaml.load(c, Loader=yaml.FullLoader)
baseURL = config['baseURL']
gemroot = os.path.expanduser(config['gemroot'])
capsule = config['capsule']
gemlog = config['gemlog']
taglist = config['taglist']
# populate lists with posts
inventory_posts(taglist, gemroot)
# populate lists with posts, and write tag indexes/feeds
inventory_posts(capsule, taglist)
# sort and write gemlog index
write_gemlog_index(gemroot, gemlog, taglist)
# sort and write gemlog index/feed
write_gemlog_index(capsule, gemlog, taglist)
print("[twindexer] Done! Capsule is ready for launch!")
print("(twindexer) Done! Capsule is ready for launch!")
if __name__ == '__main__':
main()

Loading…
Cancel
Save