Added albums API
Signed-off-by: James Ketrenos <james_git@ketrenos.com>
This commit is contained in:
parent
29dafae886
commit
bfd872f60a
@ -34,12 +34,17 @@
|
|||||||
:host:hover {
|
:host:hover {
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div[path]:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="layout vertical">
|
<div class="layout vertical">
|
||||||
<div>[[item.id]]</div>
|
<div>[[item.id]]</div>
|
||||||
<div>[[date(item)]]</div>
|
<div>[[date(item)]]</div>
|
||||||
<div>[[item.path]]</div>
|
<div path on-tap="_pathTap">[[item.path]]</div>
|
||||||
<div>[[item.filename]]</div>
|
<div>[[item.filename]]</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -61,6 +66,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
"tap": "_imageTap"
|
||||||
|
},
|
||||||
|
|
||||||
observers: [
|
observers: [
|
||||||
"widthChanged(width)",
|
"widthChanged(width)",
|
||||||
"thumbChanged(thumbpath)"
|
"thumbChanged(thumbpath)"
|
||||||
@ -76,7 +85,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
safeItemThumbFilepath: function(item, base) {
|
safeItemThumbFilepath: function(item, base) {
|
||||||
return "'" + (base + item.path + "/thumbs/" + item.filename).replace(/'/, "\\'") + "'";
|
return "'" + (base + encodeURI(item.path) + "/thumbs/" + encodeURI(item.filename)).replace(/'/, "\\'") + "'";
|
||||||
},
|
},
|
||||||
|
|
||||||
date: function(item) {
|
date: function(item) {
|
||||||
@ -85,11 +94,15 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
_imageTap: function(event) {
|
_imageTap: function(event) {
|
||||||
window.open(this.base + event.model.item.path + "/" + event.model.item.filename, "image");
|
this.fire("load-image");
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
|
|
||||||
_pathTap: function(event) {
|
_pathTap: function(event) {
|
||||||
window.location.href = event.model.item.filepath;
|
this.fire("load-album", this.item.path);
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
|
|
||||||
attached: function() {
|
attached: function() {
|
||||||
|
@ -48,6 +48,15 @@
|
|||||||
:host {
|
:host {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#breadcrumb > div {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#breadcrumb > div:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
app-toolbar {
|
app-toolbar {
|
||||||
background-color: rgba(64, 0, 64, 0.5);
|
background-color: rgba(64, 0, 64, 0.5);
|
||||||
color: white;
|
color: white;
|
||||||
@ -76,6 +85,10 @@
|
|||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#header > * {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
app-header-layout {
|
app-header-layout {
|
||||||
--layout-fit: {
|
--layout-fit: {
|
||||||
overflow-y: hidden !important;
|
overflow-y: hidden !important;
|
||||||
@ -113,15 +126,17 @@
|
|||||||
|
|
||||||
<app-header-layout reveals>
|
<app-header-layout reveals>
|
||||||
<app-header fixed>
|
<app-header fixed>
|
||||||
<div>
|
<div id="header" class="layout horizontal center">
|
||||||
<paper-spinner active$="[[loading]]" class="thin"></paper-spinner>
|
<paper-spinner active$="[[loading]]" class="thin"></paper-spinner>
|
||||||
<paper-radio-group selected="{{order}}">
|
<paper-radio-group selected="{{order}}">
|
||||||
<paper-radio-button name="by-date">By date</paper-radio-button>
|
<paper-radio-button name="by-date">By date</paper-radio-button>
|
||||||
<paper-radio-button name="by-album">By album</paper-radio-button>
|
<paper-radio-button name="by-album">By album</paper-radio-button>
|
||||||
</paper-radio-group>
|
</paper-radio-group>
|
||||||
<paper-checkbox checked$="[[limitPerFolder]]" on-checked-changed="onLimitPerFolderChecked">Limit per folder</paper-checkbox>
|
<paper-checkbox checked$="[[limitPerFolder]]" on-checked-changed="onLimitPerFolderChanged">Limit per folder</paper-checkbox>
|
||||||
<paper-checkbox checked$="[[breakOnDayChange]]" on-checked-changed="onBreakOnDayChanged">Break on day change</paper-checkbox>
|
<paper-checkbox checked$="[[breakOnDayChange]]" on-checked-changed="onBreakOnDayChanged">Break on day change</paper-checkbox>
|
||||||
<div>[[path]]</div>
|
<div id="breadcrumb" class="horizontal layout center"><template is="dom-repeat" items="[[breadcrumb(path)]]">
|
||||||
|
<div on-tap="loadPath">[[item.name]] /</div>
|
||||||
|
</template></div>
|
||||||
</div>
|
</div>
|
||||||
</app-header>
|
</app-header>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
@ -174,11 +189,31 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
breadcrumb: function(path) {
|
||||||
|
var crumbs = path.split("/"), parts = [];
|
||||||
|
path = "";
|
||||||
|
crumbs.forEach(function(crumb, index) {
|
||||||
|
if (crumb) {
|
||||||
|
path += "/" + crumb;
|
||||||
|
}
|
||||||
|
parts.push({
|
||||||
|
name: crumb ? crumb : "Top",
|
||||||
|
path: path
|
||||||
|
})
|
||||||
|
});
|
||||||
|
return parts;
|
||||||
|
},
|
||||||
|
|
||||||
observers: [
|
observers: [
|
||||||
"widthChanged(calcWidth)"
|
"widthChanged(calcWidth)",
|
||||||
|
"orderChanged(order)"
|
||||||
],
|
],
|
||||||
|
|
||||||
onLimitPerFolder: function(event) {
|
orderChanged: function(order) {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
onLimitPerFolderChanged: function(event) {
|
||||||
if (!this.photos) {
|
if (!this.photos) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -206,6 +241,25 @@
|
|||||||
"iron-resize" : "onResize"
|
"iron-resize" : "onResize"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
loadPath: function(event) {
|
||||||
|
this.path = event.model.item.path;
|
||||||
|
Polymer.dom(this.$.thumbnails).innerHTML = "";
|
||||||
|
this.photos = [];
|
||||||
|
this.next = false;
|
||||||
|
this._loadPhotos();
|
||||||
|
},
|
||||||
|
|
||||||
|
loadAlbum: function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
this.path = event.detail;
|
||||||
|
Polymer.dom(this.$.thumbnails).innerHTML = "";
|
||||||
|
this.photos = [];
|
||||||
|
this.next = false;
|
||||||
|
this._loadPhotos();
|
||||||
|
},
|
||||||
|
|
||||||
onScroll: function(event) {
|
onScroll: function(event) {
|
||||||
if (this.disableScrolling) {
|
if (this.disableScrolling) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -300,35 +354,79 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
loadNextPhotos: function() {
|
loadNextPhotos: function() {
|
||||||
|
if (!this.photos.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var cursor = this.photos[this.photos.length - 1];
|
var cursor = this.photos[this.photos.length - 1];
|
||||||
this._loadPhotos(cursor.taken.toString().replace(/T.*/, "") + "_" + cursor.id, -1, true);
|
this._loadPhotos(cursor.taken.toString().replace(/T.*/, "") + "_" + cursor.id, -1, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadPrevPhotos: function() {
|
loadPrevPhotos: function() {
|
||||||
|
if (!this.photos.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var cursor = this.photos[0];
|
var cursor = this.photos[0];
|
||||||
this._loadPhotos(cursor.taken.toString().replace(/T.*/, "") + "_" + cursor.id, +1);
|
this._loadPhotos(cursor.taken.toString().replace(/T.*/, "") + "_" + cursor.id, +1);
|
||||||
},
|
},
|
||||||
|
|
||||||
appendItems: function(photos) {
|
appendItems: function(photos) {
|
||||||
photos.forEach(function(photo) {
|
var thisDay;
|
||||||
var thumbnail = document.createElement("photo-thumbnail");
|
if (this.limitPerFolder) {
|
||||||
|
console.log("Max per day: " + this.cols);
|
||||||
|
}
|
||||||
|
|
||||||
|
thisDay = 0;
|
||||||
|
for (var i = 0; i < photos.length; i++) {
|
||||||
|
var photo = photos[i],
|
||||||
|
thumbnail = document.createElement("photo-thumbnail"),
|
||||||
|
datetime;
|
||||||
thumbnail.item = photo;
|
thumbnail.item = photo;
|
||||||
thumbnail.width = this.calcWidth;
|
thumbnail.width = this.calcWidth;
|
||||||
thumbnail.addEventListener("click", this._imageTap.bind(this));
|
thumbnail.addEventListener("load-image", this._imageTap.bind(this));
|
||||||
|
thumbnail.addEventListener("load-album", this.loadAlbum.bind(this));
|
||||||
|
datetime = (photo.taken || photo.modified || photo.added).replace(/T.*$/, "");
|
||||||
|
|
||||||
if (this.breakOnDayChange) {
|
if (this.breakOnDayChange) {
|
||||||
var datetime = (photo.taken || photo.modified || photo.added).replace(/T.*$/, ""),
|
var dateBlock = this.querySelector("#date-" + datetime);
|
||||||
dateBlock = this.querySelector("#date-" + datetime);
|
|
||||||
if (!dateBlock) {
|
if (!dateBlock) {
|
||||||
dateBlock = document.createElement("div");
|
dateBlock = document.createElement("div");
|
||||||
dateBlock.id = "date-" + datetime;
|
dateBlock.id = "date-" + datetime;
|
||||||
dateBlock.classList.add("date-line");
|
dateBlock.classList.add("date-line");
|
||||||
dateBlock.textContent = datetime;
|
dateBlock.textContent = datetime;
|
||||||
Polymer.dom(this.$.thumbnails).appendChild(dateBlock);
|
Polymer.dom(this.$.thumbnails).appendChild(dateBlock);
|
||||||
|
thisDay = 0;
|
||||||
|
} else {
|
||||||
|
if (this.limitPerFolder) {
|
||||||
|
var thumbs = [], el = dateBlock.nextElementSibling;
|
||||||
|
while (el && el.tagName == "PHOTO-THUMBNAIL") {
|
||||||
|
thumbs.push(el);
|
||||||
|
el = el.nextElementSibling;
|
||||||
|
}
|
||||||
|
thisDay = thumbs.length;
|
||||||
|
while (thisDay > this.cols) {
|
||||||
|
Polymer.dom(thumbs[thisDay - 1].parentElement).removeChild(thumbs[thisDay - 1]);
|
||||||
|
thisDay--;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.limitPerFolder || thisDay < this.cols) {
|
||||||
Polymer.dom(this.$.thumbnails).appendChild(thumbnail);
|
Polymer.dom(this.$.thumbnails).appendChild(thumbnail);
|
||||||
}.bind(this));
|
thisDay++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.limitPerFolder && thisDay == this.cols) {
|
||||||
|
while (i + 1 < photos.length) {
|
||||||
|
photo = photos[i + 1];
|
||||||
|
if (datetime != (photo.taken || photo.modified || photo.added).replace(/T.*$/, "")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
thisDay = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_loadPhotos: function(start, dir, append) {
|
_loadPhotos: function(start, dir, append) {
|
||||||
@ -341,7 +439,7 @@
|
|||||||
var params = {
|
var params = {
|
||||||
limit: Math.ceil(this.clientWidth / 200) * Math.ceil(this.clientHeight / 200),
|
limit: Math.ceil(this.clientWidth / 200) * Math.ceil(this.clientHeight / 200),
|
||||||
dir: dir
|
dir: dir
|
||||||
}, url = "";
|
}, query = "";
|
||||||
if (start) {
|
if (start) {
|
||||||
params.next = start;
|
params.next = start;
|
||||||
}
|
}
|
||||||
@ -349,15 +447,15 @@
|
|||||||
params.sort = this.sortOrder;
|
params.sort = this.sortOrder;
|
||||||
}
|
}
|
||||||
for (var key in params) {
|
for (var key in params) {
|
||||||
if (url == "") {
|
if (query == "") {
|
||||||
url = "?";
|
query = "?";
|
||||||
} else {
|
} else {
|
||||||
url += "&";
|
query += "&";
|
||||||
}
|
}
|
||||||
url += key + "=" + encodeURIComponent(params[key]);
|
query += key + "=" + encodeURIComponent(params[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.fetch("api/v1/photos" + url, function(error, xhr) {
|
window.fetch("api/v1/photos" + (this.path || "") + query, function(error, xhr) {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(JSON.stringify(error, null, 2));
|
console.error(JSON.stringify(error, null, 2));
|
||||||
@ -391,7 +489,7 @@
|
|||||||
this.photos = results.items;
|
this.photos = results.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dir == +1) {
|
if (dir == -1) {
|
||||||
this.prev = start ? true : false;
|
this.prev = start ? true : false;
|
||||||
this.next = results.more ? true : false;
|
this.next = results.more ? true : false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,6 +64,7 @@ app.use(function(req, res, next){
|
|||||||
|
|
||||||
app.use(basePath + "api/v1/photos", require("./routes/photos"));
|
app.use(basePath + "api/v1/photos", require("./routes/photos"));
|
||||||
app.use(basePath + "api/v1/days", require("./routes/days"));
|
app.use(basePath + "api/v1/days", require("./routes/days"));
|
||||||
|
app.use(basePath + "api/v1/albums", require("./routes/albums"));
|
||||||
|
|
||||||
/* Declare the "catch all" index route last; the final route is a 404 dynamic router */
|
/* Declare the "catch all" index route last; the final route is a 404 dynamic router */
|
||||||
app.use(basePath, require("./routes/index"));
|
app.use(basePath, require("./routes/index"));
|
||||||
|
71
server/routes/albums.js
Normal file
71
server/routes/albums.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const express = require("express"),
|
||||||
|
fs = require("fs"),
|
||||||
|
url = require("url"),
|
||||||
|
config = require("config"),
|
||||||
|
moment = require("moment");
|
||||||
|
|
||||||
|
let photoDB;
|
||||||
|
|
||||||
|
require("../db").then(function(db) {
|
||||||
|
photoDB = db;
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/*", function(req, res/*, next*/) {
|
||||||
|
let url = decodeURI(req.url).replace(/\?.*$/, ""),
|
||||||
|
query = "SELECT * FROM albums WHERE path=:path";
|
||||||
|
|
||||||
|
if (url == "/") {
|
||||||
|
url = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return photoDB.sequelize.query(query, {
|
||||||
|
replacements: {
|
||||||
|
path: url
|
||||||
|
},
|
||||||
|
type: photoDB.Sequelize.QueryTypes.SELECT
|
||||||
|
}).then(function(parent) {
|
||||||
|
if (parent.length == 0) {
|
||||||
|
return res.status(404).send(req.url + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = parent[0];
|
||||||
|
for (var key in parent) {
|
||||||
|
if (parent[key] instanceof Date) {
|
||||||
|
parent[key].setHours(0, 0, 0, 0);
|
||||||
|
parent[key] = moment(parent[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return photoDB.sequelize.query("SELECT * FROM albums WHERE parentId=:parentId", {
|
||||||
|
replacements: {
|
||||||
|
parentId: parent.id
|
||||||
|
},
|
||||||
|
type: photoDB.Sequelize.QueryTypes.SELECT
|
||||||
|
}).then(function(children) {
|
||||||
|
children.forEach(function(album) {
|
||||||
|
for (var key in album) {
|
||||||
|
if (album[key] instanceof Date) {
|
||||||
|
album[key].setHours(0, 0, 0, 0);
|
||||||
|
album[key] = moment(album[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let results = {
|
||||||
|
album: parent,
|
||||||
|
children: children
|
||||||
|
};
|
||||||
|
return res.status(200).json(results);
|
||||||
|
});
|
||||||
|
}).catch(function(error) {
|
||||||
|
|
||||||
|
console.error("Query failed: " + query);
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
@ -24,7 +24,7 @@ const router = express.Router();
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
router.get("/", function(req, res/*, next*/) {
|
router.get("/*", function(req, res/*, next*/) {
|
||||||
let limit = parseInt(req.query.limit) || 50,
|
let limit = parseInt(req.query.limit) || 50,
|
||||||
order = (parseInt(req.query.dir) == -1) ? "DESC" : "", id, cursor, index;
|
order = (parseInt(req.query.dir) == -1) ? "DESC" : "", id, cursor, index;
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ router.get("/", function(req, res/*, next*/) {
|
|||||||
return photoDB.sequelize.query(query, {
|
return photoDB.sequelize.query(query, {
|
||||||
replacements: {
|
replacements: {
|
||||||
cursor: cursor,
|
cursor: cursor,
|
||||||
path: req.url.replace(/\?.*$/, "") + "%"
|
path: decodeURI(req.url).replace(/\?.*$/, "") + "%"
|
||||||
},
|
},
|
||||||
type: photoDB.Sequelize.QueryTypes.SELECT
|
type: photoDB.Sequelize.QueryTypes.SELECT
|
||||||
}).then(function(photos) {
|
}).then(function(photos) {
|
||||||
@ -100,8 +100,8 @@ router.get("/", function(req, res/*, next*/) {
|
|||||||
photos.slice(limit, photos.length);
|
photos.slice(limit, photos.length);
|
||||||
}
|
}
|
||||||
photos.forEach(function(photo) {
|
photos.forEach(function(photo) {
|
||||||
photo.path = encodeURI(photo.path);
|
// photo.path = encodeURI(photo.path);
|
||||||
photo.filename = encodeURI(photo.filename);
|
// photo.filename = encodeURI(photo.filename);
|
||||||
});
|
});
|
||||||
|
|
||||||
let results = {
|
let results = {
|
||||||
|
@ -17,7 +17,7 @@ function scanDir(parent, path) {
|
|||||||
let extensions = [ "jpg", "jpeg", "png", "gif", "nef" ],
|
let extensions = [ "jpg", "jpeg", "png", "gif", "nef" ],
|
||||||
re = new RegExp("\.((" + extensions.join(")|(") + "))$", "i"),
|
re = new RegExp("\.((" + extensions.join(")|(") + "))$", "i"),
|
||||||
replacements = {
|
replacements = {
|
||||||
path: path,
|
path: path.slice(picturesPath.length),
|
||||||
parent: parent || null
|
parent: parent || null
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,10 +35,7 @@ function scanDir(parent, path) {
|
|||||||
if (results.length == 0) {
|
if (results.length == 0) {
|
||||||
// console.log("Adding " + path + " under " + parent, replacements);
|
// console.log("Adding " + path + " under " + parent, replacements);
|
||||||
return photoDB.sequelize.query("INSERT INTO albums (path,parentId) VALUES(:path,:parent)", {
|
return photoDB.sequelize.query("INSERT INTO albums (path,parentId) VALUES(:path,:parent)", {
|
||||||
replacements: {
|
replacements: replacements
|
||||||
path: path,
|
|
||||||
parent: parent || null
|
|
||||||
},
|
|
||||||
}).then(function(results) {
|
}).then(function(results) {
|
||||||
return results[1].lastID;
|
return results[1].lastID;
|
||||||
});
|
});
|
||||||
@ -47,7 +44,7 @@ function scanDir(parent, path) {
|
|||||||
}
|
}
|
||||||
}).then(function(parent) {
|
}).then(function(parent) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
console.log("Scanning path " + path + " under parent " + parent);
|
console.log("Scanning " + replacements.path);
|
||||||
|
|
||||||
fs.readdir(path, function(err, files) {
|
fs.readdir(path, function(err, files) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user