removed enclosure from rss; re-added initial steps for sending webmentions for replies; minor cleanups

This commit is contained in:
Peter Molnar 2017-10-30 10:47:08 +00:00
parent 0fc792fe00
commit 3bef504be1
6 changed files with 110 additions and 55 deletions

View file

@ -1,17 +1,36 @@
# NASG (Not Another Static Generator) # NASG (Not Another Static Generator...)
This is a tiny static site generator, written in Python, to scratch my own itches. This is a tiny static site generator, written in Python, to scratch my own itches.
It is most probably not suitable for anyone else. It is most probably not suitable for anyone else, but feel free to use it for ideas. Keep in mind that the project is licenced under GPL.
## Why not [insert static generator here]? ## Why not [insert static generator here]?
- DRY - Don't Repeat Yourself - is good, so instead of sidefiles for images, I'm using XMP metadata, which most of the ones availabe don't handle well; - DRY - Don't Repeat Yourself - is good, so instead of sidefiles for images, I'm using XMP metadata, which most of the ones availabe don't handle well;
- writing a proper plugin to existing generators - Pelican, Nicola, etc - might have taken longer and I wanted to extend my Python knowledge - writing plugins to existing generators - Pelican, Nicola, etc - might have taken longer and I wanted to extend my Python knowledge
- I wanted to use the best available utilities for some tasks, like `Pandoc` and `exiftool` instead of Python libraries trying to achive the same - I wanted to use the best available utilities for some tasks, like `Pandoc` and `exiftool` instead of Python libraries trying to achive the same
- I needed to handle webmentions and comments - I needed to handle webmentions and comments
Don't expect anything fancy: my Python Fu has much to learn. Don't expect anything fancy: my Python Fu has much to learn.
## Install
### External dependencies
PHP is in order to use [XRay](https://github.com/aaronpk/XRay/)
```
apt-get install pandoc exiftool php7.0-bcmath php7.0-bz2 php7.0-cli php7.0-common php7.0-curl php7.0-gd php7.0-imap php7.0-intl php7.0-json php7.0-mbstring php7.0-mcrypt php7.0-mysql php7.0-odbc php7.0-opcache php7.0-readline php7.0-sqlite3 php7.0-xml php7.0-zip python3 python3-pip python3-dev
```
Get XRay:
```
mkdir /usr/local/lib/php
cd /usr/local/lib/php
wget https://github.com/aaronpk/XRay/releases/download/v1.3.1/xray-app.zip
unzip xray-app.zip
rm xray-app.zip
```
## How content is organized ## How content is organized
The directory structure of the "source" is something like this: The directory structure of the "source" is something like this:

View file

@ -1,3 +1 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
__version__ = '2.0.0'

69
nasg.py
View file

@ -13,7 +13,6 @@ import asyncio
from math import ceil from math import ceil
import csv import csv
import sqlite3 import sqlite3
import magic
import frontmatter import frontmatter
import arrow import arrow
@ -191,7 +190,6 @@ class Category(NoDupeContainer):
@property @property
def title(self): def title(self):
# TODO proper title
return self.name return self.name
@property @property
@ -311,26 +309,37 @@ class Singular(object):
def init_extras(self): def init_extras(self):
self.process_webmentions() self.receive_webmentions()
c = self.comments c = self.comments
# note: due to SQLite locking, this will not be async for now
# TODO this should be async def receive_webmentions(self):
def process_webmentions(self):
wdb = shared.WebmentionQueue() wdb = shared.WebmentionQueue()
queued = wdb.get_queued(self.url) queued = wdb.get_queued(self.url)
for incoming in queued: for incoming in queued:
wm = Webmention( wm = Webmention(
incoming.get('id'),
incoming.get('source'), incoming.get('source'),
incoming.get('target'), incoming.get('target'),
incoming.get('dt') incoming.get('dt')
) )
wm.run() wm.receive()
wdb.entry_done(incoming.get('id')) wdb.entry_done(incoming.get('id'))
wdb.finish() wdb.finish()
# note: due to SQLite locking, this will not be async for now
def send_webmentions(self):
if not self.is_reply:
return
wdb = shared.WebmentionQueue()
id = wdb.queue(self.url, self.is_reply)
wm = Webmention(
self.url,
self.is_reply
)
wm.send()
wdb.entry_done(id)
wdb.finish()
@property @property
def redirects(self): def redirects(self):
r = self.meta.get('redirect', []) r = self.meta.get('redirect', [])
@ -523,9 +532,6 @@ class Singular(object):
im.title = title im.title = title
im.cssclass = css im.cssclass = css
body = body.replace(shortcode, str(im)) body = body.replace(shortcode, str(im))
# TODO if multiple meta images, inline all except the first
# which will be added at the HTML stage or as enclosure to the feed
return body return body
@property @property
@ -558,16 +564,6 @@ class Singular(object):
def shortslug(self): def shortslug(self):
return shared.baseN(self.pubtime) return shared.baseN(self.pubtime)
@property
def enclosure(self):
if not self.photo:
return {}
return {
'length': os.path.getsize(self.photo.fpath),
'url': self.photo.href,
'mime': magic.Magic(mime=True).from_file(self.photo.fpath),
}
@property @property
def tmplvars(self): def tmplvars(self):
# very simple caching because we might use this 4 times: # very simple caching because we might use this 4 times:
@ -584,13 +580,11 @@ class Singular(object):
'slug': self.fname, 'slug': self.fname,
'shortslug': self.shortslug, 'shortslug': self.shortslug,
'licence': self.licence, 'licence': self.licence,
#'sourceurl': self.sourceurl,
'is_reply': self.is_reply, 'is_reply': self.is_reply,
'age': int(self.published.format('YYYY')) - int(arrow.utcnow().format('YYYY')), 'age': int(self.published.format('YYYY')) - int(arrow.utcnow().format('YYYY')),
'summary': self.summary, 'summary': self.summary,
'replies': self.replies, 'replies': self.replies,
'reactions': self.reactions, 'reactions': self.reactions,
'enclosure': self.enclosure,
} }
return self._tmplvars return self._tmplvars
@ -1049,10 +1043,9 @@ class Comment(object):
class Webmention(object): class Webmention(object):
def __init__ (self, id, source, target, dt): def __init__ (self, source, target, dt=arrow.utcnow().timestamp):
self.source = source self.source = source
self.target = target self.target = target
self.id = id
self.dt = arrow.get(dt).to('utc') self.dt = arrow.get(dt).to('utc')
logging.info( logging.info(
"processing webmention %s => %s", "processing webmention %s => %s",
@ -1071,7 +1064,26 @@ class Webmention(object):
f.write(frontmatter.dumps(fm)) f.write(frontmatter.dumps(fm))
return return
def run(self): def send(self):
rels = shared.XRay(self.source).set_discover().parse()
endpoint = False
if 'rels' not in rels:
return
for k in rels.get('rels').keys():
if 'webmention' in k:
endpoint = rels.get('rels').get(k)
break
if not endpoint:
return
requests.post(
self.target,
data = {
'source': self.source,
'target': self.target
}
)
def receive(self):
self._fetch() self._fetch()
if 'data' not in self._source: if 'data' not in self._source:
return return
@ -1131,7 +1143,6 @@ class Webmention(object):
self.fname self.fname
) )
def setup(): def setup():
""" parse input parameters and add them as params section to config """ """ parse input parameters and add them as params section to config """
parser = argparse.ArgumentParser(description='Parameters for NASG') parser = argparse.ArgumentParser(description='Parameters for NASG')
@ -1281,8 +1292,6 @@ def build():
tasks.append(task) tasks.append(task)
# TODO: send webmentions to any url # TODO: send webmentions to any url
# TODO: comments
# TODO: ping websub?
# do all the things! # do all the things!
w = asyncio.wait(tasks) w = asyncio.wait(tasks)

