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;