diff --git a/frontend/elements/photo-lightbox.html b/frontend/elements/photo-lightbox.html
index 37d4b35..2a30ab5 100644
--- a/frontend/elements/photo-lightbox.html
+++ b/frontend/elements/photo-lightbox.html
@@ -151,7 +151,7 @@ Polymer({
download: function(event) {
console.log("Download tapped");
var anchor = document.createElement('a');
- anchor.href = this.src;
+ anchor.href = this.base + this.item.path + "/" + this.item.filename;
anchor.setAttribute("download", this.src.replace(/.*\/([^/]+)$/, "$1"));
anchor.style.display = "none";
document.body.appendChild(anchor);
@@ -233,6 +233,12 @@ Polymer({
},
attached: function() {
+ var base = document.querySelector("base");
+ if (base) {
+ this.base = new URL(base.href).pathname.replace(/\/$/, ""); /* Remove trailing slash if there */
+ } else {
+ this.base = "";
+ }
}
});
diff --git a/frontend/src/ketr-photos/ketr-photos.html b/frontend/src/ketr-photos/ketr-photos.html
index 783ec1c..7c627a5 100755
--- a/frontend/src/ketr-photos/ketr-photos.html
+++ b/frontend/src/ketr-photos/ketr-photos.html
@@ -876,7 +876,8 @@
this.lightBoxElement.selected = false;
}
- this.$.lightbox.src = this.base + el.item.path + "/" + el.item.filename;
+ this.$.lightbox.item = el.item;
+ this.$.lightbox.src = this.base + el.item.path + "/thumbs/scaled/" + el.item.filename;
this.lightBoxElement = el;
this.lightBoxElement.selected = true;
this.disableScrolling = true;
diff --git a/server/scanner.js b/server/scanner.js
index 90d729a..f6fa682 100644
--- a/server/scanner.js
+++ b/server/scanner.js
@@ -71,112 +71,83 @@ function scanDir(parent, path) {
}
}).then(function(parent) {
return new Promise(function(resolve, reject) {
- console.log("Scanning " + replacements.path);
+ console.log("Scanning ..." + replacements.path);
fs.readdir(path, function(err, files) {
if (err) {
- console.warn(" Could not readdir " + path);
- return resolve(null);
+ console.warn("Could not readdir " + path);
+ return resolve([]);
}
- scanning++;
+ return resolve(files);
+ });
+ }).then(function(files) {
+ scanning++;
- let hasThumbs = false;
- for (let i = 0; i < files.length; i++) {
- if (files[i] == "thumbs") {
- hasThumbs = true;
- break;
+ /* Remove 'thumbs' and 'raw' directories from being processed */
+ files = files.filter(function(file) {
+ for (var i = 0; i < files.length; i++) {
+ /* If this file has an original NEF/ORF on the system, don't add the JPG to the DB */
+ if (rawExtension.exec(files[i]) && file == files[i].replace(rawExtension, ".jpg")) {
+ return false;
}
- }
-
- let tmp;
- if (!hasThumbs) {
- tmp = mkdirPromise(path + "/thumbs");
- } else {
- tmp = Promise.resolve();
- }
-
- /* Remove 'thumbs' and 'raw' directories from being processed */
- files = files.filter(function(file) {
- for (var i = 0; i < files.length; i++) {
- /* If this file has an original NEF/ORF on the system, don't add the JPG to the DB */
- if (rawExtension.exec(files[i]) && file == files[i].replace(rawExtension, ".jpg")) {
- return false;
- }
-
- /* If there is a different CASE (eg. JPG vs jpg) don't add it, and remove the 'lower case'
- * version from disk. */
- if (file != files[i] && file.toUpperCase() == files[i]) {
- removeNewerFile(path, file, files[i]);
- console.log("Duplicate file in " + path + ": ", file, files[i]);
- return false;
- }
- }
-
- return file != "raw" && file != "thumbs" && file != ".git" && file != "corrupt";
- });
-
- return tmp.then(function() {
- return Promise.map(files, function(file) {
- let filepath = path + "/" + file;
-
- return new Promise(function(resolve, reject) {
- fs.stat(filepath, function(err, stats) {
- if (err) {
- console.warn("Could not stat " + filepath);
- return resolve(false);
- }
-
- if (stats.isDirectory()) {
- return scanDir(parent, filepath, stats).then(function(entry) {
- return resolve(true);
- }).catch(function(error) {
- console.warn("scanDir failed");
- return reject(error);
- });
- }
-
- /* Check file extensions */
- if (!re.exec(file)) {
- return resolve(true);
- }
+ /* If there is a different CASE (eg. JPG vs jpg) don't add it, and remove the 'lower case'
+ * version from disk. */
+ if (file != files[i] && file.toUpperCase() == files[i]) {
+ removeNewerFile(path, file, files[i]);
+ console.log("Duplicate file in " + path + ": ", file, files[i]);
+ return false;
+ }
+ }
- const replacements = {
- path: path.slice(picturesPath.length),
- filename: file.replace(rawExtension, ".jpg") /* We will be converting from NEF/ORF => JPG */
- };
+ return file != "raw" && file != "thumbs" && file != ".git" && file != "corrupt";
+ });
- replacements.path = replacements.path || "/";
+ return mkdir(path + "/thumbs/scaled").then(function() {
+ return Promise.map(files, function(file) {
+ let filepath = path + "/" + file;
- return photoDB.sequelize.query("SELECT id FROM photos WHERE path=:path AND filename=:filename", {
- replacements: replacements,
- type: photoDB.sequelize.QueryTypes.SELECT
- }).then(function(photo) {
- if (photo.length == 0) {
- processQueue.push([ replacements.path, file, stats.mtime, parent ]);
- }
- return resolve(true);
- }).catch(function(error) {
- console.error("Sequelize.query failed");
- return reject(error);
- });
+ return stat(filepath).then(function(stats) {
+ if (stats.isDirectory()) {
+ return scanDir(parent, filepath).catch(function(error) {
+ console.warn("Could not scanDir " + filepath + ": " + error);
});
- });
- }, {
- concurrency: 1
- }).then(function() {
- scanning--;
- if (scanning == 0) {
- const endStamp = Date.now();
- console.log("Scanning completed in " + Math.round(((endStamp - startStamp))) + "ms. " + processQueue.length + " items to process.");
}
- }).then(function() {
- return resolve();
+
+ /* Check file extensions */
+ if (!re.exec(file)) {
+ return;
+ }
+
+ const replacements = {
+ path: path.slice(picturesPath.length),
+ filename: file.replace(rawExtension, ".jpg") /* We will be converting from NEF/ORF => JPG */
+ };
+
+ replacements.path = replacements.path || "/";
+
+ return photoDB.sequelize.query("SELECT id FROM photos WHERE path=:path AND filename=:filename", {
+ replacements: replacements,
+ type: photoDB.sequelize.QueryTypes.SELECT
+ }).then(function(photo) {
+ if (photo.length == 0) {
+ processQueue.push([ replacements.path, file, stats.mtime, parent ]);
+ }
+ }).catch(function(error) {
+ console.error("Sequelize.query failed: ", error);
+ });
+ }).catch(function(error) {
+ console.warn("Could not stat " + filepath + ": " + error);
});
- }).catch(function(error) {
- console.error("Processing 'tmp' failed");
- return reject(error);
+ }, {
+ concurrency: 1
+ }).then(function() {
+ scanning--;
+ if (scanning == 0) {
+ const endStamp = Date.now();
+ console.log("Scanning completed in " + Math.round(((endStamp - startStamp))) + "ms. " + processQueue.length + " items to process.");
+ }
});
});
});
@@ -190,44 +161,59 @@ let processRunning = false;
const { spawn } = require('child_process');
const sharp = require("sharp"), exif = require("exif-reader");
-
-function mkdirPromise(path) {
+const stat = function (path) {
if (path.indexOf(picturesPath) != 0) {
path = picturesPath + path;
}
- return new Promise(function(resolve, reject) {
- fs.stat(path, function(err) {
- if (err && err.code != 'ENOENT') {
- console.warn("Could not stat " + path);
- return reject();
+ return new Promise(function (resolve, reject) {
+ fs.stat(path, function (error, stats) {
+ if (error) {
+ return reject(error);
+ }
+ return resolve(stats);
+ });
+ });
+}
+
+const mkdir = function (_path) {
+ if (_path.indexOf(picturesPath) == 0) {
+ _path = _path.substring(picturesPath.length + 1);
+ }
+
+ let parts = _path.split("/"), path;
+ parts.unshift(picturesPath);
+ return Promise.mapSeries(parts, function (part) {
+ if (!path) {
+ path = picturesPath;
+ } else {
+ path += "/" + part;
+ }
+ return stat(path).catch(function (error) {
+ if (error.code != "ENOENT") {
+ throw error;
}
- if (!err) {
- return resolve();
- }
+ return new Promise(function (resolve, reject) {
+ console.log("mkdir " + path);
+ fs.mkdir(path, function (error) {
+ if (error) {
+ return reject(error);
+ }
- fs.mkdir(path, function(err) {
- if (err && err.code != 'EEXIST') {
- return reject("Unable to create " + path + "\n" + err);
- }
- return resolve();
+ return resolve();
+ });
});
});
});
}
-function existsPromise(path) {
- return new Promise(function(resolve, reject) {
- fs.stat(path, function(err, stats) {
- if (!err) {
- return resolve(true);
- }
- if (err.code == 'ENOENT') {
- return resolve(false);
- }
- return reject(err);
- });
- })
+
+const exists = function(path) {
+ return stat(path).then(function() {
+ return true;
+ }).catch(function() {
+ return false;
+ });
}
function convertRawToJpg(path, file) {
@@ -236,10 +222,10 @@ function convertRawToJpg(path, file) {
path = picturesPath + path;
return new Promise(function(resolve, reject) {
- fs.stat(path + "/" + file.replace(rawExtension, ".jpg"), function(err, stats) {
- if (!err) {
+ return exists(path + "/" + file.replace(rawExtension, ".jpg")).then(function(exist) {
+ if (exist) {
console.log("Skipping already converted file: " + file);
- return resolve();
+ return;
}
const ufraw = spawn("ufraw-batch", [
@@ -259,30 +245,32 @@ function convertRawToJpg(path, file) {
stderr.push(data);
});
- 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);
- return moveCorrupt(path, file).then(function() {
- console.warn("ufraw failed");
- return reject(error);
+ return new Promise(function(ufraw, resolve, reject) {
+ 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);
+ return moveCorrupt(path, file).then(function() {
+ console.warn("ufraw failed");
+ return reject(error);
+ }).catch(function(error) {
+ console.warn("moveCorrupt failed");
+ 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);
+ return reject(err);
+ }
+ return resolve();
+ });
}).catch(function(error) {
- console.warn("moveCorrupt failed");
+ console.warn("mkdir failed");
return reject(error);
});
- }
- return mkdirPromise(path + "/raw").then(function() {
- fs.rename(path + "/" + file, path + "/raw/" + file, function(err) {
- if (err) {
- console.error("Unable to move RAW file: " + path + "/" + file);
- return reject(err);
- }
- return resolve();
- });
- }).catch(function(error) {
- console.warn("mkdirPromise failed");
- return reject(error);
- });
+ }.bind(this, ufraw));
}.bind(this, stderr));
});
});
@@ -295,7 +283,7 @@ function moveCorrupt(path, file) {
console.warn("Moving corrupt file '" + file + "' to " + path + "/corrupt");
- return mkdirPromise(path + "/corrupt").then(function() {
+ return mkdir(path + "/corrupt").then(function() {
return new Promise(function(resolve, reject) {
fs.rename(path + "/" + file, path + "/corrupt/" + file, function(err) {
if (err) {
@@ -323,17 +311,15 @@ function triggerWatcher() {
return Promise.map(processing, function(entry) {
var path = entry[0], file = entry[1], created = entry[2], albumId = entry[3];
-// console.log("Processing " + src);
-
let tmp = Promise.resolve(file);
/* If this is a Nikon RAW file, convert it to JPG and move to /raw dir */
if (rawExtension.exec(file)) {
- tmp = existsPromise(picturesPath + path + "/" + file.replace(rawExtension, ".jpg")).then(function(exists) {
- if (exists) {
+ tmp = exists(picturesPath + path + "/" + file.replace(rawExtension, ".jpg")).then(function(exist) {
+ if (exist) {
return file.replace(rawExtension, ".jpg"); /* We converted from NEF/ORF => JPG */
}
- return mkdirPromise(picturesPath + path + "/raw").then(function() {
+ return mkdir(picturesPath + path + "/raw").then(function() {
return convertRawToJpg(path, file);
}).then(function() {
return file.replace(rawExtension, ".jpg"); /* We converted from NEF/ORF => JPG */
@@ -343,7 +329,6 @@ function triggerWatcher() {
return tmp.then(function(file) {
var src = picturesPath + path + "/" + file,
- dst = picturesPath + path + "/thumbs/" + file,
image = sharp(src);
return image.limitInputPixels(1073741824).metadata().then(function(metadata) {
@@ -407,24 +392,35 @@ function triggerWatcher() {
replacements.taken = replacements.modified = date;
}
- return existsPromise(dst).then(function(exists) {
- let resize;
- if (!exists) {
- resize = image.resize(256, 256).toFile(dst);
- } else {
- resize = Promise.resolve();
+ let dst = picturesPath + path + "/thumbs/" + file;
+
+ return exists(dst).then(function(exist) {
+ if (exist) {
+ return;
}
- return resize.then(function() {
- return photoDB.sequelize.query("INSERT INTO photos " +
- "(albumId,path,filename,added,modified,taken,width,height,name)" +
- "VALUES(:albumId,:path,:filename,DATETIME(:added),DATETIME(:modified),DATETIME(:taken),:width,:height,:name)", {
- replacements: replacements
- });
- }).catch(function(error) {
- console.error("Error resizing image, writing to disc, or updating DB: " + src, error);
+ return image.resize(256, 256).toFile(dst).catch(function(error) {
+ console.error("Error resizing image: " + dst, error);
throw error;
});
+ }).then(function() {
+ let dst = picturesPath + path + "/thumbs/scaled/" + file;
+ return exists(dst).then(function(exist) {
+ if (exist) {
+ return;
+ }
+
+ return image.resize(Math.min(1024, metadata.width)).toFile(dst).catch(function(error) {
+ console.error("Error resizing image: " + dst, error);
+ throw error;
+ });
+ });
+ }).then(function() {
+ return photoDB.sequelize.query("INSERT INTO photos " +
+ "(albumId,path,filename,added,modified,taken,width,height,name)" +
+ "VALUES(:albumId,:path,:filename,DATETIME(:added),DATETIME(:modified),DATETIME(:taken),:width,:height,:name)", {
+ replacements: replacements
+ });
}).then(function() {
toProcess--;
if (moment().add(-5, 'seconds') > lastMessage) {