View file

@ -1,16 +1,25 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
from . import __version__
setup( setup(
version=__version__, version='2.0.0',
name="nasg", name="nasg",
author="Peter Molnar", author="Peter Molnar",
author_email="hello@petermolnar.eu", author_email="hello@petermolnar.eu",
description="Not Another Static Generator - a static generator", description="Not Another Static Generator - a static generator",
long_description=open('README.md').read(), long_description=open('README.md').read(),
packages=['nasg'], packages=['nasg'],
install_requires=['arrow', 'Jinja2', 'langdetect', 'requests', 'requests-oauthlib', 'sanic', 'unicode-slugify', 'Wand', 'emoji', 'html5lib', 'BeautifulSoup'], install_requires=[
'arrow',
'Jinja2',
'langdetect',
'requests',
'requests-oauthlib',
'sanic',
'unicode-slugify',
'Wand',
'emoji',
],
url='https://github.com/petermolnar/nasg', url='https://github.com/petermolnar/nasg',
license=open('LICENCE').read(), license=open('./LICENSE').read(),
include_package_data=True, include_package_data=True,
) )

View file

@ -7,7 +7,6 @@ import subprocess
import json import json
import sqlite3 import sqlite3
import requests import requests
from slugify import slugify from slugify import slugify
import jinja2 import jinja2
@ -28,21 +27,48 @@ class CMDLine(object):
class XRay(CMDLine): class XRay(CMDLine):
xraypath = '/usr/local/lib/php/xray' cmd_prefix = 'chdir("/usr/local/lib/php/xray"); include("vendor/autoload.php"); $xray = new p3k\XRay();'
def __init__(self, url): def __init__(self, url):
super().__init__('php') super().__init__('php')
self.url = url self.url = url
self.target = ''
def parse(self): self.cmd = (
cmd = (
self.executable, self.executable,
'-r', '-r',
'''chdir("%s"); include("vendor/autoload.php"); $xray = new p3k\XRay(); echo(json_encode($xray->parse("%s")));''' % (self.xraypath, self.url) '%s; echo(json_encode($xray->parse("%s")));' % (
self.cmd_prefix,
self.url
) )
)
def set_receive(self, target):
self.cmd = (
self.executable,
'-r',
'%s; echo(json_encode($xray->parse("%s")));' % (
self.cmd_prefix,
self.url,
target
)
)
return self
def set_discover(self):
self.cmd = (
self.executable,
'-r',
'%s; echo(json_encode($xray->rels("%s")));' % (
self.cmd_prefix,
self.url,
)
)
return self
def parse(self):
logging.debug('pulling %s with XRay', self.url) logging.debug('pulling %s with XRay', self.url)
p = subprocess.Popen( p = subprocess.Popen(
cmd, self.cmd,
stdin=subprocess.PIPE, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
@ -54,7 +80,6 @@ class XRay(CMDLine):
return json.loads(stdout.decode('utf-8').strip()) return json.loads(stdout.decode('utf-8').strip())
class Pandoc(CMDLine): class Pandoc(CMDLine):
""" Pandoc command line call with piped in- and output """ """ Pandoc command line call with piped in- and output """
@ -439,7 +464,9 @@ class WebmentionQueue(object):
target target
) )
) )
r = cursor.lastrowid
self.db.commit() self.db.commit()
return r
def get_queued(self, fname=None): def get_queued(self, fname=None):
logging.debug('getting queued webmentions for %s', fname) logging.debug('getting queued webmentions for %s', fname)

View file

@ -19,13 +19,6 @@
{%- if post.tags %}{% for tname in post.tags %} {%- if post.tags %}{% for tname in post.tags %}
<category>{{ tname }}></category> <category>{{ tname }}></category>
{% endfor %}{% endif -%} {% endfor %}{% endif -%}
{%- if post.rssenclosure %}
<enclosure
url="{{ post.enclosure.url }}"
type="{{ post.enclosure.mime }}"
length="{{ post.enclosure.size }}"
/>
{% endif -%}
</item> </item>
{% endfor %} {% endfor %}
</channel> </channel>