Album and photo loading is all messed up
Signed-off-by: James Ketrenos <james_git@ketrenos.com>
This commit is contained in:
parent
e9dc145812
commit
d3c08b5389
@ -101,7 +101,7 @@
|
|||||||
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;
|
||||||
},
|
},
|
||||||
|
|
||||||
date: function(item) {
|
date: function(item) {
|
||||||
@ -124,9 +124,9 @@
|
|||||||
attached: function() {
|
attached: function() {
|
||||||
var base = document.querySelector("base");
|
var base = document.querySelector("base");
|
||||||
if (base) {
|
if (base) {
|
||||||
this.base = new URL(base.href).pathname.replace(/\/$/, ""); /* Remove trailing slash if there */
|
this.base = new URL(base.href).pathname.replace(/\/$/, "") + "/"; /* Ensure trailing slash */
|
||||||
} else {
|
} else {
|
||||||
this.base = "";
|
this.base = "/";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
|
|
||||||
<link rel="import" href="../../elements/photo-lightbox.html">
|
<link rel="import" href="../../elements/photo-lightbox.html">
|
||||||
<link rel="import" href="../../elements/photo-thumbnail.html">
|
<link rel="import" href="../../elements/photo-thumbnail.html">
|
||||||
<link rel="import" href="../../elements/pan-line.html">
|
|
||||||
|
|
||||||
<script src="fetch.js"></script>
|
<script src="fetch.js"></script>
|
||||||
|
|
||||||
@ -382,7 +381,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</paper-dialog>
|
</paper-dialog>
|
||||||
<paper-toast id="toast"></paper-toast>
|
<paper-toast id="toast"></paper-toast>
|
||||||
<photo-lightbox tabindex="0" id="lightbox" on-close="lightBoxClose" on-next="lightBoxNext" on-previous="lightBoxPrevious"></photo-lightbox>
|
<photo-lightbox tabindex="0"
|
||||||
|
id="lightbox" on-close="lightBoxClose" on-next="lightBoxNext" on-previous="lightBoxPrevious"></photo-lightbox>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -878,7 +878,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.$.lightbox.item = el.item;
|
this.$.lightbox.item = el.item;
|
||||||
this.$.lightbox.src = this.base + el.item.path + "/thumbs/scaled/" + el.item.filename;
|
this.$.lightbox.src = this.base + el.item.path + "thumbs/scaled/" + el.item.filename;
|
||||||
this.lightBoxElement = el;
|
this.lightBoxElement = el;
|
||||||
this.lightBoxElement.selected = true;
|
this.lightBoxElement.selected = true;
|
||||||
this.disableScrolling = true;
|
this.disableScrolling = true;
|
||||||
@ -943,7 +943,12 @@
|
|||||||
// thumbnail.width = this.calcWidth;
|
// thumbnail.width = this.calcWidth;
|
||||||
thumbnail.addEventListener("load-image", this._imageTap.bind(this));
|
thumbnail.addEventListener("load-image", this._imageTap.bind(this));
|
||||||
thumbnail.addEventListener("load-album", this.loadAlbum.bind(this));
|
thumbnail.addEventListener("load-album", this.loadAlbum.bind(this));
|
||||||
datetime = new Date((photo.taken || photo.modified || photo.added) + " GMT").toISOString().replace(/T.*$/, "");
|
try {
|
||||||
|
datetime = new Date((photo.taken || photo.modified || photo.added).replace(/T.*/, "") + " GMT").toISOString().replace(/T.*$/, "");
|
||||||
|
} catch (error) {
|
||||||
|
console.log(JSON.stringify(photo, null, 2));
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
var dateBlock = this.root.querySelector("#date-" + datetime), thumbnails;
|
var dateBlock = this.root.querySelector("#date-" + datetime), thumbnails;
|
||||||
if (!dateBlock) {
|
if (!dateBlock) {
|
||||||
@ -1043,7 +1048,7 @@
|
|||||||
query += key + "=" + encodeURIComponent(params[key]);
|
query += key + "=" + encodeURIComponent(params[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = this.path || "", mode = this.mode;
|
var path = this.path || "/", mode = this.mode;
|
||||||
if (mode != "albums") {
|
if (mode != "albums") {
|
||||||
path = "/" + mode;
|
path = "/" + mode;
|
||||||
if (mode == "time") {
|
if (mode == "time") {
|
||||||
@ -1053,7 +1058,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var username = this.user ? this.user.username : "";
|
var username = this.user ? this.user.username : "";
|
||||||
console.log("Requesting " + this.limit + " photos.");
|
console.log("Requesting " + this.limit + " photos from " + path);
|
||||||
window.fetch("api/v1/photos" + path + query, function(path, error, xhr) {
|
window.fetch("api/v1/photos" + path + query, function(path, error, xhr) {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
||||||
@ -1063,7 +1068,7 @@
|
|||||||
|
|
||||||
if ((username != (this.user ? this.user.username : "")) ||
|
if ((username != (this.user ? this.user.username : "")) ||
|
||||||
(mode != this.mode) ||
|
(mode != this.mode) ||
|
||||||
((mode == "albums") && (path != (this.path || ""))) ||
|
((mode == "albums") && (path != (this.path || "/"))) ||
|
||||||
((mode == "memories") && (path != ("/memories/" + (this.date || ""))))) {
|
((mode == "memories") && (path != ("/memories/" + (this.date || ""))))) {
|
||||||
console.log("Skipping results for old query. Triggering re-fetch of photos for new path or mode.");
|
console.log("Skipping results for old query. Triggering re-fetch of photos for new path or mode.");
|
||||||
this._loadPhotos();
|
this._loadPhotos();
|
||||||
@ -1089,9 +1094,9 @@
|
|||||||
|
|
||||||
var base = document.querySelector("base");
|
var base = document.querySelector("base");
|
||||||
if (base) {
|
if (base) {
|
||||||
this.base = new URL(base.href).pathname.replace(/\/$/, ""); /* Remove trailing slash if there */
|
this.base = new URL(base.href).pathname.replace(/\/$/, "") + "/"; /* Make sure there is a trailing slash */
|
||||||
} else {
|
} else {
|
||||||
this.base = "";
|
this.base = "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(results.items.length + " photos received.");
|
console.log(results.items.length + " photos received.");
|
||||||
@ -1112,7 +1117,7 @@
|
|||||||
}
|
}
|
||||||
this.loadingAlbums = true;
|
this.loadingAlbums = true;
|
||||||
|
|
||||||
var path = this.path || "";
|
var path = this.path || "/";
|
||||||
window.fetch("api/v1/albums" + path, function(path, error, xhr) {
|
window.fetch("api/v1/albums" + path, function(path, error, xhr) {
|
||||||
this.loadingAlbums = false;
|
this.loadingAlbums = false;
|
||||||
|
|
||||||
@ -1120,14 +1125,14 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path != (this.path || "")) {
|
if (path != (this.path || "/")) {
|
||||||
console.log("Skipping results for old query. Triggering re-fetch of albums for new path.");
|
console.log("Skipping results for old query. Triggering re-fetch of albums for new path.");
|
||||||
this._loadAlbums();
|
this._loadAlbums();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log("Error loading album: " + (this.path || ""));
|
console.log("Error loading album: " + (this.path || "/"));
|
||||||
console.error(JSON.stringify(error, null, 2));
|
console.error(JSON.stringify(error, null, 2));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,9 @@ require("../db/photos").then(function(db) {
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get("/*", function(req, res/*, next*/) {
|
router.get("/*", function(req, res/*, next*/) {
|
||||||
let url = decodeURI(req.url).replace(/\?.*$/, ""),
|
let url = decodeURI(req.url).replace(/\?.*$/, "").replace(/^\//, ""),
|
||||||
query = "SELECT * FROM albums WHERE path=:path";
|
query = "SELECT * FROM albums WHERE path=:path";
|
||||||
|
console.log("Looking up album: " + url);
|
||||||
return photoDB.sequelize.query(query, {
|
return photoDB.sequelize.query(query, {
|
||||||
replacements: {
|
replacements: {
|
||||||
path: url
|
path: url
|
||||||
|
@ -40,11 +40,11 @@ router.get("/memories/*", function(req, res/*, next*/) {
|
|||||||
if (id == -1) {
|
if (id == -1) {
|
||||||
index = "";
|
index = "";
|
||||||
} else {
|
} else {
|
||||||
index = " AND ((taken=DATE(:cursor) AND id<"+id+ ") OR taken<DATE(:cursor))";
|
index = " AND ((taken=DATE(:cursor) AND photos.id<"+id+ ") OR taken<DATE(:cursor))";
|
||||||
}
|
}
|
||||||
|
|
||||||
let date = new Date(decodeURI(req.url).replace(/\?.*$/, ""));
|
let date = new Date(decodeURI(req.url).replace(/\?.*$/, ""));
|
||||||
let query = "SELECT * FROM photos WHERE strftime('%m%d',taken)=strftime('%m%d',:date) " + index + " ORDER BY taken DESC,id DESC LIMIT " + (limit * 2 + 1);
|
let query = "SELECT photos.*,albums.path AS path FROM photos INNER JOIN albums ON albums.id=photos.albumId WHERE strftime('%m%d',taken)=strftime('%m%d',:date) " + index + " ORDER BY taken DESC,id DESC LIMIT " + (limit * 2 + 1);
|
||||||
|
|
||||||
// console.log("Memories for " + date.toISOString().replace(/T.*/, ""));
|
// console.log("Memories for " + date.toISOString().replace(/T.*/, ""));
|
||||||
// console.log(query);
|
// console.log(query);
|
||||||
@ -109,11 +109,11 @@ router.get("/*", function(req, res/*, next*/) {
|
|||||||
if (id == -1) {
|
if (id == -1) {
|
||||||
index = "";
|
index = "";
|
||||||
} else {
|
} else {
|
||||||
index = " AND ((taken=DATE(:cursor) AND id<"+id+ ") OR taken<DATE(:cursor))";
|
index = " AND ((taken=DATE(:cursor) AND photos.id<"+id+ ") OR taken<DATE(:cursor))";
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = decodeURI(req.url).replace(/\?.*$/, ""),
|
let path = decodeURI(req.url).replace(/\?.*$/, ""),
|
||||||
query = "SELECT photos.* FROM photos INNER JOIN albums ON albums.id=photos.albumId AND albums.path LIKE :path " + index + " ORDER BY taken DESC,id DESC LIMIT " + (limit * 2 + 1);
|
query = "SELECT photos.*,albums.path AS path FROM photos INNER JOIN albums ON albums.id=photos.albumId AND albums.path LIKE :path " + index + " ORDER BY taken DESC,id DESC LIMIT " + (limit * 2 + 1);
|
||||||
|
|
||||||
console.log("Fetching from: " + path);
|
console.log("Fetching from: " + path);
|
||||||
|
|
||||||
|
@ -195,15 +195,52 @@ function processBlock(items) {
|
|||||||
processQueue = processQueue.concat(items);
|
processQueue = processQueue.concat(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!processRunning && processQueue.length) {
|
if (processRunning || processQueue.length == 0) {
|
||||||
let lastMessage = moment(), toProcess = processQueue.length, processing = processQueue.splice(0);
|
return;
|
||||||
processRunning = true;
|
}
|
||||||
/* Sort to newest files to be processed first */
|
|
||||||
processing.sort(function(a, b) {
|
|
||||||
return a.stats.mtime - b.stats.mtime;
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.map(processing, function(asset) {
|
let lastMessage = moment(), toProcess = processQueue.length, processing = processQueue.splice(0),
|
||||||
|
needsProcessing = [], duplicates = [];
|
||||||
|
processRunning = true;
|
||||||
|
/* Sort to newest files to be processed first */
|
||||||
|
processing.sort(function(a, b) {
|
||||||
|
return a.stats.mtime - b.stats.mtime;
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.map(processing, function(asset) {
|
||||||
|
return computeHash(picturesPath + asset.album.path + asset.filename).then(function(hash) {
|
||||||
|
asset.hash = hash;
|
||||||
|
});
|
||||||
|
}).then(function() {
|
||||||
|
/* Needs to be one at a time in case there are multiple HASH collisions */
|
||||||
|
return Promise.mapSeries(processing, function(asset) {
|
||||||
|
return photoDB.sequelize.query("SELECT * FROM photohashes WHERE hash=:hash OR photoId=:id", {
|
||||||
|
replacements: asset,
|
||||||
|
type: photoDB.sequelize.QueryTypes.SELECT
|
||||||
|
}).then(function(results) {
|
||||||
|
let query;
|
||||||
|
if (results.length == 0) {
|
||||||
|
query = "INSERT INTO photohashes (hash,photoId) VALUES(:hash,:id)";
|
||||||
|
} 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: " + asset.id + " vs " + results[0].photoId + ". Skipping " + asset.album.path + asset.filename);
|
||||||
|
duplicates.push(asset);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return photoDB.sequelize.query(query, {
|
||||||
|
replacements: asset
|
||||||
|
}).then(function() {
|
||||||
|
/* HASH has been updated; add to the needsProcessing array */
|
||||||
|
needsProcessing.push(asset);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).then(function() {
|
||||||
|
return Promise.map(needsProcessing, function(asset) {
|
||||||
var path = asset.album.path,
|
var path = asset.album.path,
|
||||||
file = asset.filename,
|
file = asset.filename,
|
||||||
created = asset.stats.ctime,
|
created = asset.stats.ctime,
|
||||||
@ -308,8 +345,7 @@ function processBlock(items) {
|
|||||||
});
|
});
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
return photoDB.sequelize.query("UPDATE photos SET " +
|
return photoDB.sequelize.query("UPDATE photos SET " +
|
||||||
"(added,modified,taken,width,height,scanned)" +
|
"added=:added,modified=:modified,taken=:taken,width=:width,height=:height,scanned=CURRENT_TIMESTAMP", {
|
||||||
"VALUES(:added,:modified,:taken,:width,:height,CURRENT_TIMESTAMP)", {
|
|
||||||
replacements: asset
|
replacements: asset
|
||||||
});
|
});
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
@ -349,10 +385,10 @@ function processBlock(items) {
|
|||||||
});
|
});
|
||||||
}, {
|
}, {
|
||||||
concurrency: 1
|
concurrency: 1
|
||||||
}).then(function() {
|
|
||||||
console.log("Completed processing queue.");
|
|
||||||
});
|
});
|
||||||
}
|
}).then(function() {
|
||||||
|
console.log("Completed processing queue. " + duplicates.length + " duplicates.");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -360,7 +396,7 @@ function scanDir(parent, path) {
|
|||||||
let re = new RegExp("\.((" + extensions.join(")|(") + "))$", "i"),
|
let re = new RegExp("\.((" + extensions.join(")|(") + "))$", "i"),
|
||||||
album = {
|
album = {
|
||||||
path: path.slice(picturesPath.length), /* path already ends in '/' */
|
path: path.slice(picturesPath.length), /* path already ends in '/' */
|
||||||
name: path.replace(/.*\//, "").replace(/_/g, " "),
|
name: path.replace(/\/$/, "").replace(/.*\//, "").replace(/_/g, " "),
|
||||||
parent: parent,
|
parent: parent,
|
||||||
allAssetCount: 0,
|
allAssetCount: 0,
|
||||||
allAlbumCount: 0
|
allAlbumCount: 0
|
||||||
@ -458,7 +494,7 @@ function findOrCreateDBAlbum(album) {
|
|||||||
}).then(function(results) {
|
}).then(function(results) {
|
||||||
if (results.length == 0) {
|
if (results.length == 0) {
|
||||||
if (!album.parent) {
|
if (!album.parent) {
|
||||||
console.warn("Creating top level album: " + album.path);
|
console.warn("Creating top level album: " + picturesPath);
|
||||||
}
|
}
|
||||||
return photoDB.sequelize.query("INSERT INTO albums (path,parentId,name) VALUES(:path,:parentId,:name)", {
|
return photoDB.sequelize.query("INSERT INTO albums (path,parentId,name) VALUES(:path,:parentId,:name)", {
|
||||||
replacements: album
|
replacements: album
|
||||||
@ -540,17 +576,20 @@ module.exports = {
|
|||||||
* 2. Check if entry in DB. Check mod-time in DB vs. stats from #1
|
* 2. Check if entry in DB. Check mod-time in DB vs. stats from #1
|
||||||
* - For albums
|
* - For albums
|
||||||
* - For assets
|
* - For assets
|
||||||
* 3. If not in DB, or mod-time changed, compute HASH of the file
|
* 3. If not in DB, or mod-time changed, queue for HASH CHECK
|
||||||
* 4. Check for HASH in photohash -- skip?
|
*
|
||||||
* 5. Check for and create thumbs/FILE thumbs/scaled/FILE
|
* HASH CHECK
|
||||||
* 6. If necessary, create JPG from RAW
|
* 1. Compute HASH
|
||||||
* 7. Update last-scanned date in DB for entry
|
* 2. Check for HASH in photohash -- skip?
|
||||||
* 8. Look up all DB entries with last-scanned date < NOW -- purge from DB (they were
|
* 3. Check for and create thumbs/FILE thumbs/scaled/FILE
|
||||||
|
* 4. If necessary, create JPG from RAW
|
||||||
|
* 5. Update last-scanned date in DB for entry
|
||||||
|
* 6. Look up all DB entries with last-scanned date < NOW -- purge from DB (they were
|
||||||
* removed on disk)? Also purge from the HASH table.
|
* removed on disk)? Also purge from the HASH table.
|
||||||
*/
|
*/
|
||||||
let initialized = Date.now();
|
let initialized = Date.now();
|
||||||
let now = Date.now();
|
let now = Date.now();
|
||||||
const needsProcessing = [], duplicates = [];
|
const needsProcessing = [];
|
||||||
return scanDir(null, picturesPath).spread(function(albums, assets) {
|
return scanDir(null, picturesPath).spread(function(albums, assets) {
|
||||||
console.log("Found " + assets.length + " assets in " + albums.length + " albums after " +
|
console.log("Found " + assets.length + " assets in " + albums.length + " albums after " +
|
||||||
((Date.now() - now) / 1000) + "s");
|
((Date.now() - now) / 1000) + "s");
|
||||||
@ -565,11 +604,11 @@ module.exports = {
|
|||||||
((Date.now() - now) / 1000) + "s");
|
((Date.now() - now) / 1000) + "s");
|
||||||
now = Date.now();
|
now = Date.now();
|
||||||
|
|
||||||
let processed = 0, start = Date.now(), last = 0, hashNeeded = [];
|
let processed = 0, start = Date.now(), last = 0;
|
||||||
return Promise.map(assets, function(asset) {
|
return Promise.map(assets, function(asset) {
|
||||||
return findOrUpdateDBAsset(asset).then(function(asset) {
|
return findOrUpdateDBAsset(asset).then(function(asset) {
|
||||||
if (asset.scanned < asset.stats.mtime) {
|
if (asset.scanned < asset.stats.mtime) {
|
||||||
hashNeeded.push(asset);
|
needsProcessing.push(asset);
|
||||||
}
|
}
|
||||||
}).then(function(asset) {
|
}).then(function(asset) {
|
||||||
processed++;
|
processed++;
|
||||||
@ -588,46 +627,11 @@ module.exports = {
|
|||||||
}, {
|
}, {
|
||||||
concurrency: 5
|
concurrency: 5
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
console.log(hashNeeded.length + " assets need HASH computed");
|
console.log(needsProcessing.length + " assets need HASH computed");
|
||||||
return Promise.map(hashNeeded, function(asset) {
|
processBlock(needsProcessing);
|
||||||
return computeHash(picturesPath + asset.album.path + asset.filename).then(function(hash) {
|
|
||||||
asset.hash = hash;
|
|
||||||
});
|
|
||||||
}).then(function() {
|
|
||||||
/* Needs to be one at a time in case there are multiple HASH collisions */
|
|
||||||
return Promise.mapSeries(hashNeeded, function(asset) {
|
|
||||||
return db.sequelize.query("SELECT * FROM photohashes WHERE hash=:hash OR photoId=:id", {
|
|
||||||
replacements: asset,
|
|
||||||
type: photoDB.sequelize.QueryTypes.SELECT
|
|
||||||
}).then(function(results) {
|
|
||||||
let query;
|
|
||||||
if (results.length == 0) {
|
|
||||||
query = "INSERT INTO photohashes (hash,photoId) VALUES(:hash,:id)";
|
|
||||||
} 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: " + asset.id + " vs " + results[0].photoId + ". Skipping " + asset.album.path + asset.filename);
|
|
||||||
duplicates.push(asset);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.sequelize.query(query, {
|
|
||||||
replacements: asset
|
|
||||||
}).then(function() {
|
|
||||||
/* HASH has been updated; add to the needsProcessing array */
|
|
||||||
needsProcessing.push(asset);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}).then(function() {
|
|
||||||
processBlock(needsProcessing);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
console.log("Processed " + assets.length + " asset DB entries in " +
|
console.log("Scanned " + assets.length + " asset DB entries in " +
|
||||||
((Date.now() - now) / 1000) + "s");
|
((Date.now() - now) / 1000) + "s");
|
||||||
console.log(duplicates.length + " duplicates.");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user