[[item.name]] ([[item.id]])
[[item.taken]]
[[item.path]]
@@ -82,6 +97,12 @@
"thumbChanged(thumbpath, visible)"
],
+ _fireAction: function(event) {
+ event.stopPropagation();
+ event.preventDefault();
+ this.fire("action", event.model.item);
+ },
+
thumbChanged: function(thumbpath, visible) {
if (visible) {
this.style.backgroundImage = "url(" +
diff --git a/frontend/src/ketr-photos/ketr-photos.html b/frontend/src/ketr-photos/ketr-photos.html
index 6d78ac3..cab5b80 100755
--- a/frontend/src/ketr-photos/ketr-photos.html
+++ b/frontend/src/ketr-photos/ketr-photos.html
@@ -221,6 +221,7 @@
}
photo-thumbnail {
+ transition: opacity 0.25s ease-in-out;
--photo-thumbnail: {
border: 3px solid white;
};
@@ -1212,6 +1213,14 @@
var thumbnail = document.createElement("photo-thumbnail");
thumbnail.item = photo;
+ if (this.user.maintainer) {
+ let actions = [ "delete" ];
+ if (this.mode == "duplicates") {
+ actions.unshift("text-format");
+ }
+ thumbnail.actions = actions;
+ thumbnail.addEventListener("action", this._imageAction.bind(this));
+ }
thumbnail.addEventListener("load-image", this._imageTap.bind(this));
thumbnail.addEventListener("load-album", this.loadAlbum.bind(this));
@@ -1226,6 +1235,31 @@
this.processing = false;
},
+ _imageAction: function(event) {
+ console.log(event.detail);
+ switch (event.detail) {
+ case "delete": /* Delete image */
+ var thumbnail = event.currentTarget;
+ thumbnail.style.opacity = 0;
+ thumbnail.style.pointerEvents = "none";
+
+ this.async(function(thumbnail) {
+ thumbnail.parentElement.removeChild(thumbnail);
+ for (var i = 0; i < this.thumbnails.length; i++) {
+ if (this.thumbnails[i] == thumbnail) {
+ console.log("Found thumbnail");
+ this.thumbnails.splice(i, 1);
+ this.notifyPath("thumbnails.length");
+ break;
+ }
+ }
+ }.bind(this, thumbnail), 250);
+ break;
+ case "text-format": /* Rename this image */
+ break;
+ }
+ },
+
pathTapped: function(event) {
this.path = event.currentTarget.path;
Polymer.dom(this.$.thumbnails).innerHTML = "";
diff --git a/server/scanner.js b/server/scanner.js
index 095c6ae..bc34019 100755
--- a/server/scanner.js
+++ b/server/scanner.js
@@ -27,10 +27,10 @@ function removeNewerFile(path, fileA, fileB) {
return;
}
if (statsA.mtime > statsB.mtime) {
- console.log("Removing file by moving to 'corrupt':" + fileA);
+ setStatus("Removing file by moving to 'corrupt':" + fileA);
moveCorrupt(path, fileA);
} else {
- console.log("Removing file by moving to 'corrupt':" + fileB);
+ setStatus("Removing file by moving to 'corrupt':" + fileB);
moveCorrupt(path, fileB);
}
});
@@ -101,14 +101,14 @@ const exists = function(path) {
}
function convertRawToJpg(path, file) {
- console.log("Converting " + path + file);
+ setStatus("Converting " + path + file);
path = picturesPath + path;
return new Promise(function(resolve, reject) {
return exists(path + file.replace(rawExtension, ".jpg")).then(function(exist) {
if (exist) {
- console.log("Skipping already converted file: " + file);
+ setStatus("Skipping already converted file: " + file);
return;
}
@@ -133,25 +133,25 @@ function convertRawToJpg(path, file) {
ufraw.on('exit', function(stderr, code, signal) {
if (signal || code != 0) {
let error = "UFRAW for " + path + file + " returned an error: " + code + "\n" + signal + "\n" + stderr.join("\n") + "\n";
- console.error(error);
+ setStatus(error, "error");
return moveCorrupt(path, file).then(function() {
- console.warn("ufraw failed");
+ setStatus("ufraw failed", "warn");
return reject(error);
}).catch(function(error) {
- console.warn("moveCorrupt failed");
+ setStatus("moveCorrupt failed", "warn");
return reject(error);
});
}
return mkdir(path + "raw").then(function() {
fs.rename(path + file, path + "raw/" + file, function(err) {
if (err) {
- console.error("Unable to move RAW file: " + path + file);
+ setStatus("Unable to move RAW file: " + path + file, "error");
return reject(err);
}
return resolve();
});
}).catch(function(error) {
- console.warn("mkdir failed");
+ setStatus("mkdir failed", "warn");
return reject(error);
});
}.bind(this, ufraw));
@@ -165,13 +165,13 @@ function moveCorrupt(path, file) {
path = picturesPath + path;
}
- console.warn("Moving corrupt file '" + file + "' to " + path + "corrupt");
+ setStatus("Moving corrupt file '" + file + "' to " + path + "corrupt", "warn");
return mkdir(path + "corrupt").then(function() {
return new Promise(function(resolve, reject) {
fs.rename(path + file, path + "corrupt/" + file, function(err) {
if (err) {
- console.error("Unable to move corrupt file: " + path + file);
+ setStatus("Unable to move corrupt file: " + path + file, "error");
return reject(err);
}
return resolve();
@@ -202,7 +202,7 @@ function processBlock(items) {
});
let toProcess = processing.length, lastMessage = moment();
- console.log("Items to be processed: " + toProcess);
+ setStatus("Items to be processed: " + toProcess);
return Promise.mapSeries(processing, function(asset) {
return computeHash(picturesPath + asset.album.path + asset.filename).then(function(hash) {
asset.hash = hash;
@@ -222,7 +222,7 @@ function processBlock(items) {
} else if (results[0].hash != asset.hash) {
query = "UPDATE photohashes SET hash=:hash WHERE photoId=:id";
} else if (results[0].photoId != asset.id) {
- console.log("Duplicate asset: " +
+ setStatus("Duplicate asset: " +
"'" + asset.album.path + asset.filename + "' is a copy of " +
"'" + results[0].path + results[0].filename + "'");
duplicates.push(asset);
@@ -293,7 +293,7 @@ function processBlock(items) {
asset.modified = moment(metadata.exif.exif.DateTimeOriginal).format();
if (asset.taken == "Invalid date" || asset.taken.replace(/T.*/, "") == "1899-11-30") {
- console.log("Invalid EXIF date information for " + asset.album.path + asset.filename);
+ setStatus("Invalid EXIF date information for " + asset.album.path + asset.filename);
asset.taken = asset.modified = moment(created).format();
}
} else {
@@ -332,7 +332,7 @@ function processBlock(items) {
}
return image.resize(256, 256).toFile(dst).catch(function(error) {
- console.error("Error resizing image: " + dst, error);
+ setStatus("Error resizing image: " + dst + "\n" + error, "error");
throw error;
});
}).then(function() {
@@ -343,7 +343,7 @@ function processBlock(items) {
}
return image.resize(Math.min(1024, metadata.width)).toFile(dst).catch(function(error) {
- console.error("Error resizing image: " + dst, error);
+ setStatus("Error resizing image: " + dst + "\n" + error, "error");
throw error;
});
});
@@ -355,7 +355,7 @@ function processBlock(items) {
});
});
}).catch(function(error) {
- console.error("Error reading image " + src + ": ", error);
+ setStatus("Error reading image " + src + ":\n" + error, "error");
return moveCorrupt(path, file).then(function() {
/* If the original file was not a NEF/ORF, we are done... */
@@ -369,31 +369,31 @@ function processBlock(items) {
/* Move the NEF/ORF to /corrupt as well so it isn't re-checked again and again... */
// return moveCorrupt(path, asset.filename);
- console.error("Already attempted to convert NEF/ORF to JPG: " + path + file);
+ setStatus("Already attempted to convert NEF/ORF to JPG: " + path + file, "error");
return;
}
}
- console.warn("Adding " + path + file + " back onto processing queue.");
+ setStatus("Adding " + path + file + " back onto processing queue.", "error");
triedClean.push(path + file);
processBlock([ path, file, created, albumId ]);
});
});
}).catch(function() {
- console.warn("Continuing file processing.");
+ setStatus("Continuing file processing.", "warn");
});
}).then(function() {
toProcess--;
if (moment().add(-5, 'seconds') > lastMessage) {
- console.log("Items to be processed: " + toProcess);
+ setStatus("Items to be processed: " + toProcess);
lastMessage = moment();
}
});
}).catch(function(error) {
- console.log("Error processing file. Continuing.");
+ setStatus("Error processing file. Continuing.");
throw error;
}).then(function() {
- console.log("Completed processing queue. Marking " + duplicates.length + " duplicates.");
+ setStatus("Completed processing queue. Marking " + duplicates.length + " duplicates.");
let dups = [];
duplicates.forEach(function(asset) {
/* If not already marked as a duplicate, mark it. */
@@ -412,7 +412,7 @@ function processBlock(items) {
}
});
}).then(function() {
- console.log("Looking for removed assets");
+ setStatus("Looking for removed assets");
return photoDB.sequelize.query("SELECT photos.scanned,photos.id,photos.filename,albums.path FROM photos " +
"LEFT JOIN albums ON (albums.id=photos.albumId) " +
"WHERE photos.deleted=0 AND DATETIME(photos.scanned)
lastMessage) {
- console.log("Albums to be created in DB: " + toProcess);
+ setStatus("Albums to be created in DB: " + toProcess);
lastMessage = moment();
}
});
});
}).then(function() {
- console.log("Processed " + albums.length + " album DB entries in " +
+ setStatus("Processed " + albums.length + " album DB entries in " +
((Date.now() - now) / 1000) + "s");
now = Date.now();
- console.log(assets.length + " assets remaining to be verified/updated. ETA N/A");
+ setStatus(assets.length + " assets remaining to be verified/updated. ETA N/A");
let processed = 0, start = Date.now(), last = 0, updateScanned = [], newEntries = 0;
return photoDB.sequelize.transaction(function(transaction) {
@@ -721,7 +744,7 @@ function doScan() {
}
let remaining = assets.length - processed, eta = Math.ceil((elapsed / 1000) * remaining / (processed - last));
- console.log(remaining + " assets remaining be verified/updated " +
+ setStatus(remaining + " assets remaining be verified/updated " +
"(" + newEntries + " new entries, " + (processed - newEntries) + " up-to-date so far). ETA " + eta + "s");
last = processed;
start = Date.now();
@@ -736,23 +759,23 @@ function doScan() {
ids: updateScanned
}
}).then(function() {
- console.log("Updated scan date of " + updateScanned.length + " assets");
+ setStatus("Updated scan date of " + updateScanned.length + " assets");
updateScanned = [];
});
}
}).then(function() {
- console.log(newEntries + " assets are new. " + (needsProcessing.length - newEntries) + " assets have been modified.");
- console.log(needsProcessing.length + " assets need HASH computed. " + (assets.length - needsProcessing.length) + " need no update.");;
+ setStatus(newEntries + " assets are new. " + (needsProcessing.length - newEntries) + " assets have been modified.\n" +
+ needsProcessing.length + " assets need HASH computed. " + (assets.length - needsProcessing.length) + " need no update.");;
processBlock(needsProcessing);
needsProcessing = [];
}).then(function() {
- console.log("Scanned " + assets.length + " asset DB entries in " +
+ setStatus("Scanned " + assets.length + " asset DB entries in " +
((Date.now() - now) / 1000) + "s");
assets = [];
});
});
}).then(function() {
- console.log("Total time to initialize DB and all scans: " + ((Date.now() - initialized) / 1000) + "s");
+ setStatus("Total time to initialize DB and all scans: " + ((Date.now() - initialized) / 1000) + "s");
return photoDB.sequelize.query("SELECT max(scanned) AS scanned FROM photos", {
type: photoDB.sequelize.QueryTypes.SELECT
}).then(function(results) {
@@ -761,11 +784,14 @@ function doScan() {
} else {
lastScan = new Date(results[0].scanned);
}
- console.log("Updating any asset newer than " + moment(lastScan).format());
+ setStatus("Updating any asset newer than " + moment(lastScan).format());
});
}).then(function() {
- scanning = false;
+ setStatus("idle");
return "scan complete";
+ }).catch(function(error) {
+ setStatus(error);
+ throw error;
});
}
@@ -773,8 +799,6 @@ module.exports = {
init: function(db) {
photoDB = db;
},
- scan: function () {
- return doScan();
- }
+ scan: doScan
};