- moved Tips from footer to bottom of each singular with an "encourage creation" header
- google vision api binding for images (not in use yet) - konami code css preparations - experimental gallery css - minor code cleanups
This commit is contained in:
parent
9af56f89e7
commit
de269ef39e
9 changed files with 191 additions and 49 deletions
74
exiftool.py
74
exiftool.py
|
@ -2,22 +2,28 @@ import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import keys
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
EXIFDATE = re.compile(
|
EXIFDATE = re.compile(
|
||||||
r'^(?P<year>[0-9]{4}):(?P<month>[0-9]{2}):(?P<day>[0-9]{2})\s+'
|
r'^(?P<year>[0-9]{4}):(?P<month>[0-9]{2}):(?P<day>[0-9]{2})\s+'
|
||||||
r'(?P<time>[0-9]{2}:[0-9]{2}:[0-9]{2})$'
|
r'(?P<time>[0-9]{2}:[0-9]{2}:[0-9]{2})$'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Exif(dict):
|
class CachedMeta(dict):
|
||||||
def __init__(self, fpath):
|
def __init__(self, fpath):
|
||||||
self.fpath = fpath
|
self.fpath = fpath
|
||||||
self._read()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cfile(self):
|
def cfile(self):
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
os.path.dirname(self.fpath),
|
os.path.dirname(self.fpath),
|
||||||
".%s.json" % (os.path.basename(self.fpath))
|
".%s.%s.json" % (
|
||||||
|
self.__class__.__name__,
|
||||||
|
os.path.basename(self.fpath)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -31,7 +37,7 @@ class Exif(dict):
|
||||||
|
|
||||||
def _read(self):
|
def _read(self):
|
||||||
if not self._is_cached:
|
if not self._is_cached:
|
||||||
self._call_exiftool()
|
self._call_tool()
|
||||||
self._cache_update()
|
self._cache_update()
|
||||||
else:
|
else:
|
||||||
self._cache_read()
|
self._cache_read()
|
||||||
|
@ -44,9 +50,65 @@ class Exif(dict):
|
||||||
with open(self.cfile, 'rt') as f:
|
with open(self.cfile, 'rt') as f:
|
||||||
data = json.loads(f.read())
|
data = json.loads(f.read())
|
||||||
for k, v in data.items():
|
for k, v in data.items():
|
||||||
self[k] = self.exifdate2rfc(v)
|
self[k] = v
|
||||||
|
|
||||||
def _call_exiftool(self):
|
class GoogleVision(CachedMeta):
|
||||||
|
def __init__(self, fpath, imgurl):
|
||||||
|
self.fpath = fpath
|
||||||
|
self.imgurl = imgurl
|
||||||
|
self._read()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cntr(self):
|
||||||
|
curr = 0
|
||||||
|
if os.path.exists('/tmp/visionapicallcounter'):
|
||||||
|
with open('/tmp/visionapicallcounter', 'rt') as f:
|
||||||
|
curr = int(f.read())
|
||||||
|
curr = curr + 1
|
||||||
|
with open('/tmp/visionapicallcounter', 'wt') as f:
|
||||||
|
f.write("%d" % curr)
|
||||||
|
return curr
|
||||||
|
|
||||||
|
def _call_tool(self):
|
||||||
|
if (self.cntr >= 500 ):
|
||||||
|
raise ValueError('already at 500 requests!')
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"requests": [
|
||||||
|
{
|
||||||
|
"image": {
|
||||||
|
"source": {
|
||||||
|
"imageUri": self.imgurl,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"type": "LANDMARK_DETECTION",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "LABEL_DETECTION",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
url = "https://vision.googleapis.com/v1/images:annotate?key=%s" % (keys.gcloud.get('key'))
|
||||||
|
r = requests.post(url, json=params)
|
||||||
|
try:
|
||||||
|
resp = r.json()
|
||||||
|
resp = resp['responses'][0]
|
||||||
|
for k, v in resp.items():
|
||||||
|
self[k] = v
|
||||||
|
except Exception as e:
|
||||||
|
logging.error('failed to call Google Vision API on: %s, reason: %s', self.fpath, e)
|
||||||
|
|
||||||
|
class Exif(CachedMeta):
|
||||||
|
def __init__(self, fpath):
|
||||||
|
self.fpath = fpath
|
||||||
|
self._read()
|
||||||
|
|
||||||
|
def _call_tool(self):
|
||||||
"""
|
"""
|
||||||
Why like this: the # on some of the params forces exiftool to
|
Why like this: the # on some of the params forces exiftool to
|
||||||
display values like decimals, so the latitude / longitude params
|
display values like decimals, so the latitude / longitude params
|
||||||
|
|
22
nasg.py
22
nasg.py
|
@ -31,7 +31,7 @@ from emoji import UNICODE_EMOJI
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
import requests
|
import requests
|
||||||
from pandoc import Pandoc
|
from pandoc import Pandoc
|
||||||
from exiftool import Exif
|
from exiftool import Exif, GoogleVision
|
||||||
import settings
|
import settings
|
||||||
import keys
|
import keys
|
||||||
|
|
||||||
|
@ -717,6 +717,10 @@ class WebImage(object):
|
||||||
tmpl = J2.get_template("%s.j2.html" % (self.__class__.__name__))
|
tmpl = J2.get_template("%s.j2.html" % (self.__class__.__name__))
|
||||||
return tmpl.render(self.tmplvars)
|
return tmpl.render(self.tmplvars)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def vision(self):
|
||||||
|
return GoogleVision(self.fpath, self.src)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def meta(self):
|
def meta(self):
|
||||||
return Exif(self.fpath)
|
return Exif(self.fpath)
|
||||||
|
@ -1737,12 +1741,12 @@ def make():
|
||||||
start = int(round(time.time() * 1000))
|
start = int(round(time.time() * 1000))
|
||||||
last = 0
|
last = 0
|
||||||
|
|
||||||
try:
|
if not settings.args.get('nosync'):
|
||||||
makecomments()
|
try:
|
||||||
except Exception as e:
|
makecomments()
|
||||||
logger.error('failed to make comments: %s', e)
|
except Exception as e:
|
||||||
|
logger.error('failed to make comments: %s', e)
|
||||||
makeposts();
|
makeposts();
|
||||||
|
|
||||||
content = settings.paths.get('content')
|
content = settings.paths.get('content')
|
||||||
worker = AsyncWorker()
|
worker = AsyncWorker()
|
||||||
|
@ -1817,8 +1821,8 @@ def make():
|
||||||
staticfiles = []
|
staticfiles = []
|
||||||
staticpaths = [
|
staticpaths = [
|
||||||
os.path.join(content, '*.*'),
|
os.path.join(content, '*.*'),
|
||||||
os.path.join(settings.paths.get('tmpl'), '*.css'),
|
#os.path.join(settings.paths.get('tmpl'), '*.css'),
|
||||||
os.path.join(settings.paths.get('tmpl'), '*.js'),
|
#os.path.join(settings.paths.get('tmpl'), '*.js'),
|
||||||
]
|
]
|
||||||
for p in staticpaths:
|
for p in staticpaths:
|
||||||
staticfiles = staticfiles + glob.glob(p)
|
staticfiles = staticfiles + glob.glob(p)
|
||||||
|
|
|
@ -3,10 +3,6 @@ import logging
|
||||||
|
|
||||||
class Pandoc(str):
|
class Pandoc(str):
|
||||||
def __new__(cls, text):
|
def __new__(cls, text):
|
||||||
# TODO: cache?
|
|
||||||
# import hashlib
|
|
||||||
# print(hashlib.md5("whatever your string is".encode('utf-8')).hexdigest())
|
|
||||||
|
|
||||||
""" Pandoc command line call with piped in- and output """
|
""" Pandoc command line call with piped in- and output """
|
||||||
cmd = (
|
cmd = (
|
||||||
'pandoc',
|
'pandoc',
|
||||||
|
|
|
@ -9,9 +9,12 @@
|
||||||
{% block meta %}
|
{% block meta %}
|
||||||
<link rel="alternate" type="application/rss+xml" title="{{ category.title }} RSS feed" href="{{ category.feed }}" />
|
<link rel="alternate" type="application/rss+xml" title="{{ category.title }} RSS feed" href="{{ category.feed }}" />
|
||||||
<link rel="alternate" type="application/atom+xml" title="{{ category.title }} ATOM feed" href="{{ category.feed }}atom.xml" />
|
<link rel="alternate" type="application/atom+xml" title="{{ category.title }} ATOM feed" href="{{ category.feed }}atom.xml" />
|
||||||
|
<style id="css_gallery" media="speech">
|
||||||
|
{% include 'style-gallery.css' %}
|
||||||
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main class="content-body h-feed hfeed" property="h-feed">
|
<main class="content-body h-feed hfeed {{ category.name }}" property="h-feed">
|
||||||
<header>
|
<header>
|
||||||
<h1 class="p-name" property="p-name">{{ category.name }}</h1>
|
<h1 class="p-name" property="p-name">{{ category.name }}</h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
<style id="css_alt" media="speech">
|
<style id="css_alt" media="speech">
|
||||||
{% include 'style-alt.css' %}
|
{% include 'style-alt.css' %}
|
||||||
</style>
|
</style>
|
||||||
|
<style id="css_kcl" media="speech">
|
||||||
|
{% include 'style-kcl.css' %}
|
||||||
|
</style>
|
||||||
<style media="print">
|
<style media="print">
|
||||||
{% include 'style-print.css' %}
|
{% include 'style-print.css' %}
|
||||||
</style>
|
</style>
|
||||||
|
@ -72,10 +75,6 @@
|
||||||
<svg width="16" height="16">
|
<svg width="16" height="16">
|
||||||
<use xlink:href="#icon-contrast"></use>
|
<use xlink:href="#icon-contrast"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<span>
|
|
||||||
<input name="colorscheme" value="auto" id="autoscheme" type="radio">
|
|
||||||
<label for="autoscheme">auto</label>
|
|
||||||
</span>
|
|
||||||
<span>
|
<span>
|
||||||
<input name="colorscheme" value="dark" id="darkscheme" type="radio">
|
<input name="colorscheme" value="dark" id="darkscheme" type="radio">
|
||||||
<label for="darkscheme">dark</label>
|
<label for="darkscheme">dark</label>
|
||||||
|
@ -272,6 +271,27 @@
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<section class="encourage">
|
||||||
|
<hr />
|
||||||
|
<h2>Encourage creation!</h2>
|
||||||
|
<p>If this entry helped you, or you simply liked it, leave a tip.</p>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a rel="payment" href="https://paypal.me/petermolnar/3GBP">
|
||||||
|
<svg width="16" height="16">
|
||||||
|
<use xlink:href="#icon-paypal"></use>
|
||||||
|
</svg> Paypal</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a rel="payment" href="https://monzo.me/petermolnar/3">
|
||||||
|
<svg width="16" height="16">
|
||||||
|
<use xlink:href="#icon-monzo"></use>
|
||||||
|
</svg> Monzo (UK or Google Pay)</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</section>
|
||||||
|
|
||||||
{% if post.replies|length %}
|
{% if post.replies|length %}
|
||||||
<section class="replies">
|
<section class="replies">
|
||||||
|
@ -370,12 +390,11 @@
|
||||||
alt="Photo of {{ author.name }}"
|
alt="Photo of {{ author.name }}"
|
||||||
itemprop="image url" />
|
itemprop="image url" />
|
||||||
</span>
|
</span>
|
||||||
<a class="fn p-name url u-url u-uid" property="p-name u-url u-uid" rel="me" href="{{ site.url }}/about.html">
|
<a class="fn p-name url u-url u-uid" property="p-name u-url u-uid" rel="me" href="{{ site.url }}"><span itemprop="name">{{ author.name }}</span></a>
|
||||||
<span itemprop="name">{{ author.name }}</span>
|
<!--
|
||||||
</a>
|
<a class="u-url" property="u-url" rel="me" href="{{ site.url }}/about.html">CV</a>
|
||||||
<a class="u-email email" property="u-email" rel="me" href="mailto:{{ author.email }}">
|
-->
|
||||||
<span itemprop="email">{{ author.email }}</span>
|
<a class="u-email email" property="u-email" rel="me" href="mailto:{{ author.email }}"><span itemprop="email">{{ author.email }}</span></a>
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -392,18 +411,6 @@
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
|
||||||
Leave a tip!
|
|
||||||
<a rel="payment" href="{{ tips.paypal }}">
|
|
||||||
<svg width="16" height="16">
|
|
||||||
<use xlink:href="#icon-paypal"></use>
|
|
||||||
</svg> Paypal</a>
|
|
||||||
or
|
|
||||||
<a rel="payment" href="{{ tips.monzo }}">
|
|
||||||
<svg width="16" height="16">
|
|
||||||
<use xlink:href="#icon-monzo"></use>
|
|
||||||
</svg> Monzo</a> (UK only).
|
|
||||||
</p>
|
|
||||||
<p>
|
<p>
|
||||||
<a href="https://xn--sr8hvo.ws/🇻🇮📢/previous">←</a>
|
<a href="https://xn--sr8hvo.ws/🇻🇮📢/previous">←</a>
|
||||||
Member of <a href="https://xn--sr8hvo.ws">IndieWeb Webring</a>
|
Member of <a href="https://xn--sr8hvo.ws">IndieWeb Webring</a>
|
||||||
|
|
34
templates/style-gallery.css
Normal file
34
templates/style-gallery.css
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
main.photo {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
main.photo article {
|
||||||
|
max-width: 24em;
|
||||||
|
margin: 0.6em;
|
||||||
|
display: grid;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
main.photo article figure {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main.photo article h2 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.6em;
|
||||||
|
background: rgba(0,0,0,0.7);
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main.photo article figcaption,
|
||||||
|
main.photo article p,
|
||||||
|
main.photo article section {
|
||||||
|
display: none;
|
||||||
|
}
|
5
templates/style-kcl.css
Normal file
5
templates/style-kcl.css
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
body {
|
||||||
|
font-family: serif;
|
||||||
|
background-color: #0f0;
|
||||||
|
color: #f00;
|
||||||
|
}
|
|
@ -319,6 +319,19 @@ body > footer h2,
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.encourage {
|
||||||
|
text-align: center;
|
||||||
|
padding-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.encourage h2 {
|
||||||
|
color: #080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.encourage li {
|
||||||
|
margin: 0 0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
@media all and (min-width: 58em) {
|
@media all and (min-width: 58em) {
|
||||||
body > header {
|
body > header {
|
||||||
text-align: unset;
|
text-align: unset;
|
||||||
|
|
|
@ -23,28 +23,28 @@ function applyTheme(mode) {
|
||||||
|
|
||||||
function setTheme(e) {
|
function setTheme(e) {
|
||||||
var mode = e.target.value;
|
var mode = e.target.value;
|
||||||
if (mode == 'auto') {
|
var mql = window.matchMedia('(prefers-color-scheme: ' + ALT_THEME + ')');
|
||||||
|
/* user wants == mql match => remove storage */
|
||||||
|
if ((mode == DEFAULT_THEME && !mql.matches) || (mode == ALT_THEME && mql.matches)) {
|
||||||
localStorage.removeItem(STORAGE_KEY);
|
localStorage.removeItem(STORAGE_KEY);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
localStorage.setItem(STORAGE_KEY, mode);
|
localStorage.setItem(STORAGE_KEY, mode);
|
||||||
}
|
}
|
||||||
var e = window.matchMedia('(prefers-color-scheme: ' + ALT_THEME + ')');
|
autoTheme(mql);
|
||||||
autoTheme(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function autoTheme(e) {
|
function autoTheme(e) {
|
||||||
|
var mode = DEFAULT_THEME;
|
||||||
var current = localStorage.getItem(STORAGE_KEY);
|
var current = localStorage.getItem(STORAGE_KEY);
|
||||||
var mode = 'auto';
|
|
||||||
var indicate = 'auto';
|
|
||||||
if ( current != null) {
|
if ( current != null) {
|
||||||
indicate = mode = current;
|
mode = current;
|
||||||
}
|
}
|
||||||
else if (e.matches) {
|
else if (e.matches) {
|
||||||
mode = ALT_THEME;
|
mode = ALT_THEME;
|
||||||
}
|
}
|
||||||
applyTheme(mode);
|
applyTheme(mode);
|
||||||
indicateTheme(indicate);
|
indicateTheme(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
var mql = window.matchMedia('(prefers-color-scheme: ' + ALT_THEME + ')');
|
var mql = window.matchMedia('(prefers-color-scheme: ' + ALT_THEME + ')');
|
||||||
|
@ -59,3 +59,21 @@ var themeforms = document.getElementsByClassName(STORAGE_KEY);
|
||||||
for(var i = themeforms.length; i--; ) {
|
for(var i = themeforms.length; i--; ) {
|
||||||
themeforms[i].style.display = 'inline-block';
|
themeforms[i].style.display = 'inline-block';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function kcl(cb) {
|
||||||
|
var input = '';
|
||||||
|
var key = '38384040373937396665';
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
input += ("" + e.keyCode);
|
||||||
|
if (input === key) {
|
||||||
|
return cb();
|
||||||
|
}
|
||||||
|
if (!key.indexOf(input)) return;
|
||||||
|
input = ("" + e.keyCode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
kcl(function () {
|
||||||
|
var st = document.getElementById('css_kcl');
|
||||||
|
st.setAttribute('media', 'all');
|
||||||
|
})
|
||||||
|
|
Loading…
Reference in a new issue