commit 30e438aa3276868f66ba9d849aad4e19be637ac0 Author: P.BARRY Date: Sun Jul 12 15:43:59 2015 +0200 first init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa76cfa --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- +node_modules + +# Debug log from npm +npm-debug.log diff --git a/app.js b/app.js new file mode 100644 index 0000000..89299db --- /dev/null +++ b/app.js @@ -0,0 +1,116 @@ +var BOB_mod_path = require('path'); +var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + +var BOB_cfg_config = BOB_initConfig(); + +var routes = require('./routes/index')(BOB_cfg_config); +var users = require('./routes/users'); + +var app = express(); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'ejs'); + +// uncomment after placing your favicon in /public +//app.use(favicon(__dirname + '/public/favicon.ico')); +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +app.use('/', routes); +app.use('/users', users); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handlers + +// development error handler +// will print stacktrace +if (app.get('env') === 'development') { + app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: err + }); + }); +} + +// production error handler +// no stacktraces leaked to user +app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {} + }); +}); + +app.setSocketIo = function(pSocketIo) { + BOB_cfg_config.io = pSocketIo; + + pSocketIo.on('connection', function(socket){ + console.log('New client connected', socket.id); + + socket.on('event', function(data){ + console.log("event"); + }); + socket.on('disconnect', function(){ + console.log("disconnect"); + }); + }); +} + +module.exports = app; + + + +function BOB_initConfig() { + // Config + var cfgConfig = { + background : '#ffffff', + margins : 0.01, + style : 'default', + nbPicture : 4, + cropSize : { + width : 2000, + height : 2500 + }, + pictNames : [], + layout : [], + booths : {}, + paths : { + final : "", + template : "", + prebuilt : "", + original : "" + } + }; + // Init PATH + cfgConfig.paths.final = BOB_mod_path.resolve(__dirname, './public/img/final'); + cfgConfig.paths.final_ld = BOB_mod_path.resolve(__dirname, './public/img/final_ld'); + cfgConfig.paths.prebuilt = BOB_mod_path.resolve(__dirname, './public/img/prebuilt'); + cfgConfig.paths.template = BOB_mod_path.resolve(__dirname, './public/img/template'); + cfgConfig.paths.original = BOB_mod_path.resolve(__dirname, './public/img/original'); + // Init pinctures names + for( var index = 0; index < cfgConfig.nbPicture; index ++ ) { + (function(pId) { + cfgConfig.pictNames.push('pict_'+pId+'.jpg'); + })(index); + } + + return cfgConfig; +} diff --git a/bin/www b/bin/www new file mode 100644 index 0000000..e778db1 --- /dev/null +++ b/bin/www @@ -0,0 +1,93 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var app = require('../app'); +var debug = require('debug')('bobinoscope:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); +var io = require('socket.io')(server); + +app.setSocketIo(io); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..28d18a5 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "bobinoscope", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "async": "~0.9.0", + "body-parser": "~1.12.0", + "cookie-parser": "~1.3.4", + "debug": "~2.1.1", + "ejs": "~2.3.1", + "express": "~4.12.2", + "gamepad": "^1.0.2", + "gm": "~1.17.0", + "gphoto2": "~0.1.7", + "mkdirp": "~0.5.0", + "morgan": "~1.5.1", + "serve-favicon": "~2.2.0", + "socket.io": "~1.3.5" + } +} diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..106c35b --- /dev/null +++ b/routes/index.js @@ -0,0 +1,77 @@ +var BOB_mod_express = require('express'); +var BOB_mod_router = BOB_mod_express.Router(); +var BOB_mod_path = require('path'); +var BOB_mod_mkdirp = require('mkdirp'); + +var BOB_tol_gphotos = null; +var BOB_tol_booth = null; +var BOB_cfg_config = null; + +var BOB_var_boothInProgress = 0; + +var BOB_var_gamepad = require("gamepad"); + +/* GET home page. */ +BOB_mod_router.get('/bobinoscope', function(req, res, next) { + res.render('index', { title: 'Express' }); +}); +/* GET view page. */ +BOB_mod_router.get('/', function(req, res, next) { + res.render('view', {}); +}); + +BOB_mod_router.get('/booth/build/:boothId', function(req, res, next) { + BOB_var_boothInProgress += 1; + BOB_cfg_config.io.emit('boothState'); + BOB_tol_booth.buildBooth(req.params.boothId, function(pErr) { + BOB_cfg_config.io.emit('boothState'); + BOB_var_boothInProgress -= 1; + res.send(pErr); + }); +}) + +BOB_mod_router.get('/booth/list/:from', function(req, res, next) { + BOB_tol_booth.getBoothList(req.params.from, function(pErr, pList) { + res.json({error: pErr, booths: pList, inProgress : BOB_var_boothInProgress}); + }) +}) + +/* GET home page. */ +BOB_mod_router.get('/dslr/takepicture/:boothId/:pictId', function(req, res, next) { + // BOB_cfg_config.io.emit('boothState'); + BOB_tol_gphotos.takePicture(req.params.boothId, req.params.pictId, function(pErr) { + res.json({ error: pErr, data : null}); + }) +}); + +/* GET view page. */ +BOB_mod_router.get('/download/final/hd/:pictId', function(req, res, next) { + res.download('public/img/final/'+req.params.pictId); +}); + + +// Initialize the library +BOB_var_gamepad.init() +// List the state of all currently attached devices +for (var i = 0, l = BOB_var_gamepad.numDevices(); i < l; i++) { + console.log(i, BOB_var_gamepad.deviceAtIndex()); +} +// Create a game loop and poll for events +setInterval(BOB_var_gamepad.processEvents, 16); +// Scan for new gamepads as a slower rate +setInterval(BOB_var_gamepad.detectDevices, 500); +// Listen for button down events on all gamepads +BOB_var_gamepad.on("down", function (pId, pNum) { + BOB_cfg_config.io.emit('boothClick', { + id : pId, + num : pNum + }); +}) + + +module.exports = function(pConfig) { + BOB_cfg_config = pConfig; + BOB_tol_gphotos = require('../tools/tools-gphoto2')(pConfig); + BOB_tol_booth = require('../tools/tools-photobooth')(pConfig); + return BOB_mod_router; +} diff --git a/routes/users.js b/routes/users.js new file mode 100644 index 0000000..623e430 --- /dev/null +++ b/routes/users.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET users listing. */ +router.get('/', function(req, res, next) { + res.send('respond with a resource'); +}); + +module.exports = router; diff --git a/tools/tools-gphoto2.js b/tools/tools-gphoto2.js new file mode 100644 index 0000000..2b6a1c4 --- /dev/null +++ b/tools/tools-gphoto2.js @@ -0,0 +1,63 @@ +var BOB_mod_gphoto2 = require('gphoto2'); +var BOB_var_gphoto = new BOB_mod_gphoto2.GPhoto2(); +var BOB_mod_fs = require('fs'); +var BOB_mod_path = require('path'); +var BOB_mod_mkdirp = require('mkdirp'); + +var BOB_cfg_config = null; + +var BOB_var_camera = null; + +var BOB_module = {}; + + +function BOB_upCameraReference(pCallback) { + // List cameras / assign list item to variable to use below options + if( BOB_var_camera === null ) { + BOB_var_gphoto.list(function (list) { + if (list.length === 0) { + return pCallback(); + } else { + BOB_var_camera = list[0]; + console.log(BOB_var_camera.model); + return pCallback(); + } + }) + } else { + return pCallback(); + } +} + +BOB_module.getConfig = function(pCallback) { + BOB_var_camera.getConfig(pCallback); +} + +BOB_module.takePicture = function(pBoothId, pPictId, pCallback) { + var destPath = BOB_mod_path.join(BOB_cfg_config.paths.original, pBoothId); + var pictPath = BOB_mod_path.join(destPath, BOB_cfg_config.pictNames[pPictId]); + + BOB_upCameraReference(function() { + if( BOB_var_camera === null ) { + return pCallback("No camera found"); + } else { + BOB_var_camera.takePicture({download: true}, function (pErr, pData) { + // If error occurs + if( pErr ) { + return pCallback(pErr) + // Else write file on ddisk + } else { + BOB_mod_mkdirp(destPath, function() { + BOB_mod_fs.writeFile(pictPath, pData, function(pErr) { + return pCallback(pErr, pictPath) + }); + }) + } + }); + } + }) +} + +module.exports = function(pConfig) { + BOB_cfg_config = pConfig; + return BOB_module; +} \ No newline at end of file diff --git a/tools/tools-photobooth.js b/tools/tools-photobooth.js new file mode 100644 index 0000000..e5b50af --- /dev/null +++ b/tools/tools-photobooth.js @@ -0,0 +1,153 @@ +var BOB_mod_gm = require('gm').subClass({ imageMagick: true }); +var BOB_mod_async = require('async'); +var BOB_mod_path = require('path'); +var BOB_mod_fs = require('fs'); +var BOB_mod_mkdirp = require('mkdirp'); + +var BOB_cfg_config = null; + +var BOB_module = {}; + +BOB_module.getBoothList = function(pFrom, pCallback) { + BOB_mod_fs.readdir(BOB_cfg_config.paths.final, function(pErr, pBoothList) { + var boothList = pBoothList.sort(); + + boothList = boothList.splice(boothList.indexOf(pFrom) + 1); + + pCallback(pErr, boothList); + }) +} + +BOB_module.buildBooth = function(pBoothId, pCallback) { + var index = 0; + var funcResizeArray = []; + + BOB_mod_mkdirp.sync(BOB_mod_path.join(BOB_cfg_config.paths.prebuilt, pBoothId)); + + BOB_cfg_config.pictNames.forEach(function(pPictName) { + funcResizeArray.push(function(pCb) { + var srcPict = BOB_mod_path.join(BOB_cfg_config.paths.original, pBoothId, pPictName); + var dstPict = BOB_mod_path.join(BOB_cfg_config.paths.prebuilt, pBoothId, pPictName); + + console.log("Resizing "+dstPict); + + BOB_mod_gm(srcPict) + .autoOrient() + .gravity('Center') + .resize(BOB_cfg_config.cropSize.width, BOB_cfg_config.cropSize.height, "^") + .crop(BOB_cfg_config.cropSize.width, BOB_cfg_config.cropSize.height) + // .modulate(100, 0) + // .contrast(+2) + .write(dstPict, function(pErr) { + pCb() + }) + }) + }) + + BOB_mod_async.parallel( + funcResizeArray, + function(pErr, pData) { + console.log("Building booth") + switch(BOB_cfg_config.style) { + default : + BOB_generateBooth(pBoothId, 'default', pCallback); + } + } + ) +} + +function BOB_generateBooth(pBoothId, pType, pCallback) { + var prebuiltPath = BOB_mod_path.join(BOB_cfg_config.paths.prebuilt, pBoothId); + var finalPict = BOB_mod_path.join(BOB_cfg_config.paths.final, pBoothId+".jpg"); + var finalLdPict = BOB_mod_path.join(BOB_cfg_config.paths.final_ld, pBoothId+".jpg"); + var outPict = BOB_mod_gm(BOB_cfg_config.booths[pType].template); + + var printedDate = new Date(parseInt(pBoothId)); + + outPict.fill(BOB_cfg_config.background); + outPict.drawRectangle(0, 0, BOB_cfg_config.booths[pType].resolution.width, BOB_cfg_config.booths[pType].resolution.height); + + BOB_cfg_config.pictNames.forEach(function(pPictName, pIndex) { + var cmdDraw = 'image Over'; + cmdDraw += ' '+BOB_cfg_config.booths[pType].layout[pIndex].x; + cmdDraw += ','+BOB_cfg_config.booths[pType].layout[pIndex].y; + cmdDraw += ' '+BOB_cfg_config.booths[pType].layout[pIndex].width; + cmdDraw += ','+BOB_cfg_config.booths[pType].layout[pIndex].height; + cmdDraw += ' '+BOB_mod_path.join(prebuiltPath, pPictName); + + console.log(cmdDraw); + outPict.draw(cmdDraw); + }) + + outPict.fill("#000000"); + outPict.pointSize(40); + outPict.draw('text 1900,3850 "'+printedDate.toUTCString()+'"'); + + outPict.write(finalPict, function (pErr) { + BOB_mod_gm(finalPict) + .resize(600, null) + .write(finalLdPict, function(pErr) { + pCallback(pErr); + }) + }); +} + +module.exports = function(pConfig) { + BOB_cfg_config = pConfig; + BOB_updateConfig(); + return BOB_module; +} + + +function BOB_updateConfig() { + BOB_mod_fs.readdir(BOB_cfg_config.paths.template, function(pErr, pFiles) { + if( pErr ) { + console.log(pErr); + } else { + BOB_mod_async.map(pFiles, + function(pFile, pCb) { + // Get name of template + var boothName = pFile.split('.')[0]; + // Init layouts object for the template + BOB_cfg_config.booths[boothName] = { + name : boothName, + template : BOB_mod_path.join(BOB_cfg_config.paths.template, pFile), + resolution : { width : 0, height : 0 }, + layout : [] + }; + // Get reslution of current template + BOB_mod_gm(BOB_cfg_config.booths[boothName].template).size(function(pErr, pValue){ + if( pErr ) { + console.log(pErr); + } else { + BOB_cfg_config.booths[boothName].resolution = pValue; + BOB_generatePictLayout(boothName); + } + pCb(); + }) + }, + function(pErr) { + }) + } + }) +} + +function BOB_generatePictLayout(pBoothName) { + var index = 0; + var marginX = 50; + var marginY = 50; + + var pictWidth = ( BOB_cfg_config.booths[pBoothName].resolution.width - 3 * marginX ) / 2; + var pictHeight = parseInt(pictWidth * BOB_cfg_config.cropSize.height / BOB_cfg_config.cropSize.width); + + BOB_cfg_config.pictNames.forEach(function(pPictName, pIndex) { + BOB_cfg_config.booths[pBoothName].layout.push({ + x : marginX + (marginX + pictWidth) * ( pIndex % 2 ), + y : marginY + (marginY + pictHeight) * parseInt( pIndex / 2 ), + width : pictWidth, + height : pictHeight + }) + }) + + console.log("INIT DONE"); +} diff --git a/tools/tools-test.js b/tools/tools-test.js new file mode 100644 index 0000000..d9be93f --- /dev/null +++ b/tools/tools-test.js @@ -0,0 +1,5 @@ + + +module.exports = function(pConfig) { + console.log("TOOL TEST", pConfig); +} \ No newline at end of file diff --git a/views/error.ejs b/views/error.ejs new file mode 100644 index 0000000..7cf94ed --- /dev/null +++ b/views/error.ejs @@ -0,0 +1,3 @@ +

<%= message %>

+

<%= error.status %>

+
<%= error.stack %>
diff --git a/views/index.ejs b/views/index.ejs new file mode 100644 index 0000000..3623cb8 --- /dev/null +++ b/views/index.ejs @@ -0,0 +1,161 @@ + + + + + + + <%= title %> + + + + + +
+
+
+
+ +
+
+
+

Prends ta face !

+
+ +
+
+ Faites
+ le chat +
+
+ + +
+ +
+
+

BW Bros.

+
+
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/views/index_back.ejs b/views/index_back.ejs new file mode 100644 index 0000000..c8bdc9e --- /dev/null +++ b/views/index_back.ejs @@ -0,0 +1,72 @@ + + + + + + + <%= title %> + + + + + + + + +
+
+
+
+
+

Bobinoscope

+
+
+ +
+

Prends ta face !

+

+ Clic +

+ +
+ +
+
+

BW Bros.

+
+
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/views/view.ejs b/views/view.ejs new file mode 100644 index 0000000..95dd7f7 --- /dev/null +++ b/views/view.ejs @@ -0,0 +1,93 @@ + + + + + + + Bobinoscope + + + + +
+
+ +
+
+ +
+ +
+ + + + + + + + + + \ No newline at end of file