diff --git a/frontend/face-explorer.html b/frontend/face-explorer.html
new file mode 100644
index 0000000..184a348
--- /dev/null
+++ b/frontend/face-explorer.html
@@ -0,0 +1,304 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/slideshow.html b/frontend/slideshow.html
index 85040fd..8a5ae98 100755
--- a/frontend/slideshow.html
+++ b/frontend/slideshow.html
@@ -137,8 +137,8 @@ function makeFaceBoxes() {
box.style.height = Math.floor((face.bottom - face.top) * height) + "%";
box.addEventListener("click", (event) => {
console.log(face);
- face.relatedPhotos.forEach((path) => {
- window.open(base + path);
+ face.relatedPhotos.forEach((photo) => {
+ window.open(base + photo.path);
});
event.preventDefault = true;
event.stopImmediatePropagation();
@@ -172,7 +172,7 @@ function loadPhoto(index) {
months[taken.getMonth()] + " " +
taken.getDate() + " " +
taken.getFullYear();
- document.getElementById("photo").style.backgroundImage = "url(" + encodeURI(url) + ")";
+ document.getElementById("photo").style.backgroundImage = "url(" + encodeURI(url).replace(/\(/g, '%28').replace(/\)/g, '%29') + ")";
countdown = 15;
tick();
window.fetch("api/v1/photos/faces/" + photo.id).then(res => res.json()).then((faces) => {
diff --git a/server/face-recognizer.js b/server/face-recognizer.js
index 0d7bd29..b5d6191 100644
--- a/server/face-recognizer.js
+++ b/server/face-recognizer.js
@@ -61,7 +61,7 @@ faceapi.nets.ssdMobilenetv1.loadFromDisk('./models')
return photoDB.sequelize.query("SELECT photos.id,photos.filename,photos.width,photos.height,albums.path " +
"FROM photos " +
"LEFT JOIN albums ON (albums.id=photos.albumId) " +
- "WHERE faces=-1 ORDER BY albums.path,photos.filename", {
+ "WHERE faces=-1 AND deleted=0 ORDER BY albums.path,photos.filename", {
type: photoDB.sequelize.QueryTypes.SELECT,
raw: true
}
diff --git a/server/routes/photos.js b/server/routes/photos.js
index e0d7695..c1807a1 100755
--- a/server/routes/photos.js
+++ b/server/routes/photos.js
@@ -781,11 +781,7 @@ router.get("/trash", function(req, res/*, next*/) {
});
});
-router.get("/faces/:id", (req, res) => {
- const id = parseInt(req.params.id);
- if (id != req.params.id) {
- return res.status(400).send({ message: "Usage faces/:id"});
- }
+function getFacesForPhoto(id) {
return photoDB.sequelize.query("SELECT * FROM faces WHERE photoId=:id", {
replacements: {
id: id
@@ -797,7 +793,7 @@ router.get("/faces/:id", (req, res) => {
return photoDB.sequelize.query(
"SELECT face1ID,face2ID " +
"FROM facedistances " +
- "WHERE distance<0.45 AND (face1ID=:id OR face2ID=:id) " +
+ "WHERE distance<0.5 AND (face1ID=:id OR face2ID=:id) " +
"ORDER BY distance ASC", {
replacements: {
id: face.id
@@ -821,15 +817,72 @@ router.get("/faces/:id", (req, res) => {
});
}).then((photos) => {
face.relatedPhotos = photos.filter((photo) => { return photo.id != id }).map((photo) => {
- return photo.path + photo.filename;
+ return { id: photo.id, path: photo.path + photo.filename };
});
});
}).then(() => {
- return res.status(200).json(faces);
+ return faces;
});
});
+}
+router.get("/faces/:id", (req, res) => {
+ const id = parseInt(req.params.id);
+ if (id != req.params.id) {
+ return res.status(400).send({ message: "Usage faces/:id"});
+ }
+ return getFacesForPhoto(id).then((faces) => {
+ return res.status(200).json(faces);
+ });
});
+router.get("/random/:id?", (req, res) => {
+ const id = parseInt(req.params.id);
+ let filter = "";
+
+ if (id == req.params.id) {
+ filter = "AND id=:id";
+ } else {
+ filter = "AND faces>0";
+ }
+ return photoDB.sequelize.query("SELECT id FROM photos WHERE deleted=0 " + filter, {
+ replacements: {
+ id: id
+ },
+ type: photoDB.Sequelize.QueryTypes.SELECT,
+ raw: true
+ }).then((results) => {
+ if (!results.length) {
+ return res.status(404);
+ }
+ const id = results[Math.floor(Math.random() * results.length)].id;
+
+ return photoDB.sequelize.query(
+ "SELECT photos.*,albums.path AS path FROM photos " +
+ "INNER JOIN albums ON albums.id=photos.albumId " +
+ "WHERE photos.duplicate=0 AND photos.deleted=0 AND photos.scanned NOT NULL AND photos.id=:id", {
+ replacements: {
+ id: id,
+ },
+ type: photoDB.Sequelize.QueryTypes.SELECT,
+ raw: true
+ });
+ }).then(function(photos) {
+ if (!photos.length) {
+ return res.status(404);
+ }
+ const photo = photos[0];
+ for (var key in photo) {
+ if (photo[key] instanceof Date) {
+ photo[key] = moment(photo[key]);
+ }
+ }
+ return getFacesForPhoto(photo.id).then((faces) => {
+ photo.faces = faces;
+ return res.status(200).json(photo);
+ })
+ });
+})
+
router.get("/mvimg/*", function(req, res/*, next*/) {
let limit = parseInt(req.query.limit) || 50,
id, cursor, index;