Skip to content

Commit a444d8d

Browse files
committed
Initial commit
0 parents  commit a444d8d

File tree

7 files changed

+543
-0
lines changed

7 files changed

+543
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
cache
2+
node_modules
3+
images.json
4+
images.json

LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) {{{year}}} {{{fullname}}}
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Unsplash It
2+
===========
3+
4+
Beautiful placeholders using images from [unsplash](http://unsplash.com)

config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = exports = {
2+
folder_path: '../unsplash-downloader/photos',
3+
max_height: 5000,
4+
max_width: 5000
5+
}

index.js

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
var gm = require('gm');
2+
var fs = require('fs');
3+
var path = require('path');
4+
var sharp = require('sharp');
5+
var filequeue = require('filequeue');
6+
var imagesize = require('imagesize');
7+
var express = require('express')
8+
var config = require('./config');
9+
var pjson = require('./package.json');
10+
11+
var fq = new filequeue(200);
12+
var app = express();
13+
var highestImageId = 0;
14+
try {
15+
var images = require('./images.json');
16+
} catch (e) {
17+
var images = [];
18+
}
19+
if (images.length != 0) {
20+
highestImageId = images.length;
21+
}
22+
23+
var checkParameters = function (params, callback) {
24+
if (!params.width || !params.height || isNaN(parseInt(params.width)) || isNaN(parseInt(params.height))) {
25+
return callback(true, 400, 'Invalid arguments');
26+
}
27+
if (params.width > config.max_height || params.height > config.max_width) {
28+
return callback(true, 413, 'Specified dimensions too large');
29+
}
30+
callback(false);
31+
}
32+
33+
var displayError = function (res, code, message) {
34+
res.status(code);
35+
res.send({ error: message });
36+
}
37+
38+
var findMatchingImage = function (id, callback) {
39+
var matchingImages = images.filter(function (image) { return image.id == id; });
40+
if (matchingImages.length == 0) {
41+
return callback(true);
42+
}
43+
callback(null, matchingImages[0].filename);
44+
}
45+
46+
var getDestination = function (width, height, filePath, prefix) {
47+
return 'cache/' + prefix + path.basename(filePath, path.extname(filePath)) + '-' + width + 'x' + height + path.extname(filePath);
48+
}
49+
50+
var getShortDestination = function (width, height, filePath, prefix) {
51+
return 'cache/' + prefix + width + '^' + height + path.extname(filePath);
52+
}
53+
54+
var getAndCheckDestination = function (width, height, filePath, prefix, shortName, callback) {
55+
var destination = shortName ? getShortDestination(width, height, filePath, prefix) : getDestination(width, height, filePath, prefix);
56+
fs.exists(destination, function (exists) {
57+
callback(exists, destination);
58+
})
59+
}
60+
61+
var imageResize = function (width, height, filePath, destination, callback) {
62+
try {
63+
sharp(filePath).rotate().resize(width, height).crop().progressive().toFile(destination, function (err) {
64+
callback(err, destination);
65+
});
66+
} catch (e) {
67+
callback(e, null);
68+
}
69+
}
70+
71+
var getProcessedImage = function (width, height, filePath, shortName, callback) {
72+
getAndCheckDestination(width, height, filePath, '', shortName, function (exists, destination) {
73+
if (exists) {
74+
return callback(null, destination);
75+
}
76+
imageResize(width, height, filePath, destination, function (err, destination) {
77+
if (err) {
78+
return callback(err);
79+
}
80+
callback(null, destination);
81+
})
82+
})
83+
}
84+
85+
var getProcessedGrayImage = function (width, height, filePath, shortName, callback) {
86+
getAndCheckDestination(width, height, filePath, 'gray-', shortName, function (exists, destination) {
87+
if (exists) {
88+
return callback(null, destination);
89+
}
90+
imageResize(width, height, filePath, destination, function (err, destination) {
91+
if (err) {
92+
return callback(err);
93+
}
94+
gm(destination).colorspace('GRAY').write(destination, function (err) {
95+
if (err) {
96+
return callback(err);
97+
}
98+
callback(null, destination);
99+
})
100+
})
101+
})
102+
}
103+
104+
var endsWith = function (str, suffix) {
105+
return str.indexOf(suffix, str.length - suffix.length) !== -1;
106+
}
107+
108+
var scanDirectory = function (dir, done) {
109+
var results = [];
110+
fs.readdir(dir, function (err, list) {
111+
if (err) {
112+
return done(err);
113+
}
114+
var pending = list.length;
115+
if (!pending) {
116+
return done (null, results);
117+
}
118+
list.forEach(function (file) {
119+
file = path.resolve(dir, file);
120+
fs.stat(file, function (err, stat) {
121+
if (stat && stat.isFile() && !endsWith(file, '.DS_Store')) {
122+
results.push(file);
123+
}
124+
if (!--pending) done(null, results);
125+
});
126+
});
127+
});
128+
};
129+
130+
var imageScan = function () {
131+
scanDirectory(config.folder_path, function (err, results) {
132+
if (err) throw err;
133+
var filteredResults = results.filter(function (filename) {
134+
return images.filter(function (image) { return image.filename == filename; }) == 0;
135+
});
136+
var left = filteredResults.length;
137+
filteredResults.forEach(function (filename) {
138+
var rs = fq.createReadStream(filename);
139+
imagesize(rs, function (err, result) {
140+
if (err) {
141+
return console.log(err);
142+
}
143+
144+
console.log(filename);
145+
146+
result.filename = filename;
147+
result.id = highestImageId++;
148+
images.push(result);
149+
150+
if (!--left) {
151+
fs.writeFile('images.json', JSON.stringify(images), 'utf8', function (err) {});
152+
}
153+
});
154+
});
155+
});
156+
}
157+
158+
app.get('/', function (req, res, next) {
159+
res.sendFile('public/index.html', { root: '.'} );
160+
})
161+
162+
app.get('/list', function (req, res, next) {
163+
var newImages = [];
164+
for (var i in images) {
165+
var item = images[i];
166+
var image = {
167+
format: item.format,
168+
width: item.width,
169+
height: item.height,
170+
filename: path.basename(item.filename),
171+
id: item.id
172+
}
173+
newImages.push(image);
174+
}
175+
res.jsonp(newImages);
176+
})
177+
178+
app.get('/:width/:height', function (req, res, next) {
179+
checkParameters(req.params, function (err, code, message) {
180+
if (err) {
181+
return displayError(res, code, message);
182+
}
183+
184+
var filePath;
185+
if (req.query.image) {
186+
findMatchingImage(req.query.image, function (err, matchingImage) {
187+
if (err) {
188+
return displayError(res, 400, 'Invalid image id');
189+
}
190+
filePath = matchingImage;
191+
})
192+
} else {
193+
filePath = images[Math.floor(Math.random() * images.length)].filename;
194+
}
195+
196+
getProcessedImage(req.params.width, req.params.height, filePath, (!req.query.image && !req.query.random && req.query.random != ''), function (err, imagePath) {
197+
if (err) {
198+
return displayError(res, 500, 'Something went wrong');
199+
}
200+
res.sendFile(imagePath, { root: '.' });
201+
})
202+
})
203+
})
204+
205+
app.get('/g/:width/:height', function (req, res, next) {
206+
checkParameters(req.params, function (err, code, message) {
207+
if (err) {
208+
return displayError(res, code, message);
209+
}
210+
211+
var filePath;
212+
if (req.query.image) {
213+
findMatchingImage(req.query.image, function (err, matchingImage) {
214+
if (err) {
215+
return displayError(res, 400, 'Invalid image id');
216+
}
217+
filePath = matchingImage;
218+
})
219+
} else {
220+
filePath = images[Math.floor(Math.random() * images.length)].filename;
221+
}
222+
223+
getProcessedGrayImage(req.params.width, req.params.height, filePath, (!req.query.image && !req.query.random && req.query.random != ''), function (err, imagePath) {
224+
if (err) {
225+
return displayError(res, 500, 'Something went wrong');
226+
}
227+
res.sendFile(imagePath, { root: '.' });
228+
})
229+
})
230+
})
231+
232+
app.get('*', function (req, res, next) {
233+
res.status(404);
234+
res.send({ error: 'Resource not found' });
235+
})
236+
237+
imageScan();
238+
app.listen(process.env.PORT || 5000);

package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "unsplash-it",
3+
"version": "0.0.1",
4+
"main": "index.js",
5+
"scripts": {
6+
"test": "echo \"Error: no test specified\" && exit 1",
7+
"start": "node index.js"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "https://github.com/DMarby/unsplash-it.git"
12+
},
13+
"author": "David Marby <david@dmarby.se> http://dmarby.se",
14+
"dependencies": {
15+
"gm": "^1.16.0",
16+
"filequeue": "^0.5.0",
17+
"imagesize": "^1.0.0",
18+
"express": "^4.8.2",
19+
"sharp": "^0.5.2"
20+
}
21+
}

0 commit comments

Comments
 (0)