From e10ff6b2c5385ddd7d287b00735020d3d924cb6e Mon Sep 17 00:00:00 2001 From: dave Date: Sun, 9 Sep 2018 13:45:26 -0700 Subject: [PATCH] Map and photo view skeleton --- package-lock.json | 153 +++++++++++++++++++++------------------- photoapp/daemon.py | 25 ++++--- photoapp/image.py | 19 ++--- photoapp/library.py | 12 ++-- photoapp/types.py | 1 + styles/dist/unknown.svg | 4 ++ styles/less/main.less | 16 ++++- templates/feed.html | 1 + templates/map.html | 33 +++++++++ templates/monthly.html | 1 + templates/page-top.html | 2 +- templates/photo.html | 54 ++++++++++++++ 12 files changed, 226 insertions(+), 95 deletions(-) create mode 100644 styles/dist/unknown.svg create mode 100644 templates/map.html create mode 100644 templates/photo.html diff --git a/package-lock.json b/package-lock.json index b98623b..afb9023 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "optional": true, "requires": { "co": "^4.6.0", "fast-deep-equal": "^1.0.0", @@ -55,14 +54,12 @@ "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "optional": true + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "optional": true, "requires": { "safer-buffer": "~2.1.0" } @@ -80,20 +77,17 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "optional": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "optional": true + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "optional": true + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "balanced-match": { "version": "1.0.0", @@ -104,7 +98,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "optional": true, "requires": { "tweetnacl": "^0.14.3" } @@ -156,8 +149,7 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "optional": true + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { "version": "2.4.1", @@ -192,8 +184,7 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "optional": true + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, "coffeescript": { "version": "1.10.0", @@ -239,8 +230,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "optional": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "currently-unhandled": { "version": "0.4.1", @@ -254,7 +244,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "optional": true, "requires": { "assert-plus": "^1.0.0" } @@ -295,7 +284,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "optional": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -305,7 +293,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "optional": true, "requires": { "prr": "~1.0.1" } @@ -350,8 +337,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "optional": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extsprintf": { "version": "1.3.0", @@ -361,14 +347,12 @@ "fast-deep-equal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "optional": true + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "optional": true + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "faye-websocket": { "version": "0.10.0", @@ -421,14 +405,12 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "optional": true + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "optional": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "1.0.6", @@ -462,7 +444,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "optional": true, "requires": { "assert-plus": "^1.0.0" } @@ -704,14 +685,12 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "optional": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", - "optional": true, "requires": { "ajv": "^5.3.0", "har-schema": "^2.0.0" @@ -749,7 +728,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "optional": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -764,11 +742,15 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" + }, "image-size": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "optional": true + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=" }, "indent-string": { "version": "2.1.0", @@ -816,8 +798,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "optional": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-utf8": { "version": "0.2.1", @@ -832,8 +813,7 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "optional": true + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "js-yaml": { "version": "3.5.5", @@ -847,32 +827,27 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "optional": true + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "optional": true + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "optional": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "optional": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -995,8 +970,7 @@ "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "optional": true + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { "version": "1.36.0", @@ -1071,14 +1045,23 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "optional": true + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "ol": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ol/-/ol-5.2.0.tgz", + "integrity": "sha512-/sXlXKMT7W/4txYmEJiqZousQ5GScDoqVrltyQDl0himUD1oEV460bH74eD49AMm0OmQL9+TIn6DXKqOYzjTLw==", + "requires": { + "pbf": "3.1.0", + "pixelworks": "1.1.0", + "rbush": "2.0.2" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1118,11 +1101,19 @@ "pinkie-promise": "^2.0.0" } }, + "pbf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.1.0.tgz", + "integrity": "sha512-/hYJmIsTmh7fMkHAWWXJ5b8IKLWdjdlAFb3IHkRBn1XUhIYBChVGfVwmHEAV3UfXTxsP/AKfYTXTS/dCPxJd5w==", + "requires": { + "ieee754": "^1.1.6", + "resolve-protobuf-schema": "^2.0.0" + } + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "optional": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pify": { "version": "2.3.0", @@ -1142,6 +1133,11 @@ "pinkie": "^2.0.0" } }, + "pixelworks": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pixelworks/-/pixelworks-1.1.0.tgz", + "integrity": "sha1-Hwla1I3Ki/ihyCWOAJIDGkTyLKU=" + }, "pretty-bytes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-3.0.1.tgz", @@ -1154,28 +1150,29 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "optional": true, "requires": { "asap": "~2.0.3" } }, + "protocol-buffers-schema": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.3.2.tgz", + "integrity": "sha512-Xdayp8sB/mU+sUV4G7ws8xtYMGdQnxbeIfLjyO9TZZRJdztBGhlmbI5x1qcY4TG5hBkIKGnc28i7nXxaugu88w==" + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "optional": true + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" }, "psl": { "version": "1.1.29", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", - "optional": true + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "optional": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "purecss": { "version": "1.0.0", @@ -1187,6 +1184,11 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, + "quickselect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz", + "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==" + }, "raw-body": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", @@ -1196,6 +1198,14 @@ "string_decoder": "0.10" } }, + "rbush": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-2.0.2.tgz", + "integrity": "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA==", + "requires": { + "quickselect": "^1.0.1" + } + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -1236,7 +1246,6 @@ "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "optional": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -1265,6 +1274,14 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" }, + "resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "requires": { + "protocol-buffers-schema": "^3.3.1" + } + }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -1340,7 +1357,6 @@ "version": "1.14.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", - "optional": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -1412,7 +1428,6 @@ "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "optional": true, "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -1427,7 +1442,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "optional": true, "requires": { "safe-buffer": "^5.0.1" } @@ -1435,8 +1449,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "underscore.string": { "version": "3.3.4", @@ -1455,8 +1468,7 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "optional": true + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" }, "validate-npm-package-license": { "version": "3.0.4", @@ -1471,7 +1483,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "optional": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", diff --git a/photoapp/daemon.py b/photoapp/daemon.py index 62b85a2..6205731 100644 --- a/photoapp/daemon.py +++ b/photoapp/daemon.py @@ -13,6 +13,7 @@ class PhotosWeb(object): self.tpl = Environment(loader=FileSystemLoader('templates'), autoescape=select_autoescape(['html', 'xml'])) self.tpl.globals.update(mime2ext=self.mime2ext) + self.tpl.filters['basename'] = os.path.basename self.thumb = ThumbnailView(self) self.photo = PhotoView(self) self.download = DownloadView(self) @@ -50,6 +51,14 @@ class PhotosWeb(object): yield self.tpl.get_template("monthly.html").render(images=images) + @cherrypy.expose + def map(self, i=None, zoom=3): + s = self.session() + query = s.query(PhotoSet).filter(PhotoSet.lat != 0, PhotoSet.lon != 0) + if i: + query = query.filter(PhotoSet.uuid == i) + yield self.tpl.get_template("map.html").render(images=query.all(), zoom=int(zoom)) + @cherrypy.popargs('item_type', 'thumb_size', 'uuid') class ThumbnailView(object): @@ -81,9 +90,9 @@ class ThumbnailView(object): # TODO some lock around calls to this based on uuid thumb_path = self.master.library.make_thumb(thumb_from, thumb_size) if thumb_path: - return cherrypy.lib.static.serve_file(thumb_path, 'image/jpeg') + return cherrypy.lib.static.serve_file(thumb_path, "image/jpeg") else: - return "No thumbnail available" # TODO generic svg icon + return cherrypy.lib.static.serve_file(os.path.abspath("styles/dist/unknown.svg"), "image/svg+xml") @cherrypy.popargs('item_type', 'uuid') @@ -164,17 +173,17 @@ def main(): "tools.staticdir.dir": os.path.abspath("./styles/dist")}}) cherrypy.config.update({ - 'sessionFilter.on': True, - 'tools.sessions.on': True, - 'tools.sessions.locking': 'explicit', - 'tools.sessions.timeout': 525600, - 'tools.gzip.on': True, + # 'sessionFilter.on': True, + # 'tools.sessions.on': True, + # 'tools.sessions.locking': 'explicit', + # 'tools.sessions.timeout': 525600, + # 'tools.gzip.on': True, 'request.show_tracebacks': True, 'server.socket_port': args.port, 'server.thread_pool': 25, 'server.socket_host': '0.0.0.0', 'server.show_tracebacks': True, - 'server.socket_timeout': 5, + # 'server.socket_timeout': 5, 'log.screen': False, 'engine.autoreload.on': args.debug }) diff --git a/photoapp/image.py b/photoapp/image.py index 4effda6..4732fc8 100644 --- a/photoapp/image.py +++ b/photoapp/image.py @@ -11,7 +11,7 @@ def get_jpg_info(fpath): """ Given the path to a jpg, return a dict describing it """ - date, gps, dimensions = get_exif_data(fpath) + date, gps, dimensions, orientation = get_exif_data(fpath) if date is None: import pdb @@ -24,7 +24,8 @@ def get_jpg_info(fpath): mime = magic.from_file(fpath, mime=True) size = os.path.getsize(fpath) - photo = Photo(hash=get_hash(fpath), path=fpath, format=mime, size=size, width=dimensions[0], height=dimensions[1]) + photo = Photo(hash=get_hash(fpath), path=fpath, format=mime, size=size, + width=dimensions[0], height=dimensions[1], orientation=orientation) return PhotoSet(date=date, lat=lat, lon=lon, files=[photo]) @@ -45,13 +46,14 @@ def get_hash(path): def get_exif_data(path): """ - Return a (datetime, (decimal, decimal)) tuple describing the photo's exif date and gps coordinates + Return a (datetime, (decimal, decimal), (width, height), rotation) tuple describing the photo's exif date and gps coordinates """ img = Image.open(path) datestr = None gpsinfo = None dateinfo = None + orientationinfo = 0 sizeinfo = (img.width, img.height) if img.format in ["JPEG", "PNG", "GIF"]: @@ -74,6 +76,10 @@ def get_exif_data(path): raise Exception("{} has no DateTime".format(path)) # TODO how often do we hit this dateinfo = datetime.strptime(datestr, "%Y:%m:%d %H:%M:%S") + orien = exif.get("Orientation") + if orien: + orientationinfo = {0: 0, 8: 1, 3: 2, 6: 3}.get(int(orien), 0) + gps = exif.get("GPSInfo") if gps: # see https://gis.stackexchange.com/a/273402 @@ -84,16 +90,11 @@ def get_exif_data(path): if gps[3] == 'W': gps_x *= -1 gpsinfo = (gps_y, gps_x) - import pdb - pdb.set_trace() - pass - pass - pass if dateinfo is None: dateinfo = get_mtime(path) - return dateinfo, gpsinfo, sizeinfo + return dateinfo, gpsinfo, sizeinfo, orientationinfo def rational64u_to_hms(values): diff --git a/photoapp/library.py b/photoapp/library.py index a8f3eb7..82f9a82 100644 --- a/photoapp/library.py +++ b/photoapp/library.py @@ -70,16 +70,20 @@ class PhotoLibrary(object): Create a thumbnail of the given photo, scaled/cropped to the given named style :return: local path to thumbnail file or None if creation failed or was blocked """ - styles = {"feed": (250, 250), + styles = {"tiny": (80, 80), + "small": (100, 100), + "feed": (250, 250), "preview": (1024, 768), "big": (2048, 1536)} dest = os.path.join(self.cache_path, "thumbs", style, "{}.jpg".format(photo.uuid)) if os.path.exists(dest): return os.path.abspath(dest) + if photo.width is None: # todo better detection of images that PIL can't open + return None if photo.uuid not in self._failed_thumbs_cache[style]: width = min(styles[style][0], photo.width if photo.width > 0 else 999999999) height = min(styles[style][1], photo.height if photo.height > 0 else 999999999) # TODO this is bad. - p = Process(target=self.gen_thumb, args=(os.path.join(self.path, photo.path), dest, width, height)) + p = Process(target=self.gen_thumb, args=(os.path.join(self.path, photo.path), dest, width, height, photo.orientation)) p.start() p.join() if p.exitcode != 0: @@ -89,13 +93,13 @@ class PhotoLibrary(object): return None @staticmethod - def gen_thumb(src_img, dest_img, width, height): + def gen_thumb(src_img, dest_img, width, height, rotation): try: start = time() # TODO lock around the dir creation os.makedirs(os.path.split(dest_img)[0], exist_ok=True) - image = Image.open(src_img) + image = image.rotate(90 * rotation, expand=True) thumb = ImageOps.fit(image, (width, height), Image.ANTIALIAS) thumb.save(dest_img, 'JPEG') print("Generated {} in {}s".format(dest_img, round(time() - start, 4))) diff --git a/photoapp/types.py b/photoapp/types.py index 60c5a01..3f56c9f 100644 --- a/photoapp/types.py +++ b/photoapp/types.py @@ -32,6 +32,7 @@ class Photo(Base): size = Column(Integer) width = Column(Integer) height = Column(Integer) + orientation = Column(Integer, default=0) hash = Column(String(length=64), unique=True) path = Column(Unicode) format = Column(String(length=64)) # TODO how long can a mime string be diff --git a/styles/dist/unknown.svg b/styles/dist/unknown.svg new file mode 100644 index 0000000..0222c25 --- /dev/null +++ b/styles/dist/unknown.svg @@ -0,0 +1,4 @@ + + + + diff --git a/styles/less/main.less b/styles/less/main.less index 41d8c35..8abd8f9 100644 --- a/styles/less/main.less +++ b/styles/less/main.less @@ -130,7 +130,7 @@ a { } .nav-inner { display: block; - padding: 2em 0; + padding: 1.5em 0; } #nav .nav-menu-button { display: none; @@ -183,7 +183,7 @@ a { } .photo-view { - img { + .photo-preview img { display: block; width: 100%; max-height: 100%; @@ -192,4 +192,16 @@ a { .photo-info { padding: 0px 0px 0px 25px; } + .photo-formats { + ul { + padding: 0; + } + li { + margin-bottom: 15px; + } + img { + // float: left; + max-width: 100px; + } + } } diff --git a/templates/feed.html b/templates/feed.html index 586f3f0..a255024 100644 --- a/templates/feed.html +++ b/templates/feed.html @@ -1,4 +1,5 @@ {% set title = "All photos" %} +{% set subtitle = "By date, descending" %} {% include "page-top.html" %} diff --git a/templates/map.html b/templates/map.html new file mode 100644 index 0000000..aeb2a9b --- /dev/null +++ b/templates/map.html @@ -0,0 +1,33 @@ +{% set title = "Photo map" %} +{% set subtitle = "GPS data" %} + +{% include "page-top.html" %} + +
+
+ + +
+ +{% include "page-bottom.html" %} diff --git a/templates/monthly.html b/templates/monthly.html index bb01bb2..49b8a82 100644 --- a/templates/monthly.html +++ b/templates/monthly.html @@ -1,4 +1,5 @@ {% set title = "Server statistics" %} +{% set subtitle = "Placeholder" %} {% include "page-top.html" %} diff --git a/templates/page-top.html b/templates/page-top.html index 215be87..2fe1ae1 100644 --- a/templates/page-top.html +++ b/templates/page-top.html @@ -35,7 +35,7 @@

{{ title }}

diff --git a/templates/photo.html b/templates/photo.html new file mode 100644 index 0000000..08dcae7 --- /dev/null +++ b/templates/photo.html @@ -0,0 +1,54 @@ +{% set title = "Placeholder Title" %} +{% set subtitle = image.uuid %} + +{% include "page-top.html" %} + +
+
+ + + +
+
+ +
+

Versions

+
    + {% for img in image.files %} +
  • + + + +
    +
    + {{ img.uuid }} +
    +
    + {{ img.path | basename }} +
    +
    + {{ img.size | filesizeformat }} - {{ img.format }} +
    + +
    +
  • + {% endfor %} +
+
+
+
+ + +{% include "page-bottom.html" %}