- 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 json
|
||||
import os
|
||||
import keys
|
||||
import requests
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
EXIFDATE = re.compile(
|
||||
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})$'
|
||||
)
|
||||
|
||||
class Exif(dict):
|
||||
class CachedMeta(dict):
|
||||
def __init__(self, fpath):
|
||||
self.fpath = fpath
|
||||
self._read()
|
||||
|
||||
@property
|
||||
def cfile(self):
|
||||
return os.path.join(
|
||||
os.path.dirname(self.fpath),
|
||||
".%s.json" % (os.path.basename(self.fpath))
|
||||
".%s.%s.json" % (
|
||||
self.__class__.__name__,
|
||||
os.path.basename(self.fpath)
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -31,7 +37,7 @@ class Exif(dict):
|
|||
|
||||
def _read(self):
|
||||
if not self._is_cached:
|
||||
self._call_exiftool()
|
||||
self._call_tool()
|
||||
self._cache_update()
|
||||
else:
|
||||
self._cache_read()
|
||||
|
@ -44,9 +50,65 @@ class Exif(dict):
|
|||
with open(self.cfile, 'rt') as f:
|
||||
data = json.loads(f.read())
|
||||
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
|
||||
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
|
||||
import requests
|
||||
from pandoc import Pandoc
|
||||
from exiftool import Exif
|
||||
from exiftool import Exif, GoogleVision
|
||||
import settings
|
||||
import keys
|
||||
|
||||
|
@ -717,6 +717,10 @@ class WebImage(object):
|
|||
tmpl = J2.get_template("%s.j2.html" % (self.__class__.__name__))
|
||||
return tmpl.render(self.tmplvars)
|
||||
|
||||
@cached_property
|
||||
def vision(self):
|
||||
return GoogleVision(self.fpath, self.src)
|
||||
|
||||
@cached_property
|
||||
def meta(self):
|
||||
return Exif(self.fpath)
|
||||
|
@ -1737,12 +1741,12 @@ def make():
|
|||
start = int(round(time.time() * 1000))
|
||||
last = 0
|
||||
|
||||
try:
|
||||
makecomments()
|
||||
except Exception as e:
|
||||
logger.error('failed to make comments: %s', e)
|
||||
|
||||
makeposts();
|
||||
if not settings.args.get('nosync'):
|
||||
try:
|
||||
makecomments()
|
||||
except Exception as e:
|
||||
logger.error('failed to make comments: %s', e)
|
||||
makeposts();
|
||||
|
||||
content = settings.paths.get('content')
|
||||
worker = AsyncWorker()
|
||||
|
@ -1817,8 +1821,8 @@ def make():
|
|||
staticfiles = []
|
||||
staticpaths = [
|
||||
os.path.join(content, '*.*'),
|
||||
os.path.join(settings.paths.get('tmpl'), '*.css'),
|
||||
os.path.join(settings.paths.get('tmpl'), '*.js'),
|
||||
#os.path.join(settings.paths.get('tmpl'), '*.css'),
|
||||
#os.path.join(settings.paths.get('tmpl'), '*.js'),
|
||||
]
|
||||
for p in staticpaths:
|
||||
staticfiles = staticfiles + glob.glob(p)
|
||||
|
|
|
@ -3,10 +3,6 @@ import logging
|
|||
|
||||
class Pandoc(str):
|
||||
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 """
|
||||
cmd = (
|
||||
'pandoc',
|
||||
|
|
|
@ -9,9 +9,12 @@
|
|||
{% block meta %}
|
||||
<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" />
|
||||
<style id="css_gallery" media="speech">
|
||||
{% include 'style-gallery.css' %}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% 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>
|
||||
<h1 class="p-name" property="p-name">{{ category.name }}</h1>
|
||||
</header>
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
<style id="css_alt" media="speech">
|
||||
{% include 'style-alt.css' %}
|
||||
</style>
|
||||
<style id="css_kcl" media="speech">
|
||||
{% include 'style-kcl.css' %}
|
||||
</style>
|
||||
<style media="print">
|
||||
{% include 'style-print.css' %}
|
||||
</style>
|
||||
|
@ -72,10 +75,6 @@
|
|||
<svg width="16" height="16">
|
||||
<use xlink:href="#icon-contrast"></use>
|
||||
</svg>
|
||||
<span>
|
||||
<input name="colorscheme" value="auto" id="autoscheme" type="radio">
|
||||
<label for="autoscheme">auto</label>
|
||||
</span>
|
||||
<span>
|
||||
<input name="colorscheme" value="dark" id="darkscheme" type="radio">
|
||||
<label for="darkscheme">dark</label>
|
||||
|
@ -272,6 +271,27 @@
|
|||
</section>
|
||||
{% 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 %}
|
||||
<section class="replies">
|
||||
|
@ -370,12 +390,11 @@
|
|||
alt="Photo of {{ author.name }}"
|
||||
itemprop="image url" />
|
||||
</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">
|
||||
<span itemprop="name">{{ author.name }}</span>
|
||||
</a>
|
||||
<a class="u-email email" property="u-email" rel="me" href="mailto:{{ author.email }}">
|
||||
<span itemprop="email">{{ author.email }}</span>
|
||||
</a>
|
||||
<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>
|
||||
<!--
|
||||
<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>
|
||||
</p>
|
||||
<nav>
|
||||
<ul>
|
||||
|
@ -392,18 +411,6 @@
|
|||
</ul>
|
||||
</nav>
|
||||
</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>
|
||||
<a href="https://xn--sr8hvo.ws/🇻🇮📢/previous">←</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;
|
||||
}
|
||||
|
||||
.encourage {
|
||||
text-align: center;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.encourage h2 {
|
||||
color: #080;
|
||||
}
|
||||
|
||||
.encourage li {
|
||||
margin: 0 0.6em;
|
||||
}
|
||||
|
||||
@media all and (min-width: 58em) {
|
||||
body > header {
|
||||
text-align: unset;
|
||||
|
|
|
@ -23,28 +23,28 @@ function applyTheme(mode) {
|
|||
|
||||
function setTheme(e) {
|
||||
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);
|
||||
}
|
||||
else {
|
||||
localStorage.setItem(STORAGE_KEY, mode);
|
||||
}
|
||||
var e = window.matchMedia('(prefers-color-scheme: ' + ALT_THEME + ')');
|
||||
autoTheme(e);
|
||||
autoTheme(mql);
|
||||
}
|
||||
|
||||
function autoTheme(e) {
|
||||
var mode = DEFAULT_THEME;
|
||||
var current = localStorage.getItem(STORAGE_KEY);
|
||||
var mode = 'auto';
|
||||
var indicate = 'auto';
|
||||
if ( current != null) {
|
||||
indicate = mode = current;
|
||||
mode = current;
|
||||
}
|
||||
else if (e.matches) {
|
||||
mode = ALT_THEME;
|
||||
}
|
||||
applyTheme(mode);
|
||||
indicateTheme(indicate);
|
||||
indicateTheme(mode);
|
||||
}
|
||||
|
||||
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--; ) {
|
||||
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