Improved slideshow to support ?holiday URL matching
Added docker config Signed-off-by: James Ketrenos <james_gitlab@ketrenos.com>
This commit is contained in:
parent
55a9c16e7b
commit
c2f72df80a
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!entrypoint.sh
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,3 +3,5 @@ node_modules
|
|||||||
./elements
|
./elements
|
||||||
frontend/bower_components
|
frontend/bower_components
|
||||||
pictures
|
pictures
|
||||||
|
db
|
||||||
|
*.db
|
||||||
|
52
Dockerfile
Normal file
52
Dockerfile
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
FROM ubuntu:disco
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
|
||||||
|
RUN apt-get install -y wget
|
||||||
|
|
||||||
|
# Upgrade Node
|
||||||
|
RUN wget -qO- https://deb.nodesource.com/setup_10.x | bash -
|
||||||
|
RUN DEBIAN_FRONTEND=NONINTERACTIVE apt-get install -y \
|
||||||
|
nodejs \
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
make \
|
||||||
|
cmake
|
||||||
|
|
||||||
|
# You can then install the latest npm, polymer-cli, and bower:
|
||||||
|
RUN npm install --global npm@latest npx
|
||||||
|
|
||||||
|
# Speed up face-recognition and dev tools
|
||||||
|
RUN apt-get install -y libopenblas-dev cmake
|
||||||
|
|
||||||
|
# NEF processing uses ufraw-batch
|
||||||
|
RUN apt-get install -y ufraw-batch
|
||||||
|
|
||||||
|
# Create a user with sudo access
|
||||||
|
RUN DEBIAN_FRONTEND=noninteractive \
|
||||||
|
&& apt-get install --no-install-recommends -y \
|
||||||
|
sudo
|
||||||
|
|
||||||
|
# NOTE: Requires 'sudo' package to already be installed
|
||||||
|
RUN groupadd -g 1000 user \
|
||||||
|
&& useradd --no-log-init \
|
||||||
|
-s /bin/bash \
|
||||||
|
-u 1000 \
|
||||||
|
-m \
|
||||||
|
-g user \
|
||||||
|
-G sudo \
|
||||||
|
-p $(echo "user" | openssl passwd -stdin) user
|
||||||
|
|
||||||
|
# Set 'sudo' to NOPASSWD for all container users
|
||||||
|
RUN sed -i -e 's,%sudo.*,%sudo ALL=(ALL) NOPASSWD:ALL,g' /etc/sudoers
|
||||||
|
|
||||||
|
COPY /entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
|
RUN DEBIAN_FRONTEND=noninteractive \
|
||||||
|
&& apt-get install --no-install-recommends -y \
|
||||||
|
git
|
||||||
|
|
||||||
|
USER user
|
||||||
|
WORKDIR /website
|
||||||
|
|
||||||
|
CMD [ "/entrypoint.sh" ]
|
47
README.md
47
README.md
@ -28,47 +28,6 @@ sudo apt install -y ufraw-batch
|
|||||||
|
|
||||||
### Create `photos` user for DB
|
### Create `photos` user for DB
|
||||||
|
|
||||||
You will need to know the root password for your mariadb installation. If you
|
Photos is currently using sqlite, which means you don't need to
|
||||||
need to reset the root password, you can perform the following:
|
do anything fancy beyond having run 'npm install' to get sequelize
|
||||||
|
and sqlite3 installed.
|
||||||
```bash
|
|
||||||
export PASSWORD=m4g1cP4ssw0rd
|
|
||||||
sudo service mysql stop
|
|
||||||
sudo mysqld_safe --skip-grant-tables &
|
|
||||||
sleep 1 # mysqld_safe can take a bit of time to come online
|
|
||||||
sudo mysql << EOF
|
|
||||||
use mysql;
|
|
||||||
UPDATE user SET PASSWORD=PASSWORD("${PASSWORD}") WHERE USER='root';
|
|
||||||
UPDATE user SET plugin='mysql_native_password' WHERE USER='root';
|
|
||||||
FLUSH PRIVILEGES;
|
|
||||||
QUIT
|
|
||||||
EOF
|
|
||||||
sudo service mysql stop
|
|
||||||
sudo service mysql start
|
|
||||||
```
|
|
||||||
|
|
||||||
Once you know the root password, you can then create a new user and DB
|
|
||||||
for the photo app via:
|
|
||||||
|
|
||||||
### Create the USER
|
|
||||||
|
|
||||||
```bash
|
|
||||||
U='photos'
|
|
||||||
P='p4$$w0rd'
|
|
||||||
mysql -u root --password=${PASSWORD} << EOF
|
|
||||||
CREATE USER '${U}'@'localhost' IDENTIFIED BY '${P}';
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
### Create the DB 'photos'
|
|
||||||
|
|
||||||
```bash
|
|
||||||
U='photos'
|
|
||||||
P='p4$$w0rd'
|
|
||||||
D='photos'
|
|
||||||
mysql -u root --password=${PASSWORD} << EOF
|
|
||||||
DROP DATABASE IF EXISTS ${D};
|
|
||||||
CREATE DATABASE ${D} CHARACTER SET utf8 COLLATE utf8_general_ci;
|
|
||||||
GRANT ALL PRIVILEGES ON ${D}.* TO '${U}'@'localhost' IDENTIFIED BY '${P}';
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"db": {
|
"db": {
|
||||||
"photos": {
|
"photos": {
|
||||||
"host": "sqlite:photos.db",
|
"host": "sqlite:db/photos.db",
|
||||||
"options": {
|
"options": {
|
||||||
"logging" : false,
|
"logging" : false,
|
||||||
"timezone": "+00:00"
|
"timezone": "+00:00"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"host": "sqlite:users.db",
|
"host": "sqlite:db/users.db",
|
||||||
"options": {
|
"options": {
|
||||||
"logging" : false,
|
"logging" : false,
|
||||||
"timezone": "+00:00"
|
"timezone": "+00:00"
|
||||||
|
30
docker-compose.yml
Normal file
30
docker-compose.yml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
version: '3.1'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
# db:
|
||||||
|
# image: mariadb
|
||||||
|
# restart: always
|
||||||
|
# environment:
|
||||||
|
# MYSQL_ROOT_PASSWORD: photos
|
||||||
|
# PHOTOS_DB_USER: photos
|
||||||
|
# PHOTOS_DB_PASSWD: ph0t0z
|
||||||
|
# PHOTOS_DB: photos
|
||||||
|
# volumes:
|
||||||
|
# - ${PWD}/db:/var/lib/mysql
|
||||||
|
# - ./init.sql:/data/application/init.sql
|
||||||
|
|
||||||
|
photos:
|
||||||
|
user: ${USER_ID}
|
||||||
|
build: .
|
||||||
|
image: photos:latest
|
||||||
|
container_name: photos
|
||||||
|
# depends_on:
|
||||||
|
# - db
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8123:8123
|
||||||
|
volumes:
|
||||||
|
- ${PWD}/photos:/photos
|
||||||
|
- ${PWD}/db:/db
|
||||||
|
- ${PWD}:/website
|
5
entrypoint.sh
Executable file
5
entrypoint.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
while true; do
|
||||||
|
npm start
|
||||||
|
sleep 3
|
||||||
|
done
|
@ -1,6 +1,5 @@
|
|||||||
<html>
|
<html>
|
||||||
<script src="src/ketr-photos/fetch.js"></script>
|
<base href="/photos/">
|
||||||
<base href="/">
|
|
||||||
<body>
|
<body>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
@ -15,6 +14,7 @@ body {
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
z-index: 0;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: 50% 50%;
|
background-position: 50% 50%;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
@ -27,25 +27,40 @@ body {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#info {
|
#footer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
padding: 0.5em 1em;
|
opacity: 0.6;
|
||||||
background-color: #444;
|
background: linear-gradient(45deg, rgba(16, 16, 16, 1), transparent);
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
font-family: Arial;
|
font-family: Arial;
|
||||||
box-shadow: 0px -0.25em 1em black;
|
box-shadow: 0px -0.25em 1em black;
|
||||||
|
text-shadow: 0 1px 0 black;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#info {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<img id="placeholder"></img>
|
<img id="placeholder"></img>
|
||||||
<div id="photo">
|
<div id="photo">
|
||||||
</div>
|
</div>
|
||||||
<div id="info">
|
<div id="footer">
|
||||||
Loading photoset...
|
<div id="info">Loading photoset...</div>
|
||||||
|
<div id="loading"></div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
var base, placeholder, info, photos = [], photoIndex;
|
var base, placeholder, info, photos = [], photoIndex;
|
||||||
@ -66,9 +81,48 @@ function shuffle(array) {
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var countdown = 15;
|
||||||
|
|
||||||
|
const days = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
|
||||||
|
months = [ "January", "February", "March", "April", "May", "June",
|
||||||
|
"July", "August", "September", "October", "November", "December" ];
|
||||||
|
|
||||||
function loadPhoto(index) {
|
function loadPhoto(index) {
|
||||||
var photo = photos[index];
|
const photo = photos[index],
|
||||||
placeholder.src = base + photo.path /* + "thumbs/scaled/"*/ + photo.filename;
|
xml = new XMLHttpRequest(),
|
||||||
|
url = base + photo.path /* + "thumbs/scaled/"*/ + photo.filename,
|
||||||
|
taken = new Date(photo.taken);
|
||||||
|
|
||||||
|
document.getElementById("loading").textContent = "0%";
|
||||||
|
|
||||||
|
xml.onprogress = function (event) {
|
||||||
|
var alpha = 0;
|
||||||
|
if (event.total) {
|
||||||
|
alpha = event.loaded / event.total;
|
||||||
|
document.getElementById("loading").textContent = Math.ceil(100 * alpha) + "%";
|
||||||
|
} else {
|
||||||
|
document.getElementById("loading").textContent = "0%";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xml.onload = function(event) {
|
||||||
|
info.textContent =
|
||||||
|
days[taken.getDay()] + ", " +
|
||||||
|
months[taken.getMonth()] + " " +
|
||||||
|
taken.getDate() + " " +
|
||||||
|
taken.getFullYear();
|
||||||
|
document.getElementById("photo").style.backgroundImage = "url(" + url + ")";
|
||||||
|
countdown = 15;
|
||||||
|
tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
xml.onerror = function(event) {
|
||||||
|
info.textContent = "Error loading photo. Trying next photo.";
|
||||||
|
nextPhoto();
|
||||||
|
}
|
||||||
|
|
||||||
|
xml.open("GET", url, true);
|
||||||
|
xml.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextPhoto() {
|
function nextPhoto() {
|
||||||
@ -77,16 +131,33 @@ function nextPhoto() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var scheduled = false;
|
var scheduled = false;
|
||||||
|
|
||||||
|
function tick() {
|
||||||
|
if (scheduled) {
|
||||||
|
clearTimeout(scheduled);
|
||||||
|
}
|
||||||
|
document.getElementById("loading").textContent = countdown + "s";
|
||||||
|
if (countdown > 0) {
|
||||||
|
/* If there is a timer running, then decrement the counter */
|
||||||
|
if (scheduled) {
|
||||||
|
countdown--;
|
||||||
|
}
|
||||||
|
scheduled = setTimeout(tick, 1000);
|
||||||
|
} else {
|
||||||
|
scheduled = null;
|
||||||
|
countdown = 15;
|
||||||
|
nextPhoto();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function schedule() {
|
function schedule() {
|
||||||
if (scheduled) {
|
if (scheduled) {
|
||||||
clearTimeout(scheduled);
|
clearTimeout(scheduled);
|
||||||
}
|
}
|
||||||
scheduled = setTimeout(function () {
|
tick();
|
||||||
scheduled = undefined;
|
|
||||||
nextPhoto();
|
|
||||||
}, 15 * 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var paused = false;
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
var tmp = document.querySelector("base");
|
var tmp = document.querySelector("base");
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
@ -96,6 +167,18 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("keydown", function(event) {
|
document.addEventListener("keydown", function(event) {
|
||||||
|
if (event.keyCode == 32) { /* space */
|
||||||
|
paused = !paused;
|
||||||
|
if (!paused) {
|
||||||
|
tick();
|
||||||
|
} else {
|
||||||
|
document.getElementById("loading").textContent = "||";
|
||||||
|
clearTimeout(scheduled);
|
||||||
|
scheduled = null;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (event.keyCode == 37) { /* left */
|
if (event.keyCode == 37) { /* left */
|
||||||
if (photoIndex == 0) {
|
if (photoIndex == 0) {
|
||||||
photoIndex = photos.length;
|
photoIndex = photos.length;
|
||||||
@ -115,52 +198,27 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
info = document.getElementById("info");
|
info = document.getElementById("info");
|
||||||
placeholder = document.getElementById("placeholder");
|
|
||||||
|
|
||||||
placeholder.onload = function() {
|
/* Trim off everything up to and including the ? (if its there) */
|
||||||
var item = photos[photoIndex],
|
var parts = window.location.href.match(/^.*\?(.*)$/);
|
||||||
days = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
|
if (parts) {
|
||||||
months = [ "January", "February", "March", "April", "May", "June",
|
holiday = parts[1];
|
||||||
"July", "August", "September", "October", "November", "December" ],
|
} else {
|
||||||
taken = new Date(item.taken);
|
holiday = "memorial day";
|
||||||
|
|
||||||
info.textContent = "Photo taken on " +
|
|
||||||
days[taken.getDay()] + ", " +
|
|
||||||
months[taken.getMonth()] + " " +
|
|
||||||
taken.getDate() + " " +
|
|
||||||
taken.getFullYear();
|
|
||||||
|
|
||||||
photo.style.backgroundImage = "url(" + placeholder.src + ")";
|
|
||||||
|
|
||||||
schedule();
|
|
||||||
};
|
|
||||||
|
|
||||||
placeholder.onerror = function() {
|
|
||||||
info.textContent = "Error loading photo. Trying next photo.";
|
|
||||||
nextPhoto();
|
|
||||||
}
|
}
|
||||||
|
window.fetch("api/v1/photos/holiday/" + holiday.replace(/ +/g, "%20"))
|
||||||
window.fetch("api/v1/photos/holiday/memorial%20day", function(error, xhr) {
|
.then(res => res.json()).then(function(data) {
|
||||||
if (error && xhr.status != 404) {
|
if (data.items.length) {
|
||||||
console.log("Unable to fetch holiday: " + error);
|
info.textContent = photos.length + " photos found. Shuffling.";
|
||||||
return;
|
photos = shuffle(data.items);
|
||||||
}
|
|
||||||
|
|
||||||
var results;
|
|
||||||
try {
|
|
||||||
results = JSON.parse(xhr.responseText);
|
|
||||||
} catch (___) {
|
|
||||||
info.textContent = "Unable to parse results"
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results.items.length) {
|
|
||||||
photos = shuffle(results.items);
|
|
||||||
photoIndex = -1;
|
photoIndex = -1;
|
||||||
nextPhoto();
|
nextPhoto();
|
||||||
} else {
|
} else {
|
||||||
info.textContent = "No photos found.";
|
info.textContent = "No photos found for " + holiday + ".";
|
||||||
}
|
}
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.error(error);
|
||||||
|
info.textContent = "Unable to fetch holiday :(";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -12,6 +12,9 @@
|
|||||||
},
|
},
|
||||||
"author": "James Ketrenos",
|
"author": "James Ketrenos",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"unused": {
|
||||||
|
"face-recognition": "^0.9.4"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bluebird": "^3.5.3",
|
"bluebird": "^3.5.3",
|
||||||
"body-parser": "^1.18.3",
|
"body-parser": "^1.18.3",
|
||||||
@ -21,7 +24,6 @@
|
|||||||
"exif-reader": "github:paras20xx/exif-reader",
|
"exif-reader": "github:paras20xx/exif-reader",
|
||||||
"express": "^4.16.4",
|
"express": "^4.16.4",
|
||||||
"express-session": "^1.15.6",
|
"express-session": "^1.15.6",
|
||||||
"face-recognition": "^0.9.4",
|
|
||||||
"handlebars": "^4.0.12",
|
"handlebars": "^4.0.12",
|
||||||
"ldapauth-fork": "^4.0.2",
|
"ldapauth-fork": "^4.0.2",
|
||||||
"ldapjs": "^1.0.2",
|
"ldapjs": "^1.0.2",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user