Rotation now works

Signed-off-by: James Ketrenos <james_git@ketrenos.com>
This commit is contained in:
James Ketr 2018-10-11 18:05:45 -07:00
parent 82e5875e52
commit 2f6f0ccfa5
4 changed files with 192 additions and 60 deletions

View File

@ -81,6 +81,10 @@
Polymer({ Polymer({
is: "photo-thumbnail", is: "photo-thumbnail",
properties: { properties: {
"unique": {
type: String,
value: ""
},
"disabled": { "disabled": {
reflectToAttribute: true reflectToAttribute: true
}, },
@ -89,7 +93,7 @@
}, },
"thumbpath": { "thumbpath": {
type: String, type: String,
computed: "safeItemThumbFilepath(item, base)" computed: "safeItemThumbFilepath(item, base, unique)"
}, },
"width": { "width": {
type: Number type: Number
@ -130,11 +134,11 @@
this.style.height = width + "px"; this.style.height = width + "px";
}, },
safeItemThumbFilepath: function(item, base) { safeItemThumbFilepath: function(item, base, unique) {
if (item === undefined|| base === undefined || item.path === undefined) { if (item === undefined|| base === undefined || item.path === undefined) {
return ""; return "";
} }
return base + item.path + "thumbs/" + item.filename; return base + item.path + "thumbs/" + item.filename + (this.unique ? ("?" + this.unique) : "");
}, },
date: function(item) { date: function(item) {
@ -154,6 +158,10 @@
event.preventDefault(); event.preventDefault();
}, },
reload: function() {
this.unique = parseInt(this.unique || 0) + 1;
},
attached: function() { attached: function() {
var base = document.querySelector("base"); var base = document.querySelector("base");
if (base) { if (base) {

View File

@ -10,6 +10,7 @@
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html"> <link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../bower_components/iron-icons/iron-icons.html"> <link rel="import" href="../../bower_components/iron-icons/iron-icons.html">
<link rel="import" href="../../bower_components/iron-icons/image-icons.html">
<link rel="import" href="../../bower_components/iron-iconset/iron-iconset.html"> <link rel="import" href="../../bower_components/iron-iconset/iron-iconset.html">
<link rel="import" href="../../bower_components/iron-pages/iron-pages.html"> <link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
<link rel="import" href="../../bower_components/iron-resizable-behavior/iron-resizable-behavior.html"> <link rel="import" href="../../bower_components/iron-resizable-behavior/iron-resizable-behavior.html">
@ -1221,9 +1222,11 @@
let actions = [ "delete" ]; let actions = [ "delete" ];
if (this.mode == "duplicates") { if (this.mode == "duplicates") {
actions.unshift("text-format"); actions.unshift("text-format");
} } else if (this.mode == "trash") {
if (this.mode == "trash") {
actions.unshift("undo"); actions.unshift("undo");
} else {
actions.unshift("image:rotate-right");
actions.unshift("image:rotate-left");
} }
thumbnail.actions = actions; thumbnail.actions = actions;
thumbnail.addEventListener("action", this._imageAction.bind(this)); thumbnail.addEventListener("action", this._imageAction.bind(this));
@ -1293,49 +1296,65 @@
}.bind(this, thumbnail), 250); }.bind(this, thumbnail), 250);
}, },
_imageAction: function(event) { undoAction: function(thumbnail) {
switch (event.detail) { var params = {};
case "undo": /* Undelete an image */
var thumbnail = event.currentTarget, params = {};
thumbnail.disabled = true; thumbnail.disabled = true;
window.fetch("api/v1/photos/" + thumbnail.item.id + "?a=undelete",
params.undelete = 1;
var query = "";
for (var key in params) {
if (!query) {
query = "?";
} else {
query += "&";
}
query += encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
}
window.fetch("api/v1/photos/" + thumbnail.item.id + query,
this._removeImageAfterFetch.bind(this, thumbnail), {}, "PUT"); this._removeImageAfterFetch.bind(this, thumbnail), {}, "PUT");
break; },
case "delete": /* Delete image */ deleteAction: function(photo) {
var thumbnail = event.currentTarget, params = {}; var thumbnail = event.currentTarget, params = {};
thumbnail.disabled = true; thumbnail.disabled = true;
var query = "";
if (this.mode == "trash") { if (this.mode == "trash") {
console.log("TODO: Prompt user 'Are you sure?' ?"); console.log("TODO: Prompt user 'Are you sure?' ?");
params.permanent = 1; query += "?permanent=1";
}
var query = "";
for (var key in params) {
if (!query) {
query = "?";
} else {
query += "&";
}
query += encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
} }
window.fetch("api/v1/photos/" + thumbnail.item.id + query, window.fetch("api/v1/photos/" + thumbnail.item.id + query,
this._removeImageAfterFetch.bind(this, thumbnail), {}, "DELETE"); this._removeImageAfterFetch.bind(this, thumbnail), {}, "DELETE");
},
renameAction: function(thumbnail) {
return;
},
rotateAction: function(thumbnail, direction) {
thumbnail.disabled = true;
window.fetch("api/v1/photos/" + thumbnail.item.id + "?a=rotate&direction=" + direction,
function(thumbnail, error, xhr) {
thumbnail.disabled = false;
if (error) {
console.log("Unable to take action on photo: " + error);
return;
}
thumbnail.reload();
}.bind(this, thumbnail), {}, "PUT");
},
_imageAction: function(event) {
switch (event.detail) {
case "undo": /* Undelete an image */
this.undoAction(event.currentTarget);
break;
case "delete": /* Delete image */
this.deleteAction(event.currentTarget);
break;
case "image:rotate-left":
this.rotateAction(event.currentTarget, "left");
break;
case "image:rotate-right":
this.rotateAction(event.currentTarget, "right");
break; break;
case "text-format": /* Rename this image */ case "text-format": /* Rename this image */
this.renameAction(event.currentTarget);
break; break;
} }
}, },

View File

@ -32,38 +32,143 @@ const unlink = function (_path) {
}); });
} }
const rename = function (_src, _dst) {
if (_src.indexOf(picturesPath.replace(/\/$/, "")) == 0) {
_src = _src.substring(picturesPath.length);
}
if (_dst.indexOf(picturesPath.replace(/\/$/, "")) == 0) {
_dst = _dst.substring(picturesPath.length);
}
let src = picturesPath + _src,
dst = picturesPath + _dst;
return new Promise(function (resolve, reject) {
fs.rename(src, dst, function (error, stats) {
if (error) {
return reject(error);
}
return resolve(stats);
});
});
}
const stat = function (_path) {
if (_path.indexOf(picturesPath.replace(/\/$/, "")) == 0) {
_path = _path.substring(picturesPath.length);
}
let path = picturesPath + _path;
return new Promise(function (resolve, reject) {
fs.stat(path, function (error, stats) {
if (error) {
return reject(error);
}
return resolve(stats);
});
});
}
const sharp = require("sharp"), exif = require("exif-reader");
router.put("/:id", function(req, res/*, next*/) { router.put("/:id", function(req, res/*, next*/) {
if (!req.user.maintainer) { if (!req.user.maintainer) {
return res.status(401).send("Unauthorized to delete photos."); return res.status(401).send("Unauthorized to modify photos.");
} }
const replacements = { const replacements = {
id: req.params.id id: req.params.id
}; };
let query = "";
console.log("PUT /" + replacements.id, req.query); console.log("PUT /" + replacements.id, req.query);
for (let key in req.query) { switch (req.query.a) {
switch (key) {
case "undelete": case "undelete":
console.log("Undeleting " + req.params.id); console.log("Undeleting " + req.params.id);
query = "UPDATE photos SET deleted=0 WHERE id=:id"; return photoDB.sequelize.query("UPDATE photos SET deleted=0 WHERE id=:id", {
break;
default:
continue;
}
}
if (!query) {
return res.status(400).send("Invalid request");
}
return photoDB.sequelize.query(query, {
replacements: replacements replacements: replacements
}).then(function() { }).then(function() {
return res.status(200).send(req.params.id + " updated."); return res.status(200).send(req.params.id + " updated.");
}); });
case "rotate":
let direction = req.query.direction || "right";
if (direction == "right") {
direction = 90;
} else {
direction = -90;
}
return getPhoto(req.params.id).then(function(asset) {
if (!asset) {
return res.status(404).send(req.params.id + " not found.");
}
let original = picturesPath + asset.path + asset.filename,
target = picturesPath + asset.path + ".tmp." + asset.filename,
thumb = picturesPath + asset.path + "thumbs/" + asset.filename,
scaled = picturesPath + asset.path + "thumbs/scaled/" + asset.filename;
let tmp = asset.width;
asset.width = asset.height;
asset.height = tmp;
asset.image = sharp(original);
return asset.image.rotate(direction).withMetadata().toFile(target).then(function() {
/*...*/
}).then(function() {
return asset.image.rotate(direction).resize(256, 256).withMetadata().toFile(thumb);
}).then(function() {
return asset.image.resize(Math.min(1024, asset.width)).withMetadata().toFile(scaled);
}).then(function() {
return stat(target).then(function(stats) {
if (!stats) {
throw "Unable to find original file after attempting to rotate!";
}
asset.size = stats.size;
return photoDB.sequelize.query("UPDATE photos SET " +
"modified=CURRENT_TIMESTAMP,width=:width,height=:height,size=:size,scanned=CURRENT_TIMESTAMP " +
"WHERE id=:id", {
replacements: asset
}).then(function() {
return unlink(original).then(function() {
return rename(target, original);
}); });
});
});
}).then(function() {
sharp.cache(false);
sharp.cache(true);
return res.status(200).send(asset);
});
}).catch(function(error) {
console.log(error);
return res.status(500).send(error);
});
}
return res.status(400).send("Invalid request");
});
const getPhoto = function(id) {
return photoDB.sequelize.query("SELECT " +
"photos.*,albums.path AS path,photohashes.hash,(albums.path || photos.filename) AS filepath FROM photos " +
"LEFT JOIN albums ON albums.id=photos.albumId " +
"LEFT JOIN photohashes ON photohashes.photoId=photos.id " +
"WHERE photos.id=:id", {
replacements: {
id: id
},
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
}).then(function(photos) {
if (photos.length == 0) {
return null;
}
return photos[0];
});
}
router.delete("/:id", function(req, res/*, next*/) { router.delete("/:id", function(req, res/*, next*/) {
if (!req.user.maintainer) { if (!req.user.maintainer) {

View File

@ -333,7 +333,7 @@ function processBlock(items) {
return; return;
} }
return image.resize(256, 256).toFile(dst).catch(function(error) { return image.resize(256, 256).withMetadata().toFile(dst).catch(function(error) {
setStatus("Error resizing image: " + dst + "\n" + error, "error"); setStatus("Error resizing image: " + dst + "\n" + error, "error");
throw error; throw error;
}); });
@ -344,7 +344,7 @@ function processBlock(items) {
return; return;
} }
return image.resize(Math.min(1024, metadata.width)).toFile(dst).catch(function(error) { return image.resize(Math.min(1024, metadata.width)).withMetadata().toFile(dst).catch(function(error) {
setStatus("Error resizing image: " + dst + "\n" + error, "error"); setStatus("Error resizing image: " + dst + "\n" + error, "error");
throw error; throw error;
}); });