Compare commits

...

No commits in common. 'master' and 'bob' have entirely different histories.
master ... bob

44 changed files with 1861 additions and 2259 deletions
Split View
  1. 38
      .gitignore
  2. 124
      app.js
  3. 93
      bin/www
  4. 104
      config.js
  5. 38
      index.js
  6. 77
      njs-bobinoscope
  7. 1333
      package-lock.json
  8. 45
      package.json
  9. 6
      pm2.json
  10. 0
      public/css/bootstrap.min.css
  11. 0
      public/css/cover.css
  12. 0
      public/css/style.css
  13. 0
      public/css/view.css
  14. 0
      public/img/common/294.GIF
  15. 0
      public/img/footer/cloclo.jpg
  16. 0
      public/img/footer/m-cloclo.jpg
  17. BIN
      public/img/footer/m-cloclo.xcf
  18. 0
      public/img/footer/m-clotilde60.jpg
  19. BIN
      public/img/footer/m-clotilde60.xcf
  20. 0
      public/img/template/default.jpg
  21. 0
      public/js/bootstrap.min.js
  22. 0
      public/js/jquery-1.11.2.min.js
  23. 0
      public/js/socket.io-1.3.4.js
  24. 206
      routes/index.js
  25. 9
      routes/users.js
  26. 131
      server/booth.js
  27. 693
      server/dnsmasq.conf
  28. 11
      server/hosts
  29. 63
      server/index.js
  30. 8
      server/public/js/socket.io.js
  31. 1
      server/public/js/socket.io.js.map
  32. BIN
      server/public/rsc/footer/m-30ansv2.jpg
  33. BIN
      server/public/waiting.gif
  34. BIN
      server/public/waiting2.gif
  35. 251
      server/views/scope.ejs
  36. 62
      server/views/test.ejs
  37. 258
      tools/tools-gphoto2.js
  38. 192
      tools/tools-photobooth.js
  39. 5
      tools/tools-test.js
  40. 3
      views/error.ejs
  41. 19
      views/fixdate.ejs
  42. 248
      views/index.ejs
  43. 72
      views/index_back.ejs
  44. 30
      views/view.ejs

38
.gitignore

@ -1,2 +1,36 @@
*node_modules*
output/
# 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
public/img/final_ld/*
public/img/final/*
public/img/final_ld/*
public/img/original/*
public/img/prebuilt/*
# Debug log from npm
npm-debug.log

124
app.js

@ -0,0 +1,124 @@
var BOB_mod_path = require('path');
var BOB_mod_mkdirp = require('mkdirp');
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.005,
style : 'default',
nbPicture : 4,
cropSize : {
width : 900,
height : 1200
},
pictNames : [],
layout : [],
booths : {},
paths : {
final : "",
template : "",
prebuilt : "",
original : ""
},
footer : BOB_mod_path.resolve(__dirname, './public/img/footer', 'm-default.jpg')
};
// 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');
BOB_mod_mkdirp.sync(cfgConfig.paths.final);
BOB_mod_mkdirp.sync(cfgConfig.paths.final_ld);
BOB_mod_mkdirp.sync(cfgConfig.paths.prebuilt);
BOB_mod_mkdirp.sync(cfgConfig.paths.original);
// Init pinctures names
for( var index = 0; index < cfgConfig.nbPicture; index ++ ) {
(function(pId) {
cfgConfig.pictNames.push('pict_'+pId+'.jpg');
})(index);
}
return cfgConfig;
}

93
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);
}

104
config.js

@ -1,104 +0,0 @@
const os = require('os')
const path = require('path')
const mkdirp = require('mkdirp')
const fs = require('fs-extra')
const config = {
background: '#ffffff',
margins: 0.005,
style: 'default',
nbPicture: 4,
cropSize: {
width: 900,
height: 1200
},
pictNames: [],
layout: [],
booths: {},
paths: {
final: '',
template: '',
prebuilt: '',
original: '',
toprint: ''
},
output: path.resolve('/media/share/'),
footer: 'm-30ansv2.jpg'
}
config.server = {
ipAddress: getIpAddress(),
port: 3111
}
config.killZone = {
top: 2,
bottom: 2,
left: 25,
right: 25
}
config.output = path.join(config.output, 'bobinoscope')
try {
fs.ensureDirSync(config.output)
console.log('[!] Bobinogrammes will be available in: ' + config.output)
} catch (e) {
console.log('[!] Failed to create folders: ' + config.output)
try {
config.output = process.cwd()
config.output = path.join(config.output, 'bobinoscope')
console.log('[!] Bobinogrammes will be available in: ' + config.output)
} catch (e) {
console.log('[!] Failed to create folders: ' + config.output)
process.exit(1)
}
}
// Init PATH
config.paths.template = path.resolve(__dirname, './server/public/rsc/template')
config.footer = path.resolve(__dirname, './server/public/rsc/footer', config.footer)
config.paths.final = path.resolve(config.output, './final')
config.paths.final_ld = path.resolve(config.output, './final_ld')
config.paths.prebuilt = path.resolve(config.output, './cache')
config.paths.original = path.resolve(config.output, './original')
config.paths.toprint = path.resolve(config.output, './toprint')
mkdirp.sync(config.paths.final)
mkdirp.sync(config.paths.final_ld)
mkdirp.sync(config.paths.prebuilt)
mkdirp.sync(config.paths.original)
mkdirp.sync(config.paths.toprint)
// Init pinctures names
for (let index = 0; index < config.nbPicture; index++) {
config.pictNames.push('pict_' + index + '.jpg')
}
module.exports = config
function getIpAddress () {
let ipAddress = 'localhost'
// Get local server ip address
let ifaces = os.networkInterfaces()
Object.keys(ifaces).forEach(function (ifname) {
let alias = 0
ifaces[ifname].forEach(function (iface) {
// skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
if (iface.family !== 'IPv4' || iface.internal !== false) return
if (/wlan/i.test(ifname)) ipAddress = iface.address
if (alias >= 1) {
// this single interface has multiple ipv4 addresses
// console.log(`[-] network: ${ifname} : ${alias} - ${iface.address}`)
} else {
// this interface has only one ipv4 adress
// console.log(`[-] network: ${ifname} - ${iface.address}`)
// console.log(ifname, iface.address)
}
})
})
return ipAddress
}

38
index.js

@ -1,38 +0,0 @@
#!/usr/bin/env node
const gphoto2 = require('./tools/tools-gphoto2')
const server = require('./server')
const args = require('args-parser')(process.argv)
const PKG = require('./package')
if (args.noinit) {
let app = server(args)
} else {
gphoto2.init(function () {
console.log('[>] Checking list done with success')
let app = server(args)
})
}
// killall gvfs-gphoto2-volume-monitor
// // 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(i));
// }
// // 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
// });
// })
// bobinoscope admin: 63748543

77
njs-bobinoscope

@ -0,0 +1,77 @@
#!/bin/bash
### BEGIN INIT INFO
# Provides: /home/cocoon/workspace/bobinoscope/bin/www
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: forever running /home/cocoon/workspace/bobinoscope/bin/www
# Description: /home/cocoon/workspace/bobinoscope/bin/www
### END INIT INFO
#
# initd a node app
# Based on a script posted by https://gist.github.com/jinze at https://gist.github.com/3748766
#
# Source function library.
. /lib/lsb/init-functions
pidFile="/var/run/njs-bobinoscope.pid"
logFile="/var/log/njs-bobinoscope"
command="/usr/local/bin/node"
nodeApp="/home/cocoon/workspace/bobinoscope/bin/www"
foreverApp="/usr/local/bin/forever"
start() {
echo "Starting $nodeApp"
# Notice that we change the PATH because on reboot
# the PATH does not include the path to node.
# Launching forever with a full path
# does not work unless we set the PATH.
PATH=/usr/local/bin:$PATH
export NODE_ENV=production
#PORT=80
$foreverApp start --pidFile $pidFile -l $logFile -a -d -c "$command" $nodeApp
RETVAL=$?
}
restart() {
echo -n "Restarting $nodeApp"
$foreverApp restart $nodeApp
RETVAL=$?
}
stop() {
echo -n "Shutting down $nodeApp"
$foreverApp stop $nodeApp
RETVAL=$?
}
status() {
echo -n "Status $nodeApp"
$foreverApp list
RETVAL=$?
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
restart
;;
*)
echo "Usage: {start|stop|status|restart}"
exit 1
;;
esac
exit $RETVAL

1333
package-lock.json
File diff suppressed because it is too large
View File

45
package.json

@ -1,33 +1,24 @@
{
"name": "bobinoscope-v2",
"version": "1.0.0",
"description": "Code for bobinoscope",
"main": "index.js",
"name": "bobinoscope",
"version": "0.0.0",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"start": "node ./bin/www"
},
"author": "P.BARRY",
"license": "ISC",
"dependencies": {
"args-parser": "^1.1.0",
"async": "^2.6.1",
"body-parser": "^1.18.3",
"colors": "^1.3.1",
"cookie-parser": "^1.4.3",
"ejs": "^2.6.1",
"express": "^4.16.3",
"fs-extra": "^7.0.0",
"gamepad": "^1.6.0",
"gm": "^1.23.1",
"mkdirp": "^0.5.1",
"morgan": "^1.9.0",
"opn": "^5.3.0",
"socket.io": "^2.1.1",
"split-lines": "^2.0.0"
},
"nodemonConfig": {
"ignore": [
"webapp/*"
]
"async": "~0.9.0",
"body-parser": "~1.12.0",
"cookie-parser": "~1.3.4",
"debug": "~2.1.1",
"ejs": "~2.3.1",
"es6-shim": "^0.35.0",
"express": "~4.12.2",
"gamepad": "^1.0.2",
"gm": "~1.17.0",
"mkdirp": "~0.5.0",
"morgan": "~1.5.1",
"odroid-gpio": "0.0.2",
"serve-favicon": "~2.2.0",
"socket.io": "~1.3.5"
}
}

6
pm2.json

@ -1,9 +1,9 @@
{
"apps" : [{
"name" : "Bobinoscope V2",
"script" : "./index.js",
"name" : "Bobinoscope",
"script" : "./bin/www",
"args" : [],
"cwd" : "/home/odroid/workspace/bobinoscope-v2",
"cwd" : "/home/odroid/workspace/bobinoscope",
"env": {
"NODE_ENV": "production",
}

server/public/css/bootstrap.min.css → public/css/bootstrap.min.css

server/public/css/cover.css → public/css/cover.css

server/public/css/style.css → public/css/style.css

server/public/css/view.css → public/css/view.css

server/public/rsc/common/294.GIF → public/img/common/294.GIF

server/public/rsc/footer/cloclo.jpg → public/img/footer/cloclo.jpg

server/public/rsc/footer/m-cloclo.jpg → public/img/footer/m-cloclo.jpg

BIN
public/img/footer/m-cloclo.xcf

server/public/rsc/footer/m-clotilde60.jpg → public/img/footer/m-clotilde60.jpg

BIN
public/img/footer/m-clotilde60.xcf

server/public/rsc/template/default.jpg → public/img/template/default.jpg

server/public/js/bootstrap.min.js → public/js/bootstrap.min.js

server/public/js/jquery-1.11.2.min.js → public/js/jquery-1.11.2.min.js

server/public/js/socket.io-1.3.4.js → public/js/socket.io-1.3.4.js

206
routes/index.js

@ -0,0 +1,206 @@
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_mod_os = require('os');
var BOB_mod_fs = require('fs');
// var BOB_mod_fsExtra = require('fs-extra')
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");
var BOB_var_ipAddress = '127.0.0.1';
var BOB_var_path = "/home/odroid/workspace/bobinoscope";
var BOB_var_killZone = {
top : 2,
bottom : 2,
left : 25,
right : 25
}
BOB_mod_mkdirp.sync(BOB_var_path+"/public/printer/todo");
BOB_mod_mkdirp.sync(BOB_var_path+"/public/printer/done");
/* GET home page. */
BOB_mod_router.get('/bobinoscope', function(req, res, next) {
res.render('index', {
serverIpAddress: BOB_var_ipAddress,
killZone : BOB_var_killZone
});
});
/* GET view page. */
BOB_mod_router.get('/', function(req, res, next) {
res.render('view', {serverIpAddress: BOB_var_ipAddress});
});
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.json({ error: pErr, data : null, boothId: req.params.boothId});
});
})
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(BOB_var_path+'/public/img/final/'+req.params.pictId);
});
/* GET view page. */
BOB_mod_router.get('/print/bobinogramme/:pictId', function(req, res, next) {
var srcPath = BOB_mod_path.join(BOB_var_path, '/public/img/final/'+req.params.pictId);
var dstPath = BOB_mod_path.join(BOB_var_path, '/public/printer/todo/'+req.params.pictId);
copyFile(srcPath, dstPath, function(pErr) {
res.json({error: pErr})
})
});
BOB_mod_router.get('/print/list/todo', function(req, res, next) {
var todoPath = BOB_mod_path.join(BOB_var_path, '/public/printer/todo');
BOB_mod_fs.readdir(todoPath, function(pErr, pList) {
res.json({error: pErr, todos: pList})
})
})
BOB_mod_router.get('/fixdate', function (req, res, next) {
res.render('fixdate', {serverIpAddress: BOB_var_ipAddress});
})
BOB_mod_router.get('/fixdate/:timestamp', function(req, res, next) {
var spawn = require('child_process').spawn;
var date = spawn('date', ['-s', '@'+(req.params.timestamp/1000)]);
var error = '';
date.stdout.on('data', function(data) {console.log(data.toString())});
date.stderr.on('data', function(data) {error += data; console.log(data.toString())});
date.on('close', function(code) {
res.json({error : code !== 0 ? error : ''})
})
})
function copyFile(source, target, cb) {
var cbCalled = false;
var rd = BOB_mod_fs.createReadStream(source);
rd.on("error", function(err) {
done(err);
});
var wr = BOB_mod_fs.createWriteStream(target);
wr.on("error", function(err) {
done(err);
});
wr.on("close", function(ex) {
done();
});
rd.pipe(wr);
function done(err) {
if (!cbCalled) {
cb(err);
cbCalled = true;
}
}
}
var gpio = require("odroid-gpio");
var previous = 1;
gpio.open(7, "input pullup", function(err) { // Open pin 16 for output
console.log(err)
setInterval(function() {
gpio.read(7, function(err, value) {
if(err) console.log(err);
// console.log(value); // The current state of the pini
if (value === 0 && value !== previous) {
console.log('click')
BOB_cfg_config.io.emit('boothClick', {
id : 0,
num : 0
});
}
previous = value;
});
}, 200)
});
// 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(i));
//}
// 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
// });
//})
// Get local server ip address
var ifaces = BOB_mod_os.networkInterfaces();
Object.keys(ifaces).forEach(function (ifname) {
var alias = 0;
ifaces[ifname].forEach(function (iface) {
if ('IPv4' !== iface.family || iface.internal !== false) {
// skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
return;
}
BOB_var_ipAddress = iface.address;
if (alias >= 1) {
// this single interface has multiple ipv4 addresses
console.log(ifname + ':' + alias, iface.address);
} else {
// this interface has only one ipv4 adress
console.log(ifname, iface.address);
}
});
});
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;
}

9
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;

131
server/booth.js

@ -1,131 +0,0 @@
const express = require('express')
const router = express.Router()
const fs = require('fs-extra')
const gm = require('gm').subClass({ imageMagick: true })
const path = require('path')
const gamepad = require('gamepad')
require('colors')
const gphoto2 = require('../tools/tools-gphoto2')
const booth = require('../tools/tools-photobooth')
const CFG = require('../config')
const CTX = {
__io: null,
io: function () {
return CTX.__io
},
inProgress: 0,
testPath: path.join(CFG.output, 'bobinoscope-test.jpg')
}
booth.init()
setGamepad()
// router.use(function (req, res, next) {
// console.log(req.url)
// next()
// })
// ////////////////////////////////////////////////////////////////////////////
// DSLR
router.get('/dslr/takepicture/:boothId/:pictId', function (req, res, next) {
// return res.json({})
gphoto2.takeOnePicture(req.params.boothId, req.params.pictId, function (pErr) {
let error = typeof pErr === 'string' ? pErr : JSON.stringify(pErr)
if (pErr) console.error(pErr)
res.json({ error: error, data: null })
})
})
router.get('/build/:boothId', function (req, res, next) {
console.log('Building')
// res.json({ok: 1})
CTX.inProgress += 1
CTX.io().emit('boothState')
booth.buildBooth(req.params.boothId, function (pErr) {
CTX.io().emit('boothState')
CTX.inProgress -= 1
res.json({ error: pErr, data: null, boothId: req.params.boothId })
})
})
router.use('/build/result/low', express.static(CFG.paths.final_ld))
router.use('/build/result', express.static(CFG.paths.final))
// ////////////////////////////////////////////////////////////////////////////
// PRINTER
router.get('/list/:from', function (req, res, next) {
booth.getBoothList(req.params.from, function (pErr, pList) {
res.json({error: pErr, booths: pList, inProgress: CTX.inProgress})
})
})
router.get('/toprint', function (req, res, next) {
var todoPath = path.join(CFG.paths.toprint)
fs.readdir(todoPath, function (pErr, pList) {
res.json({error: pErr, todos: pList})
})
})
router.get('/print/:pictId', function (req, res, next) {
let srcPath = path.join(CFG.paths.final, req.params.pictId)
let dstPath = path.join(CFG.paths.toprint, req.params.pictId)
fs.copy(srcPath, dstPath, function (pErr) {
res.json({error: pErr})
})
})
// ////////////////////////////////////////////////////////////////////////////
// TEST
router.get('/test/takepicture', function (req, res, next) {
let tmpPict = path.join(CFG.output, 'bobinoscope-test.tmp.jpg')
gphoto2.__takeOnePicture(tmpPict, function (pErr) {
if (pErr) return res.status(500).send(pErr)
gm(tmpPict)
.autoOrient()
.gravity('Center')
.write(CTX.testPath, function (pErr) {
if (pErr) console.log('Failed to resize picture', pErr)
res.json({ok: 1})
})
})
})
router.get('/test/takepicture/result', function (req, res, next) {
res.sendFile(path.join(CFG.output, 'bobinoscope-test.jpg'))
})
router.setSocketIo = function (pIo) {
CTX.__io = pIo
}
function setGamepad () {
// Initialize the library
gamepad.init()
// List the state of all currently attached devices
// for (var i = 0, l = gamepad.numDevices(); i < l; i++) {
// console.log(i, gamepad.deviceAtIndex(i))
// }
if (!gamepad.numDevices()) {
console.log('[!] No gamepad found'.yellow)
} else {
console.log(`[!] ${gamepad.numDevices()} gamepad(s) found`.bgBlue)
// Create a game loop and poll for events
setInterval(gamepad.processEvents, 16)
// Scan for new gamepads as a slower rate
setInterval(gamepad.detectDevices, 500)
// Listen for button down events on all gamepads
gamepad.on('down', function (pId, pNum) {
console.log(`[!] gamepad action received | ${pId}:${pNum}`.bgBlue)
CTX.io().emit('boothClick', {
id: pId,
num: pNum
})
})
}
}
module.exports = router

693
server/dnsmasq.conf

@ -0,0 +1,693 @@
# Configuration file for dnsmasq.
#
# Format is one option per line, legal options are the same
# as the long options legal on the command line. See
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
# Listen on this specific port instead of the standard DNS port
# (53). Setting this to zero completely disables DNS function,
# leaving only DHCP and/or TFTP.
#port=5353
# The following two options make you a better netizen, since they
# tell dnsmasq to filter out queries which the public DNS cannot
# answer, and which load the servers (especially the root servers)
# unnecessarily. If you have a dial-on-demand link they also stop
# these requests from bringing up the link unnecessarily.
# Never forward plain names (without a dot or domain part)
#domain-needed
# Never forward addresses in the non-routed address spaces.
#bogus-priv
# Uncomment these to enable DNSSEC validation and caching:
# (Requires dnsmasq to be built with DNSSEC option.)
#conf-file=%%PREFIX%%/share/dnsmasq/trust-anchors.conf
#dnssec
# Replies which are not DNSSEC signed may be legitimate, because the domain
# is unsigned, or may be forgeries. Setting this option tells dnsmasq to
# check that an unsigned reply is OK, by finding a secure proof that a DS
# record somewhere between the root and the domain does not exist.
# The cost of setting this is that even queries in unsigned domains will need
# one or more extra DNS queries to verify.
#dnssec-check-unsigned
# Uncomment this to filter useless windows-originated DNS requests
# which can trigger dial-on-demand links needlessly.
# Note that (amongst other things) this blocks all SRV requests,
# so don't use it if you use eg Kerberos, SIP, XMMP or Google-talk.
# This option only affects forwarding, SRV records originating for
# dnsmasq (via srv-host= lines) are not suppressed by it.
#filterwin2k
# Change this line if you want dns to get its upstream servers from
# somewhere other that /etc/resolv.conf
#resolv-file=
# By default, dnsmasq will send queries to any of the upstream
# servers it knows about and tries to favour servers to are known
# to be up. Uncommenting this forces dnsmasq to try each query
# with each server strictly in the order they appear in
# /etc/resolv.conf
#strict-order
# If you don't want dnsmasq to read /etc/resolv.conf or any other
# file, getting its servers from this file instead (see below), then
# uncomment this.
#no-resolv
# If you don't want dnsmasq to poll /etc/resolv.conf or other resolv
# files for changes and re-read them then uncomment this.
#no-poll
# Add other name servers here, with domain specs if they are for
# non-public domains.
#server=/localnet/192.168.0.1
# Example of routing PTR queries to nameservers: this will send all
# address->name queries for 192.168.3/24 to nameserver 10.1.2.3
#server=/3.168.192.in-addr.arpa/10.1.2.3
# Add local-only domains here, queries in these domains are answered
# from /etc/hosts or DHCP only.
#local=/localnet/
# Add domains which you want to force to an IP address here.
# The example below send any host in double-click.net to a local
# web-server.
address=/double-click.net/127.0.0.1
# --address (and --server) work with IPv6 addresses too.
#address=/www.thekelleys.org.uk/fe80::20d:60ff:fe36:f83
# Add the IPs of all queries to yahoo.com, google.com, and their
# subdomains to the vpn and search ipsets:
#ipset=/yahoo.com/google.com/vpn,search
# You can control how dnsmasq talks to a server: this forces
# queries to 10.1.2.3 to be routed via eth1
# server=10.1.2.3@eth1
# and this sets the source (ie local) address used to talk to
# 10.1.2.3 to 192.168.1.1 port 55 (there must be a interface with that
# IP on the machine, obviously).
# server=10.1.2.3@192.168.1.1#55
# If you want dnsmasq to change uid and gid to something other
# than the default, edit the following lines.
#user=
#group=
# If you want dnsmasq to listen for DHCP and DNS requests only on
# specified interfaces (and the loopback) give the name of the
# interface (eg eth0) here.
# Repeat the line for more than one interface.
#interface=
# Or you can specify which interface _not_ to listen on
#except-interface=
# Or which to listen on by address (remember to include 127.0.0.1 if
# you use this.)
#listen-address=
# If you want dnsmasq to provide only DNS service on an interface,
# configure it as shown above, and then use the following line to
# disable DHCP and TFTP on it.
#no-dhcp-interface=
# On systems which support it, dnsmasq binds the wildcard address,
# even when it is listening on only some interfaces. It then discards
# requests that it shouldn't reply to. This has the advantage of
# working even when interfaces come and go and change address. If you
# want dnsmasq to really bind only the interfaces it is listening on,
# uncomment this option. About the only time you may need this is when
# running another nameserver on the same machine.
#bind-interfaces
# If you don't want dnsmasq to read /etc/hosts, uncomment the
# following line.
#no-hosts
# or if you want it to read another file, as well as /etc/hosts, use
# this.
#addn-hosts=/etc/banner_add_hosts
# Set this (and domain: see below) if you want to have a domain
# automatically added to simple names in a hosts-file.
#expand-hosts
# Set the domain for dnsmasq. this is optional, but if it is set, it
# does the following things.
# 1) Allows DHCP hosts to have fully qualified domain names, as long
# as the domain part matches this setting.
# 2) Sets the "domain" DHCP option thereby potentially setting the
# domain of all systems configured by DHCP
# 3) Provides the domain part for "expand-hosts"
domain=bobine.lol
# Set a different domain for a particular subnet
#domain=wireless.thekelleys.org.uk,192.168.2.0/24
# Same idea, but range rather then subnet
#domain=reserved.thekelleys.org.uk,192.68.3.100,192.168.3.200
# Uncomment this to enable the integrated DHCP server, you need
# to supply the range of addresses available for lease and optionally
# a lease time. If you have more than one network, you will need to
# repeat this for each network on which you want to supply DHCP
# service.
#dhcp-range=192.168.0.50,192.168.0.150,12h
# This is an example of a DHCP range where the netmask is given. This
# is needed for networks we reach the dnsmasq DHCP server via a relay
# agent. If you don't know what a DHCP relay agent is, you probably
# don't need to worry about this.
#dhcp-range=192.168.0.50,192.168.0.150,255.255.255.0,12h
# This is an example of a DHCP range which sets a tag, so that
# some DHCP options may be set only for this network.
#dhcp-range=set:red,192.168.0.50,192.168.0.150
# Use this DHCP range only when the tag "green" is set.
#dhcp-range=tag:green,192.168.0.50,192.168.0.150,12h
# Specify a subnet which can't be used for dynamic address allocation,
# is available for hosts with matching --dhcp-host lines. Note that
# dhcp-host declarations will be ignored unless there is a dhcp-range
# of some type for the subnet in question.
# In this case the netmask is implied (it comes from the network
# configuration on the machine running dnsmasq) it is possible to give
# an explicit netmask instead.
#dhcp-range=192.168.0.0,static
# Enable DHCPv6. Note that the prefix-length does not need to be specified
# and defaults to 64 if missing/
#dhcp-range=1234::2, 1234::500, 64, 12h
# Do Router Advertisements, BUT NOT DHCP for this subnet.
#dhcp-range=1234::, ra-only
# Do Router Advertisements, BUT NOT DHCP for this subnet, also try and
# add names to the DNS for the IPv6 address of SLAAC-configured dual-stack
# hosts. Use the DHCPv4 lease to derive the name, network segment and
# MAC address and assume that the host will also have an
# IPv6 address calculated using the SLAAC alogrithm.
#dhcp-range=1234::, ra-names
# Do Router Advertisements, BUT NOT DHCP for this subnet.
# Set the lifetime to 46 hours. (Note: minimum lifetime is 2 hours.)
#dhcp-range=1234::, ra-only, 48h
# Do DHCP and Router Advertisements for this subnet. Set the A bit in the RA
# so that clients can use SLAAC addresses as well as DHCP ones.
#dhcp-range=1234::2, 1234::500, slaac
# Do Router Advertisements and stateless DHCP for this subnet. Clients will
# not get addresses from DHCP, but they will get other configuration information.
# They will use SLAAC for addresses.
#dhcp-range=1234::, ra-stateless
# Do stateless DHCP, SLAAC, and generate DNS names for SLAAC addresses
# from DHCPv4 leases.
#dhcp-range=1234::, ra-stateless, ra-names
# Do router advertisements for all subnets where we're doing DHCPv6
# Unless overriden by ra-stateless, ra-names, et al, the router
# advertisements will have the M and O bits set, so that the clients
# get addresses and configuration from DHCPv6, and the A bit reset, so the
# clients don't use SLAAC addresses.
#enable-ra
# Supply parameters for specified hosts using DHCP. There are lots
# of valid alternatives, so we will give examples of each. Note that
# IP addresses DO NOT have to be in the range given above, they just
# need to be on the same network. The order of the parameters in these
# do not matter, it's permissible to give name, address and MAC in any
# order.
# Always allocate the host with Ethernet address 11:22:33:44:55:66
# The IP address 192.168.0.60
#dhcp-host=11:22:33:44:55:66,192.168.0.60
# Always set the name of the host with hardware address
# 11:22:33:44:55:66 to be "fred"
#dhcp-host=11:22:33:44:55:66,fred
# Always give the host with Ethernet address 11:22:33:44:55:66
# the name fred and IP address 192.168.0.60 and lease time 45 minutes
#dhcp-host=11:22:33:44:55:66,fred,192.168.0.60,45m
# Give a host with Ethernet address 11:22:33:44:55:66 or
# 12:34:56:78:90:12 the IP address 192.168.0.60. Dnsmasq will assume
# that these two Ethernet interfaces will never be in use at the same
# time, and give the IP address to the second, even if it is already
# in use by the first. Useful for laptops with wired and wireless
# addresses.
#dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.60
# Give the machine which says its name is "bert" IP address
# 192.168.0.70 and an infinite lease
#dhcp-host=bert,192.168.0.70,infinite
# Always give the host with client identifier 01:02:02:04
# the IP address 192.168.0.60
#dhcp-host=id:01:02:02:04,192.168.0.60
# Always give the host with client identifier "marjorie"
# the IP address 192.168.0.60
#dhcp-host=id:marjorie,192.168.0.60
# Enable the address given for "judge" in /etc/hosts
# to be given to a machine presenting the name "judge" when
# it asks for a DHCP lease.
#dhcp-host=judge
# Never offer DHCP service to a machine whose Ethernet
# address is 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,ignore
# Ignore any client-id presented by the machine with Ethernet
# address 11:22:33:44:55:66. This is useful to prevent a machine
# being treated differently when running under different OS's or
# between PXE boot and OS boot.
#dhcp-host=11:22:33:44:55:66,id:*
# Send extra options which are tagged as "red" to
# the machine with Ethernet address 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,set:red
# Send extra options which are tagged as "red" to
# any machine with Ethernet address starting 11:22:33:
#dhcp-host=11:22:33:*:*:*,set:red
# Give a fixed IPv6 address and name to client with
# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
# Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
# Note also the they [] around the IPv6 address are obilgatory.
#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5]
# Ignore any clients which are not specified in dhcp-host lines
# or /etc/ethers. Equivalent to ISC "deny unknown-clients".
# This relies on the special "known" tag which is set when
# a host is matched.
#dhcp-ignore=tag:!known
# Send extra options which are tagged as "red" to any machine whose
# DHCP vendorclass string includes the substring "Linux"
#dhcp-vendorclass=set:red,Linux
# Send extra options which are tagged as "red" to any machine one
# of whose DHCP userclass strings includes the substring "accounts"
#dhcp-userclass=set:red,accounts
# Send extra options which are tagged as "red" to any machine whose
# MAC address matches the pattern.
#dhcp-mac=set:red,00:60:8C:*:*:*
# If this line is uncommented, dnsmasq will read /etc/ethers and act
# on the ethernet-address/IP pairs found there just as if they had
# been given as --dhcp-host options. Useful if you keep
# MAC-address/host mappings there for other purposes.
#read-ethers
# Send options to hosts which ask for a DHCP lease.
# See RFC 2132 for details of available options.
# Common options can be given to dnsmasq by name:
# run "dnsmasq --help dhcp" to get a list.
# Note that all the common settings, such as netmask and
# broadcast address, DNS server and default route, are given
# sane defaults by dnsmasq. You very likely will not need
# any dhcp-options. If you use Windows clients and Samba, there
# are some options which are recommended, they are detailed at the
# end of this section.
# Override the default route supplied by dnsmasq, which assumes the
# router is the same machine as the one running dnsmasq.
#dhcp-option=3,1.2.3.4
# Do the same thing, but using the option name
#dhcp-option=option:router,1.2.3.4
# Override the default route supplied by dnsmasq and send no default
# route at all. Note that this only works for the options sent by
# default (1, 3, 6, 12, 28) the same line will send a zero-length option
# for all other option numbers.
#dhcp-option=3
# Set the NTP time server addresses to 192.168.0.4 and 10.10.0.5
#dhcp-option=option:ntp-server,192.168.0.4,10.10.0.5
# Send DHCPv6 option. Note [] around IPv6 addresses.
#dhcp-option=option6:dns-server,[1234::77],[1234::88]
# Send DHCPv6 option for namservers as the machine running
# dnsmasq and another.
#dhcp-option=option6:dns-server,[::],[1234::88]
# Ask client to poll for option changes every six hours. (RFC4242)
#dhcp-option=option6:information-refresh-time,6h
# Set the NTP time server address to be the same machine as
# is running dnsmasq
#dhcp-option=42,0.0.0.0
# Set the NIS domain name to "welly"
#dhcp-option=40,welly
# Set the default time-to-live to 50
#dhcp-option=23,50
# Set the "all subnets are local" flag
#dhcp-option=27,1
# Send the etherboot magic flag and then etherboot options (a string).
#dhcp-option=128,e4:45:74:68:00:00
#dhcp-option=129,NIC=eepro100
# Specify an option which will only be sent to the "red" network
# (see dhcp-range for the declaration of the "red" network)
# Note that the tag: part must precede the option: part.
#dhcp-option = tag:red, option:ntp-server, 192.168.1.1
# The following DHCP options set up dnsmasq in the same way as is specified
# for the ISC dhcpcd in
# http://www.samba.org/samba/ftp/docs/textdocs/DHCP-Server-Configuration.txt
# adapted for a typical dnsmasq installation where the host running
# dnsmasq is also the host running samba.
# you may want to uncomment some or all of them if you use
# Windows clients and Samba.
#dhcp-option=19,0 # option ip-forwarding off
#dhcp-option=44,0.0.0.0 # set netbios-over-TCP/IP nameserver(s) aka WINS server(s)
#dhcp-option=45,0.0.0.0 # netbios datagram distribution server
#dhcp-option=46,8 # netbios node type
# Send an empty WPAD option. This may be REQUIRED to get windows 7 to behave.
#dhcp-option=252,"\n"
# Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client
# probably doesn't support this......
#dhcp-option=option:domain-search,eng.apple.com,marketing.apple.com
# Send RFC-3442 classless static routes (note the netmask encoding)
#dhcp-option=121,192.168.1.0/24,1.2.3.4,10.0.0.0/8,5.6.7.8
# Send vendor-class specific options encapsulated in DHCP option 43.
# The meaning of the options is defined by the vendor-class so
# options are sent only when the client supplied vendor class
# matches the class given here. (A substring match is OK, so "MSFT"
# matches "MSFT" and "MSFT 5.0"). This example sets the
# mtftp address to 0.0.0.0 for PXEClients.
#dhcp-option=vendor:PXEClient,1,0.0.0.0
# Send microsoft-specific option to tell windows to release the DHCP lease
# when it shuts down. Note the "i" flag, to tell dnsmasq to send the
# value as a four-byte integer - that's what microsoft wants. See
# http://technet2.microsoft.com/WindowsServer/en/library/a70f1bb7-d2d4-49f0-96d6-4b7414ecfaae1033.mspx?mfr=true
#dhcp-option=vendor:MSFT,2,1i
# Send the Encapsulated-vendor-class ID needed by some configurations of
# Etherboot to allow is to recognise the DHCP server.
#dhcp-option=vendor:Etherboot,60,"Etherboot"
# Send options to PXELinux. Note that we need to send the options even
# though they don't appear in the parameter request list, so we need
# to use dhcp-option-force here.
# See http://syslinux.zytor.com/pxe.php#special for details.
# Magic number - needed before anything else is recognised
#dhcp-option-force=208,f1:00:74:7e
# Configuration file name
#dhcp-option-force=209,configs/common
# Path prefix
#dhcp-option-force=210,/tftpboot/pxelinux/files/
# Reboot time. (Note 'i' to send 32-bit value)
#dhcp-option-force=211,30i
# Set the boot filename for netboot/PXE. You will only need
# this is you want to boot machines over the network and you will need
# a TFTP server; either dnsmasq's built in TFTP server or an
# external one. (See below for how to enable the TFTP server.)
#dhcp-boot=pxelinux.0
# The same as above, but use custom tftp-server instead machine running dnsmasq
#dhcp-boot=pxelinux,server.name,192.168.1.100
# Boot for Etherboot gPXE. The idea is to send two different
# filenames, the first loads gPXE, and the second tells gPXE what to
# load. The dhcp-match sets the gpxe tag for requests from gPXE.
#dhcp-match=set:gpxe,175 # gPXE sends a 175 option.
#dhcp-boot=tag:!gpxe,undionly.kpxe
#dhcp-boot=mybootimage
# Encapsulated options for Etherboot gPXE. All the options are
# encapsulated within option 175
#dhcp-option=encap:175, 1, 5b # priority code
#dhcp-option=encap:175, 176, 1b # no-proxydhcp
#dhcp-option=encap:175, 177, string # bus-id
#dhcp-option=encap:175, 189, 1b # BIOS drive code
#dhcp-option=encap:175, 190, user # iSCSI username
#dhcp-option=encap:175, 191, pass # iSCSI password
# Test for the architecture of a netboot client. PXE clients are
# supposed to send their architecture as option 93. (See RFC 4578)
#dhcp-match=peecees, option:client-arch, 0 #x86-32
#dhcp-match=itanics, option:client-arch, 2 #IA64
#dhcp-match=hammers, option:client-arch, 6 #x86-64
#dhcp-match=mactels, option:client-arch, 7 #EFI x86-64
# Do real PXE, rather than just booting a single file, this is an
# alternative to dhcp-boot.
#pxe-prompt="What system shall I netboot?"
# or with timeout before first available action is taken:
#pxe-prompt="Press F8 for menu.", 60
# Available boot services. for PXE.
#pxe-service=x86PC, "Boot from local disk"
# Loads <tftp-root>/pxelinux.0 from dnsmasq TFTP server.
#pxe-service=x86PC, "Install Linux", pxelinux
# Loads <tftp-root>/pxelinux.0 from TFTP server at 1.2.3.4.
# Beware this fails on old PXE ROMS.
#pxe-service=x86PC, "Install Linux", pxelinux, 1.2.3.4
# Use bootserver on network, found my multicast or broadcast.
#pxe-service=x86PC, "Install windows from RIS server", 1
# Use bootserver at a known IP address.
#pxe-service=x86PC, "Install windows from RIS server", 1, 1.2.3.4
# If you have multicast-FTP available,
# information for that can be passed in a similar way using options 1
# to 5. See page 19 of
# http://download.intel.com/design/archives/wfm/downloads/pxespec.pdf
# Enable dnsmasq's built-in TFTP server
#enable-tftp
# Set the root directory for files available via FTP.
#tftp-root=/var/ftpd
# Make the TFTP server more secure: with this set, only files owned by
# the user dnsmasq is running as will be send over the net.
#tftp-secure
# This option stops dnsmasq from negotiating a larger blocksize for TFTP
# transfers. It will slow things down, but may rescue some broken TFTP
# clients.
#tftp-no-blocksize
# Set the boot file name only when the "red" tag is set.
#dhcp-boot=tag:red,pxelinux.red-net
# An example of dhcp-boot with an external TFTP server: the name and IP
# address of the server are given after the filename.
# Can fail with old PXE ROMS. Overridden by --pxe-service.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
# If there are multiple external tftp servers having a same name
# (using /etc/hosts) then that name can be specified as the
# tftp_servername (the third option to dhcp-boot) and in that
# case dnsmasq resolves this name and returns the resultant IP
# addresses in round robin fasion. This facility can be used to
# load balance the tftp load among a set of servers.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,tftp_server_name
# Set the limit on DHCP leases, the default is 150
#dhcp-lease-max=150
# The DHCP server needs somewhere on disk to keep its lease database.
# This defaults to a sane location, but if you want to change it, use
# the line below.
#dhcp-leasefile=/var/lib/misc/dnsmasq.leases
# Set the DHCP server to authoritative mode. In this mode it will barge in
# and take over the lease for any client which broadcasts on the network,
# whether it has a record of the lease or not. This avoids long timeouts
# when a machine wakes up on a new network. DO NOT enable this if there's
# the slightest chance that you might end up accidentally configuring a DHCP
# server for your campus/company accidentally. The ISC server uses
# the same option, and this URL provides more information:
# http://www.isc.org/files/auth.html
#dhcp-authoritative
# Run an executable when a DHCP lease is created or destroyed.
# The arguments sent to the script are "add" or "del",
# then the MAC address, the IP address and finally the hostname
# if there is one.
#dhcp-script=/bin/echo
# Set the cachesize here.
#cache-size=150
# If you want to disable negative caching, uncomment this.
#no-negcache
# Normally responses which come from /etc/hosts and the DHCP lease
# file have Time-To-Live set as zero, which conventionally means
# do not cache further. If you are happy to trade lower load on the
# server for potentially stale date, you can set a time-to-live (in
# seconds) here.
#local-ttl=
# If you want dnsmasq to detect attempts by Verisign to send queries
# to unregistered .com and .net hosts to its sitefinder service and
# have dnsmasq instead return the correct NXDOMAIN response, uncomment
# this line. You can add similar lines to do the same for other
# registries which have implemented wildcard A records.
#bogus-nxdomain=64.94.110.11
# If you want to fix up DNS results from upstream servers, use the
# alias option. This only works for IPv4.
# This alias makes a result of 1.2.3.4 appear as 5.6.7.8
#alias=1.2.3.4,5.6.7.8
# and this maps 1.2.3.x to 5.6.7.x
#alias=1.2.3.0,5.6.7.0,255.255.255.0
# and this maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
#alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0
# Change these lines if you want dnsmasq to serve MX records.
# Return an MX record named "maildomain.com" with target
# servermachine.com and preference 50
#mx-host=maildomain.com,servermachine.com,50
# Set the default target for MX records created using the localmx option.
#mx-target=servermachine.com
# Return an MX record pointing to the mx-target for all local
# machines.
#localmx
# Return an MX record pointing to itself for all local machines.
#selfmx
# Change the following lines if you want dnsmasq to serve SRV
# records. These are useful if you want to serve ldap requests for
# Active Directory and other windows-originated DNS requests.
# See RFC 2782.
# You may add multiple srv-host lines.
# The fields are <name>,<target>,<port>,<priority>,<weight>
# If the domain part if missing from the name (so that is just has the
# service and protocol sections) then the domain given by the domain=
# config option is used. (Note that expand-hosts does not need to be
# set for this to work.)
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 389
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 389 (using domain=)
#domain=example.com
#srv-host=_ldap._tcp,ldapserver.example.com,389
# Two SRV records for LDAP, each with different priorities
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,1
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,2
# A SRV record indicating that there is no LDAP server for the domain
# example.com
#srv-host=_ldap._tcp.example.com
# The following line shows how to make dnsmasq serve an arbitrary PTR
# record. This is useful for DNS-SD. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for PTR records.)
#ptr-record=_http._tcp.dns-sd-services,"New Employee Page._http._tcp.dns-sd-services"
# Change the following lines to enable dnsmasq to serve TXT records.
# These are used for things like SPF and zeroconf. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for TXT records.)
#Example SPF.
#txt-record=example.com,"v=spf1 a -all"
#Example zeroconf
#txt-record=_http._tcp.example.com,name=value,paper=A4
# Provide an alias for a "local" DNS name. Note that this _only_ works
# for targets which are names from DHCP or /etc/hosts. Give host
# "bert" another name, bertrand
#cname=bertand,bert
# For debugging purposes, log each DNS query as it passes through
# dnsmasq.
#log-queries
# Log lots of extra information about DHCP transactions.
#log-dhcp
# Include another lot of configuration options.
#conf-file=/etc/dnsmasq.more.conf
#conf-dir=/etc/dnsmasq.d
# Include all the files in a directory except those ending in .bak
#conf-dir=/etc/dnsmasq.d,.bak
# Include all files in a directory which end in .conf
#conf-dir=/etc/dnsmasq.d/*.conf
domain-needed
bogus-priv
filterwin2k
localise-queries
local=/bobine.lol/
domain=bobine.lol
expand-hosts
no-negcache
# Fichier contenant la définition des serveurs en amont
# (au lieu de /etc/resolv.conf)
resolv-file=/etc/resolv.dnsmasq
dhcp-authoritative
dhcp-leasefile=/tmp/dhcp.leases
# use /etc/ethers for static hosts; same format as --dhcp-host
read-ethers
# Add domains which you want to force to an IP address here.
# The example below send any host in double-click.net to a local
# web-server.
# address=/in.bobine.lol/192.168.1.2
# Plage DHCP
dhcp-range=192.168.1.5,192.168.1.250,1m
# Netmask
dhcp-option=1,255.255.255.0
# Route
dhcp-option=3,192.168.1.1
# DNS
#dhcp-option=6,192.168.1.2,8.8.8.8
dhcp-option=6,192.168.1.2,192.168.1.2
# Ajouter une directive de journalisation, non incluse par défaut
log-facility=/var/log/dnsmasq.log
# Pour le débogage, journaliser chaque requête DNS qui passe par Dnsmasq.
log-queries
# Journaliser beaucoup d'informations supplémentaires sur les transactions DHCP.
log-dhcp

11
server/hosts

@ -0,0 +1,11 @@
127.0.0.1 localhost
192.168.1.2 bobinoscope
192.168.1.2 ma.bobine.lol
192.168.1.2 box.bobine.lol
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

63
server/index.js

@ -1,63 +0,0 @@
const path = require('path')
const express = require('express')
const app = express()
const logger = require('morgan')
const cookieParser = require('cookie-parser')
const bodyParser = require('body-parser')
const opn = require('opn')
const booth = require('./booth')
const CFG = require('../config')
const PKG = require('../package')
// view engine setup
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
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('/booth', booth)
app.get('/', (req, res) => {
res.render('index', {
serverIpAddress: CFG.server.ipAddress,
serverPort: CFG.server.port
})
})
app.get('/test', (req, res) => {
res.render('test', {
serverIpAddress: CFG.server.ipAddress,
serverPort: CFG.server.port
})
})
app.get('/bobinoscope', (req, res) => {
res.render('scope', {
serverIpAddress: CFG.server.ipAddress,
serverPort: CFG.server.port,
killZone: CFG.killZone,
renderingTime: 5000
})
})
module.exports = function (pArgs) {
let server = app.listen(CFG.server.port, () => {
console.log(`[-] Server running on: ${CFG.server.ipAddress}:${CFG.server.port}`)
})
// Socket Io
app.locals.io = require('socket.io')(server)
booth.setSocketIo(app.locals.io)
if (!pArgs.noc) {
console.log(`[-] Starting chromium...`)
opn('http://localhost:3111/bobinoscope', {
app: ['chromium-browser', '--app=http://localhost:3111/bobinoscope', '--start-fullscreen', '--password-store=basic']
})
}
return app
}

8
server/public/js/socket.io.js
File diff suppressed because it is too large
View File

1
server/public/js/socket.io.js.map
File diff suppressed because it is too large
View File

BIN
server/public/rsc/footer/m-30ansv2.jpg

Before After
Width: 1600  |  Height: 280  |  Size: 118 KiB

BIN
server/public/waiting.gif

Before After
Width: 220  |  Height: 20  |  Size: 9.2 KiB

BIN
server/public/waiting2.gif

Before After
Width: 64  |  Height: 64  |  Size: 6.0 KiB

251
server/views/scope.ejs

@ -1,251 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Studio</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/cover.css"/>
</head>
<body>
<div class="site-wrapper">
<div class="site-wrapper-inner">
<div class="cover-container">
<div class="inner cover" style="height:900px;position:relative">
<div id="idBoothPanel" style="position:absolute;width:100%">
<div style="padding-top:1em">
<div id="idViewCountdown" style="font-size:20em; display:none"></div>
<div id="idViewTrigger" style="display:block">
<h1 class="cover-heading">Prends ta bobine !</h1>
<br/><br/>
<div style="width:800px;height:600px;position:relative;display:none">
<video id="player" autoplay="true" style="height:100%;width:100%;position:absolute;left:0;top:0"></video>
<div style="position:absolute;background:#3C0000;top:0;left:0;width:<%= killZone.left%>%;height:100%;opacity:0.9"></div>
<div style="position:absolute;background:#3C0000;top:0;right:0;width:<%= killZone.right%>%;height:100%;opacity:0.9"></div>
<div style="position:absolute;background:#3C0000;top:0;left:<%= killZone.left%>%;right:<%= killZone.right%>%;height:<%= killZone.top%>%;opacity:0.9"></div>
<div style="position:absolute;background:#3C0000;bottom:0;left:<%= killZone.left%>%;right:<%= killZone.right%>%;height:<%= killZone.bottom%>%;opacity:0.9"></div>
</div>
</div>
<div id="idViewGenerating" style="display:none">
<h1 class="cover-heading">Bobinogramme <i>en cours de génération...</i></h1>
<br/><br/>
<h3>Merci de bien vouloir patienter<br/>quelques secondes</h3>
<br/>
<br/>
<div style="position:relative;width:100%;height:2px;background:#444">
<div id="idProgressBar" style="background:#fff;height:2px; width:0%;transition: all 200ms ease"></div>
</div>
</div>
<div id="idViewBobinogramme" style="display:none">
<img src="" height="600px"/>
<!-- <h3 class="cover-heading">Retrouve ton <b>bobinogramme</b> sur <br/>http://<%= serverIpAddress %>:<%= serverPort %></h3> -->
</div>
</div>
<div id="idViewDoIt" style="font-size:8em; height:600px; display:none">
Faites<br/>
<span id="idViewDoIt-theme" style="font-size:2em">le chat</span>
</div>
</div>
<div id="idPictPreview" style="position:absolute;display:none;width:100%">
<img src="" height="900px">
</div>
</div>
<div class="mastfoot">
<div class="inner">
<p>BW Bros.</p>
</div>
</div>
</div>
</div>
</div>
</body>
<!-- Latest compiled and minified JQuery -->
<script src="/js/jquery-1.11.2.min.js"></script>
<!-- Latest compiled and minified javaScript bootstrap-->
<script src="/js/bootstrap.min.js"></script>
<!-- Getting the Socket.IO Client -->
<script src="/js/socket.io.js"></script>
</html>
<style type="text/css">
* {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
</style>
<script type="text/javascript">
var socket = io('/');
socket.on('connect', function() {
console.log("connect")
})
socket.on('boothClick', function(pData) {
BOB_runBooth();
});
var BOB_var_context = {
boothOnGoing : false,
initTimer : 3,
onGoingTimer : 0,
boothId : null,
nbError : 0,
pictureId : 0
}
var BOB_var_themes = {
selectedId : 0,
list : [
"le chat",
"le cuir moustache",
"le roi",
"le bouffon",
"le moussailon",
"le geek",
"l'amoureux",
]
}
$(document).ready(function() {
$(document).keypress(function (e) {
if (e.which === 32) BOB_runBooth()
})
})
function BOB_runBooth() {
if( BOB_var_context.boothOnGoing === false ) {
var currentDate = new Date();
BOB_var_context.boothOnGoing = true;
BOB_var_context.boothId = currentDate.getTime();
BOB_var_context.pictureId = 0;
BOB_var_context.onGoingTimer = BOB_var_context.initTimer;
$('#idViewTrigger').fadeOut(500, function() {
BOB_countdown();
});
}
}
function BOB_countdown() {
$('#idViewCountdown').fadeIn();
$('#idViewCountdown').css('fontSize', '20em')
$('#idViewCountdown').html(BOB_var_context.onGoingTimer);
//
if( BOB_var_context.onGoingTimer > 5 ) {
// $('#idViewDoIt').show();
// $('#idViewDoIt-theme').hide();
} else if( BOB_var_context.onGoingTimer > 2 ) {
// $('#idViewDoIt-theme').html( BOB_var_themes.list[BOB_var_themes.selectedId] );
// $('#idViewDoIt-theme').show();
}
if( BOB_var_context.onGoingTimer > 0 ) {
setTimeout(function() {
BOB_var_context.onGoingTimer -= 1;
BOB_countdown();
}, 1000)
} else {
$('#idViewCountdown').html("Souriez");
$('#idViewCountdown').css('fontSize', '14em')
BOB_takeThe1Pictures();
}
}
function BOB_displayPictPreview(pBoothId, pPictureId, pCallback) {
$('#idViewCountdown').html("");
$('#idPictPreview > img').attr('src', '/img/prebuilt/'+pBoothId+'/pict_'+pPictureId+'.jpg');
$('#idBoothPanel').fadeOut(50, function() {
$('#idPictPreview').show();
setTimeout(function() {
$('#idPictPreview').hide();
$('#idBoothPanel').fadeIn(50, function() {
pCallback()
})
}, 4000)
})
}
var BOB_var_progressTimer = new Date();
function BOB_lauchFakeProgressBar() {
setTimeout(function() {
var progress = (new Date() - BOB_var_progressTimer) / <%= renderingTime %> * 100;
$('#idProgressBar').css('width', progress+'%');
if( progress < 100 ) {
BOB_lauchFakeProgressBar();
}
}, 500)
}
function BOB_showError () {
BOB_var_context.nbError += 1
$('#idViewCountdown').fadeIn()
$('#idViewCountdown').css('fontSize', '3em')
if (BOB_var_context.nbError >= 3) {
BOB_var_context.nbError = 0
$('#idViewCountdown').html("Oulala, le bobinoscope se sent pas<br/>très bien<br/>Revenez plus tard !")
setTimeout(function () {
$('#idViewCountdown').fadeOut(500, function() {
$('#idViewTrigger').fadeIn();
})
}, 5000)
} else {
$('#idViewCountdown').html("Oops ! Un problème de pellicule ...<br/>On recommence")
BOB_var_context.onGoingTimer = BOB_var_context.initTimer
setTimeout(BOB_countdown, 4000)
}
}
function BOB_takeThe1Pictures() {
$.get('/booth/dslr/takepicture/'+BOB_var_context.boothId+'/'+BOB_var_context.pictureId, function(pResponse) {
if( pResponse.error ) {
// if( confirm(pResponse.error) ) {
// BOB_var_context.boothOnGoing = false;
// $('#idViewCountdown').fadeOut(500, function() {
// $('#idViewTrigger').fadeIn();
// });
// return;
// }
BOB_showError()
return
}
BOB_var_context.pictureId += 1
BOB_var_context.nbError = 0
if( BOB_var_context.pictureId < 4 ) {
BOB_var_context.onGoingTimer = BOB_var_context.initTimer
BOB_countdown();
} else {
$('#idViewCountdown').fadeOut(500, function() {
BOB_var_progressTimer = new Date();
$('#idProgressBar').css('width', '0');
$('#idViewGenerating').fadeIn();
BOB_lauchFakeProgressBar()
})
$.get('/booth/build/'+BOB_var_context.boothId, function(pResponse) {
$('#idViewBobinogramme > img').attr('src', '/booth/build/result/low/'+pResponse.boothId+'.jpg');
$('#idViewGenerating').fadeOut(500, function() {
$('#idViewBobinogramme').fadeIn(500, function() {
setTimeout(function(){
BOB_var_context.boothOnGoing = false;
$('#idViewBobinogramme').fadeOut(500, function() {
$('#idViewTrigger').fadeIn();
});
}, 10000)
})
})
});
}
})
}
</script>

62
server/views/test.ejs

@ -1,62 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bobinoscope</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="/css/view.css"/>
</head>
<body>
<div id="idMain" style="margin: auto">
<p>
<button onclick="takePicture()">Prendre une photo</button>
</p>
<div id="idViewLoading">
<img src='/waiting2.gif'/>
</div>
<div id="idViewContainer">
</div>
</div>
</body>
<!-- Latest compiled and minified JQuery -->
<script src="/js/jquery-1.11.2.min.js"></script>
</html>
<style type="text/css">
#idViewContainer {
background-size: contain;
background-repeat: no-repeat;
background-position: center;
/* background-origin: */
}
</style>
<script type="text/javascript">
// var socket = io('http://<%= serverIpAddress %>:<%= serverPort %>');
// socket.on('connect', function() {
// console.log("connect")
// })
// socket.on('boothState', function (data) {
// BOB_getBooths();
// })
$(document).ready(function() {
$('#idViewLoading').hide()
$('#idViewContainer').css('height', 90 * $(document).height() / 100)
// console.log()
})
function takePicture () {
$('#idViewLoading').fadeIn()
$.get('/booth/test/takepicture', function (pResponse) {
$('#idViewLoading').fadeOut()
$('#idViewContainer').css('backgroundImage', 'url("/booth/test/takepicture/result?time='+(new Date()).getTime()+'")')
})
}
</script>

258
tools/tools-gphoto2.js

@ -1,144 +1,154 @@
const exec = require('child_process').exec
const path = require('path')
const async = require('async')
const splitLines = require('split-lines')
const mkdirp = require('mkdirp')
const fs = require('fs-extra')
require('colors')
var CFG = require('../config')
let tools = {}
function execGphoto2 (pArgs, pMore, pCallback) {
let args, more, callback
if (pCallback) {
args = pArgs
more = pMore
callback = pCallback
} else {
args = pArgs
more = {}
callback = pMore
}
exec(`gphoto2 ${args}`, more, function (pErr, pStdout, pStderr) {
if (pErr) {
// console.log(`[!] Please install gphoto2`.red)
// console.log(`sudo apt-get install gphoto2`.bgMagenta)
// console.log(`[-] Bobinoscope will close in 10 seconds...`)
// console.log('[!]', pErr)
callback(pErr)
// setTimeout(function () {
// process.exit(1)
// }, 10000)
return
}
let error = null
let outInLines = []
if (/Error/i.test(pStderr)) {
error = pStderr
}
if (pStdout) {
outInLines = splitLines(pStdout)
}
callback(error, outInLines)
})
}
var BOB_mod_gm = require('gm').subClass({ imageMagick: true });
// 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');
tools.checkVersion = function (pCb) {
console.log('[-] Checking gphoto2 installation...'.bgBlue)
execGphoto2('--version', function (pErr, pStdout) {
if (pErr) {
console.log(`KO`.red)
} else {
console.log(`OK: ${pStdout[0]}`.green)
}
pCb(pErr)
var BOB_mod_exec = require('child_process').exec;
var BOB_cfg_config = null;
var BOB_var_camera = null;
var BOB_module = {};
var BOB_tmpPict = "/home/odroid/bobine-tmp.jpg"
BOB_upCameraReference(function() {});
function BOB_takePicture(pCallback) {
BOB_mod_exec("gphoto2 --capture-image-and-download --filename="+BOB_tmpPict+" --force-overwrite", function(pErr, pStdout, pStderr) {
console.log("takePicture", pErr || pStdout || pStderr)
console.log(pErr, pStdout, pStderr)
return pCallback(pErr)
})
}
tools.checkCameraConnection = function (pCb) {
console.log('[-] Checking camera connection...'.bgBlue)
execGphoto2('--auto-detect', function (pErr, pStdout) {
let device = pStdout && pStdout.length ? pStdout[2] : null
if (pErr || !device) {
console.log(`[!] Camera not connected`.red)
console.log('1. Connect camera via USB'.bgMagenta)
console.log('2. Turn on the camera'.bgMagenta)
console.log('3. Maybe - Force manual focus on camera'.bgMagenta)
pCb(pErr || 'CameraNotFound')
// console.log(`\n\n[-] Bobinoscope will close in 10 seconds...`)
// setTimeout(function () {
// process.exit(1)
// }, 10000)
// return
} else {
console.log(`OK: ${device}`.green)
pCb()
}
})
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 {
// console.log(list)
// BOB_var_camera = list[0];
// console.log(BOB_var_camera.model);
// return pCallback();
// }
// })
//} else {
return pCallback();
//}
}
tools.__takeOnePicture = function (pPath, pCb) {
let lPath = path.parse(pPath)
execGphoto2(`--capture-image-and-download --filename=${lPath.base} --force-overwrite`, {cwd: lPath.dir}, pCb)
BOB_module.getConfig = function(pCallback) {
BOB_var_camera.getConfig(pCallback);
}
tools.takeOnePicture = function (pBoothId, pPictId, pCallback) {
let destPict = '/tmp/bobine.jpg'
var destPath = path.join(CFG.paths.original, pBoothId)
var pictOrig = path.join(destPath, CFG.pictNames[pPictId])
BOB_module.takePicture = function(pBoothId, pPictId, pCallback) {
var destPath = BOB_mod_path.join(BOB_cfg_config.paths.original, pBoothId);
var pbltPath = BOB_mod_path.join(BOB_cfg_config.paths.prebuilt, pBoothId);
var pictOrig = BOB_mod_path.join(destPath, BOB_cfg_config.pictNames[pPictId]);
var pictPblt = BOB_mod_path.join(destPath, BOB_cfg_config.pictNames[pPictId]);
BOB_mod_mkdirp.sync(BOB_mod_path.join(BOB_cfg_config.paths.prebuilt, pBoothId));
mkdirp.sync(path.join(CFG.paths.prebuilt, pBoothId))
console.log("new")
tools.__takeOnePicture(destPict, function (pErr) {
BOB_takePicture(function (pErr) {
// If error occurs
if (pErr) return pCallback(pErr)
mkdirp(destPath, function () {
fs.copy(destPict, pictOrig, function (pErr) {
return pCallback(pErr, pictOrig)
if( pErr ) {
return pCallback(pErr)
// Else write file on ddisk
} else {
BOB_mod_mkdirp(destPath, function() {
copyFile(BOB_tmpPict, pictOrig, function(pErr) {
return pCallback(pErr, pictOrig)
});
})
})
})
}
});
}
tools.init = function (pCb) {
let tasks = []
tasks.push(tools.checkVersion)
tasks.push(tools.checkCameraConnection)
tasks.push(function (pCb) {
let output = '/tmp/bobine-dummy.jpg'
console.log('[-] Taking dummy photo...'.bgBlue)
tools.__takeOnePicture(output, function (pErr) {
if (pErr) {
console.log(`[!] Failed to take picture`.red)
console.log(pErr.grey)
console.log('1. Add one SDCard into the device')
console.log('2. Take one picture manually')
} else {
console.log(`OK: ${output}`.green)
}
pCb(pErr)
})
// opn(output)
// .then(function () {
// console.log('OK'.green)
// pCb()
// })
})
async.waterfall(tasks, function (pErr) {
if (pErr) {
console.log('[!] Bobinoscope init failed... Retry in 10 seconds'.red)
setTimeout(function () {
tools.init(pCb)
}, 10000)
function copyFile(source, target, cb) {
var cbCalled = false;
var rd = BOB_mod_fs.createReadStream(source);
rd.on("error", function(err) {
done(err);
});
var wr = BOB_mod_fs.createWriteStream(target);
wr.on("error", function(err) {
done(err);
});
wr.on("close", function(ex) {
done();
});
rd.pipe(wr);
function done(err) {
if (!cbCalled) {
cb(err);
cbCalled = true;
}
}
}
BOB_module.takePictureNpm = function(pBoothId, pPictId, pCallback) {
var destPath = BOB_mod_path.join(BOB_cfg_config.paths.original, pBoothId);
var pbltPath = BOB_mod_path.join(BOB_cfg_config.paths.prebuilt, pBoothId);
var pictOrig = BOB_mod_path.join(destPath, BOB_cfg_config.pictNames[pPictId]);
var pictPblt = BOB_mod_path.join(destPath, BOB_cfg_config.pictNames[pPictId]);
console.log("1");
BOB_mod_mkdirp.sync(BOB_mod_path.join(BOB_cfg_config.paths.prebuilt, pBoothId));
console.log("2");
BOB_upCameraReference(function() {
console.log("3");
if( BOB_var_camera === null ) {
return pCallback("No camera found");
} else {
pCb()
console.log("4");
BOB_var_camera.takePicture({download: true}, function (pErr, pData) {
// If error occurs
if( pErr ) {
return pCallback(pErr)
// Else write file on ddisk
} else {
console.log("5");
BOB_mod_mkdirp(destPath, function() {
console.log("6");
BOB_mod_fs.writeFile(pictOrig, pData, function(pErr) {
console.log("7");
// BOB_mod_gm(pictOrig)
// .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(pictOrig, function(pErr) {
// return pCallback(pErr, pictPblt)
// })
return pCallback(pErr, pictOrig)
});
})
}
});
}
})
}
module.exports = tools
module.exports = function(pConfig) {
BOB_cfg_config = pConfig;
return BOB_module;
}

192
tools/tools-photobooth.js

@ -1,155 +1,153 @@
var gm = require('gm').subClass({ imageMagick: true })
var async = require('async')
var path = require('path')
var fs = require('fs')
var mkdirp = require('mkdirp')
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 CFG = require('../config')
var BOB_cfg_config = null;
var tools = {}
var BOB_module = {};
tools.getBoothList = function (pFrom, pCallback) {
fs.readdir(CFG.paths.final, function (pErr, pBoothList) {
var boothList = pBoothList ? pBoothList.sort() : []
boothList = boothList.splice(boothList.indexOf(pFrom) + 1)
pCallback(pErr, boothList)
BOB_module.getBoothList = function(pFrom, pCallback) {
BOB_mod_fs.readdir(BOB_cfg_config.paths.final, function(pErr, pBoothList) {
var boothList = pBoothList ? pBoothList.sort() : [];
boothList = boothList.splice(boothList.indexOf(pFrom) + 1);
pCallback(pErr, boothList);
})
}
tools.buildBooth = function (pBoothId, pCallback) {
BOB_module.buildBooth = function(pBoothId, pCallback) {
// Create output prebuild directory
mkdirp.sync(path.join(CFG.paths.prebuilt, pBoothId))
BOB_mod_mkdirp.sync(BOB_mod_path.join(BOB_cfg_config.paths.prebuilt, pBoothId));
// Resize pictures
async.mapSeries(CFG.pictNames,
function (pPictName, pCb) {
var srcPict = path.join(CFG.paths.original, pBoothId, pPictName)
var dstPict = path.join(CFG.paths.prebuilt, pBoothId, pPictName)
BOB_mod_async.mapSeries(BOB_cfg_config.pictNames,
function(pPictName, 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)
console.log("Resizing "+dstPict);
gm(srcPict)
BOB_mod_gm(srcPict)
.autoOrient()
.gravity('Center')
.resize(CFG.cropSize.width, CFG.cropSize.height, '^')
.crop(CFG.cropSize.width, CFG.cropSize.height)
.write(dstPict, function (pErr) {
if (pErr) console.log('Failed to resize picture', pErr)
.resize(BOB_cfg_config.cropSize.width, BOB_cfg_config.cropSize.height, "^")
.crop(BOB_cfg_config.cropSize.width, BOB_cfg_config.cropSize.height)
.write(dstPict, function(pErr) {
if(pErr) console.log("Failed to resize picture", pErr)
pCb()
})
},
function (pErr) {
console.log('Building booth')
switch (CFG.style) {
function(pErr) {
console.log("Building booth")
switch(BOB_cfg_config.style) {
default :
BOB_generateBooth(pBoothId, 'default', pCallback)
BOB_generateBooth(pBoothId, 'default', pCallback);
}
})
}
function BOB_generateBooth (pBoothId, pType, pCallback) {
var prebuiltPath = path.join(CFG.paths.prebuilt, pBoothId)
var finalPict = path.join(CFG.paths.final, pBoothId + '.jpg')
var finalLdPict = path.join(CFG.paths.final_ld, pBoothId + '.jpg')
var outPict = gm(CFG.booths[pType].template)
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))
var printedDate = new Date(parseInt(pBoothId));
outPict.fill(CFG.background)
outPict.drawRectangle(0, 0, CFG.booths[pType].resolution.width, CFG.booths[pType].resolution.height)
outPict.fill(BOB_cfg_config.background);
outPict.drawRectangle(0, 0, BOB_cfg_config.booths[pType].resolution.width, BOB_cfg_config.booths[pType].resolution.height);
// Draw footer
outPict.draw('image Over 0,2100 1600,250 \'' + CFG.footer + '\'')
outPict.draw('image Over 0,2100 1600,250 \''+BOB_cfg_config.footer+'\'');
CFG.pictNames.forEach(function (pPictName, pIndex) {
var cmdDraw = 'image Over'
cmdDraw += ' ' + CFG.booths[pType].layout[pIndex].x
cmdDraw += ',' + CFG.booths[pType].layout[pIndex].y
cmdDraw += ' ' + CFG.booths[pType].layout[pIndex].width
cmdDraw += ',' + CFG.booths[pType].layout[pIndex].height
cmdDraw += ' \'' + path.join(prebuiltPath, pPictName) + '\''
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.draw(cmdDraw);
})
outPict.fill('#666')
outPict.pointSize(20)
outPict.draw('text 1250,2350 "' + printedDate.toUTCString() + '"')
outPict.fill("#666");
outPict.pointSize(20);
outPict.draw('text 1250,2350 "'+printedDate.toUTCString()+'"');
outPict.write(finalPict, function (pErr) {
if (pErr) {
console.log('Write outfile')
if( pErr ) {
console.log("Write outfile")
console.log(pErr)
pCallback(pErr)
pCallback(pErr);
} else {
gm(finalPict)
BOB_mod_gm(finalPict)
.resize(400, null)
.write(finalLdPict, function (pErr) {
pCallback(pErr)
.write(finalLdPict, function(pErr) {
pCallback(pErr);
})
}
})
});
}
module.exports = tools
tools.init = function () {
BOB_updateConfig()
module.exports = function(pConfig) {
BOB_cfg_config = pConfig;
BOB_updateConfig();
return BOB_module;
}
// function (pConfig) {
// CFG = pConfig
// return tools
// }
function BOB_updateConfig () {
fs.readdir(CFG.paths.template, function (pErr, pFiles) {
if (pErr) {
console.log('BOB_updateConfig', 'Path:', CFG.paths.template, pErr)
function BOB_updateConfig() {
BOB_mod_fs.readdir(BOB_cfg_config.paths.template, function(pErr, pFiles) {
if( pErr ) {
console.log("BOB_updateConfig", "Path:", BOB_cfg_config.paths.template, pErr);
} else {
async.map(pFiles,
function (pFile, pCb) {
BOB_mod_async.map(pFiles,
function(pFile, pCb) {
// Get name of template
var boothName = pFile.split('.')[0]
var boothName = pFile.split('.')[0];
// Init layouts object for the template
CFG.booths[boothName] = {
name: boothName,
template: path.join(CFG.paths.template, pFile),
resolution: { width: 0, height: 0 },
layout: []
}
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
gm(CFG.booths[boothName].template).size(function (pErr, pValue) {
if (pErr) {
console.log('[ERROR] Get template size', pErr)
BOB_mod_gm(BOB_cfg_config.booths[boothName].template).size(function(pErr, pValue){
if( pErr ) {
console.log("[ERROR] Get template size", pErr);
} else {
CFG.booths[boothName].resolution = pValue
BOB_generatePictLayout(boothName)
BOB_cfg_config.booths[boothName].resolution = pValue;
BOB_generatePictLayout(boothName);
}
pCb()
pCb();
})
},
function (pErr) {
function(pErr) {
})
}
})
}
function BOB_generatePictLayout (pBoothName) {
var index = 0
var marginX = 20
var marginY = 20
function BOB_generatePictLayout(pBoothName) {
var index = 0;
var marginX = 20;
var marginY = 20;
var pictWidth = (CFG.booths[pBoothName].resolution.width - 3 * marginX) / 2
var pictHeight = parseInt(pictWidth * CFG.cropSize.height / CFG.cropSize.width)
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);
CFG.pictNames.forEach(function (pPictName, pIndex) {
CFG.booths[pBoothName].layout.push({
x: marginX + (marginX + pictWidth) * (pIndex % 2),
y: marginY + (marginY + pictHeight) * parseInt(pIndex / 2),
width: pictWidth,
height: pictHeight
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')
console.log("INIT DONE");
}

5
tools/tools-test.js

@ -0,0 +1,5 @@
module.exports = function(pConfig) {
console.log("TOOL TEST", pConfig);
}

3
views/error.ejs

@ -0,0 +1,3 @@
<h1><%= message %></h1>
<h2><%= error.status %></h2>
<pre><%= error.stack %></pre>

19
views/fixdate.ejs

@ -0,0 +1,19 @@
<html>
<body style="text-align:center">
<h2>Click to force Bobinoscope date from your phone.</h2>
<br/>
<button class="fixdate">Fix Date</button>
</body>
<!-- Latest compiled and minified JQuery -->
<script src="/js/jquery-1.11.2.min.js"></script>
<script>
$(document).ready(function() {
$('.fixdate').click(function() {
$.get('/fixdate/'+((new Date()).getTime()), function(pResponse) {
console.log('DONE', pResponse);
alert('done', pResponse.error);
})
})
})
</script>
</html>

248
views/index.ejs

@ -0,0 +1,248 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Studio</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/cover.css"/>
</head>
<body>
<div class="site-wrapper">
<div class="site-wrapper-inner">
<div class="cover-container">
<div class="inner cover" style="height:900px;position:relative">
<div id="idBoothPanel" style="position:absolute;width:100%">
<div style="padding-top:1em">
<div id="idViewCountdown" style="font-size:20em; display:none"></div>
<div id="idViewTrigger" style="display:block">
<h1 class="cover-heading">Prends ta bobine !</h1>
<br/><br/>
<div style="width:800px;height:600px;position:relative;display:none">
<video id="player" autoplay="true" style="height:100%;width:100%;position:absolute;left:0;top:0"></video>
<div style="position:absolute;background:#3C0000;top:0;left:0;width:<%= killZone.left%>%;height:100%;opacity:0.9"></div>
<div style="position:absolute;background:#3C0000;top:0;right:0;width:<%= killZone.right%>%;height:100%;opacity:0.9"></div>
<div style="position:absolute;background:#3C0000;top:0;left:<%= killZone.left%>%;right:<%= killZone.right%>%;height:<%= killZone.top%>%;opacity:0.9"></div>
<div style="position:absolute;background:#3C0000;bottom:0;left:<%= killZone.left%>%;right:<%= killZone.right%>%;height:<%= killZone.bottom%>%;opacity:0.9"></div>
</div>
</div>
<div id="idViewGenerating" style="display:none">
<h1 class="cover-heading">Bobinogramme <i>en cours de génération...</i></h1>
<br/><br/>
<h3>Merci de bien vouloir patienter<br/>une quinzaine de secondes</h3>
<br/>
<br/>
<div style="position:relative;width:100%;height:2px;background:#444">
<div id="idProgressBar" style="background:#fff;height:2px; width:0%;transition: all 200ms ease"></div>
</div>
</div>
<div id="idViewBobinogramme" style="display:none">
<img src="/img/final_ld/1438098902994.jpg" height="600px"/>
<h3 class="cover-heading">Retrouve ton <b>bobinogramme</b> sur <br/>http://192.168.1.2</h3>
</div>
</div>
<div id="idViewDoIt" style="font-size:8em; height:600px; display:none">
Faites<br/>
<span id="idViewDoIt-theme" style="font-size:2em">le chat</span>
</div>
</div>
<div id="idPictPreview" style="position:absolute;display:none;width:100%">
<img src="/img/test.jpgautoOrient.jpg" height="900px">
</div>
</div>
<div class="mastfoot">
<div class="inner">
<p>BW Bros.</p>
</div>
</div>
</div>
</div>
</div>
</body>
<!-- Latest compiled and minified JQuery -->
<script src="/js/jquery-1.11.2.min.js"></script>
<!-- Latest compiled and minified javaScript bootstrap-->
<script src="/js/bootstrap.min.js"></script>
<!-- Getting the Socket.IO Client -->
<script src="/js/socket.io-1.3.4.js"></script>
</html>
<style type="text/css">
* {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
</style>
<script type="text/javascript">
var socket = io('http://<%= serverIpAddress %>:3000');
socket.on('connect', function() {
console.log("connect")
})
socket.on('boothClick', function(pData) {
BOB_runBooth();
});
var BOB_var_context = {
boothOnGoing : false,
initTimer : 3,
onGoingTimer : 0,
boothId : null,
pictureId : 0
}
var BOB_var_themes = {
selectedId : 0,
list : [
"le chat",
"le cuir moustache",
"le roi",
"le bouffon",
"le moussailon",
"le geek",
"l'amoureux",
]
}
$(document).ready(function() {
$('#idBtnClick').click(function() {
//BOB_runBooth();
})
})
function BOB_runBooth() {
if( BOB_var_context.boothOnGoing === false ) {
var currentDate = new Date();
BOB_var_context.boothOnGoing = true;
BOB_var_context.boothId = currentDate.getTime();
BOB_var_context.pictureId = 0;
BOB_var_context.onGoingTimer = BOB_var_context.initTimer;
$('#idViewTrigger').fadeOut(500, function() {
BOB_countdown();
});
}
}
function BOB_countdown() {
$('#idViewCountdown').fadeIn();
$('#idViewCountdown').html(BOB_var_context.onGoingTimer);
//
if( BOB_var_context.onGoingTimer > 5 ) {
// $('#idViewDoIt').show();
// $('#idViewDoIt-theme').hide();
} else if( BOB_var_context.onGoingTimer > 2 ) {
// $('#idViewDoIt-theme').html( BOB_var_themes.list[BOB_var_themes.selectedId] );
// $('#idViewDoIt-theme').show();
}
if( BOB_var_context.onGoingTimer > 0 ) {
setTimeout(function() {
BOB_var_context.onGoingTimer -= 1;
BOB_countdown();
}, 1000)
} else {
$('#idViewCountdown').html("Smile");
BOB_takeThe1Pictures();
}
}
function BOB_displayPictPreview(pBoothId, pPictureId, pCallback) {
$('#idViewCountdown').html("");
$('#idPictPreview > img').attr('src', '/img/prebuilt/'+pBoothId+'/pict_'+pPictureId+'.jpg');
$('#idBoothPanel').fadeOut(50, function() {
$('#idPictPreview').show();
setTimeout(function() {
$('#idPictPreview').hide();
$('#idBoothPanel').fadeIn(50, function() {
pCallback()
})
}, 4000)
})
}
var BOB_var_progressTimer = new Date();
function BOB_lauchFakeProgressBar() {
setTimeout(function() {
var progress = (new Date() - BOB_var_progressTimer) / 15000 * 100;
$('#idProgressBar').css('width', progress+'%');
if( progress < 100 ) {
BOB_lauchFakeProgressBar();
}
}, 500)
}
function BOB_takeThe1Pictures() {
$.get('/dslr/takepicture/'+BOB_var_context.boothId+'/'+BOB_var_context.pictureId, function(pResponse) {
if( pResponse.error ) {
if( confirm(pResponse.error) ) {
BOB_var_context.boothOnGoing = false;
$('#idViewCountdown').fadeOut(500, function() {
$('#idViewTrigger').fadeIn();
});
return;
}
}
BOB_var_context.pictureId += 1;
if( BOB_var_context.pictureId < 4 ) {
BOB_var_context.onGoingTimer = BOB_var_context.initTimer;
BOB_countdown();
} else {
$('#idViewCountdown').fadeOut(500, function() {
BOB_var_progressTimer = new Date();
$('#idProgressBar').css('width', '0');
$('#idViewGenerating').fadeIn();
BOB_lauchFakeProgressBar()
})
$.get('/booth/build/'+BOB_var_context.boothId, function(pResponse) {
$('#idViewBobinogramme > img').attr('src', '/img/final_ld/'+pResponse.boothId+'.jpg');
$('#idViewGenerating').fadeOut(500, function() {
$('#idViewBobinogramme').fadeIn(500, function() {
setTimeout(function(){
BOB_var_context.boothOnGoing = false;
$('#idViewBobinogramme').fadeOut(500, function() {
$('#idViewTrigger').fadeIn();
});
}, 10000)
})
})
});
}
})
}
/*
(function(){
var mediaOptions = { audio: false, video: true };
if (!navigator.getUserMedia) {
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
}
if (!navigator.getUserMedia){
return alert('getUserMedia not supported in this browser.');
}
navigator.getUserMedia(mediaOptions, success, function(e) {
console.log(e);
});
function success(stream){
var video = document.querySelector("#player");
video.src = window.URL.createObjectURL(stream);
}
})();
*/
</script>

72
views/index_back.ejs

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= title %></title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/cover.css"/>
</head>
<body>
<body>
<div class="site-wrapper">
<div class="site-wrapper-inner">
<div class="cover-container">
<div class="masthead clearfix">
<div class="inner">
<h3 class="masthead-brand">Bobinoscope</h3>
</div>
</div>
<div class="inner cover">
<h1 class="cover-heading">Prends ta face !</h1>
<p class="lead">
<a href="#" class="btn btn-lg btn-default" id="idBtnClick">Clic</a>
</p>
<img id="idDisplayImg" width="640px"/>
</div>
<div class="mastfoot">
<div class="inner">
<p>BW Bros.</p>
</div>
</div>
</div>
</div>
</div>
</body>
<!-- Latest compiled and minified JQuery -->
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<!-- Latest compiled and minified javaScript bootstrap-->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
</html>
<script type="text/javascript">
$(document).ready(function() {
$('#idBtnClick').click(function() {
BOB_takeThe4Pictures();
})
})
function BOB_takeThe4Pictures() {
var currentDate = new Date();
var pictId = 0;
var boothId = currentDate.getTime();
}
function BOB_takeOnePicture(pBoothId, pPictId) {
$.get('/dslr/takepicture', function(pResponse) {
$('#idDisplayImg').attr('src', pResponse.data);
})
}
</script>

server/views/index.ejs → views/view.ejs

@ -12,7 +12,7 @@
<div id="idMain" style="width:400px; margin: auto">
<p>Cliquez sur le <i>bobinogramme</i> qui vous intéresse<br/>pour afficher le menu</p>
<div id="idViewLoading">
<img src='/waiting.gif'/>
<img src='/img/common/294.GIF'/>
</div>
<div id="idViewContainer">
@ -24,14 +24,14 @@
<br/>
<div>
<button id="idMenu-btnDownload">Télécharger</button>
<!-- <button id="idMenu-btnPrint">Imprimer</button> -->
<button id="idMenu-btnPrint">Imprimer</button>
</div>
</div>
</body>
<!-- Latest compiled and minified JQuery -->
<script src="/js/jquery-1.11.2.min.js"></script>
<!-- Getting the Socket.IO Client -->
<script src="/js/socket.io.js"></script>
<script src="/js/socket.io-1.3.4.js"></script>
</html>
<style type="text/css">
@ -64,13 +64,13 @@
<script type="text/javascript">
var socket = io('/');
var socket = io('http://<%= serverIpAddress %>:3000');
socket.on('connect', function() {
// console.log("connect")
console.log("connect")
})
socket.on('boothState', function (data) {
BOB_getBooths();
})
});
$(document).ready(function() {
BOB_getBooths();
@ -88,15 +88,15 @@ $(document).ready(function() {
$('#idMenu-btnDownload').click(function() {
if( confirm("Télécharger le bobinogramme ?\n\nPour les iPhone-istes, un nouvel onglet va s'ouvrir avec le bobinogramme voulu. Un click long sur celui-ci permet de la télécharger définitivement.") ) {
window.open("/booth/build/result/"+BOB_var_selectedBoothId, "_blank");
window.open("/download/final/hd/"+BOB_var_selectedBoothId, "_blank");
}
})
$('#idMenu-btnPrint').click(function() {
console.log(BOB_var_printer.todos, BOB_var_selectedBoothId)
if( BOB_var_printer.todos.indexOf(BOB_var_selectedBoothId) === -1 ) {
if( BOB_var_printer.todos.indexOf(BOB_var_selectedBoothId) === -1 ) {
if( confirm("Envoyer une demande d'impression du bobinogramme pour le livre d'Or?\n\nPour que tout le monde puisse y mettre sa bobine, ne faites qu'une seule demande par groupe.") ) {
$.get("/booth/print/"+BOB_var_selectedBoothId, function(pResponse) {
$.get("/print/bobinogramme/"+BOB_var_selectedBoothId, function(pResponse) {
if( pResponse.error ) {
console.log("Printer: ", pResponse.error)
alert("Echec ! Essayez d'envoyer une nouvelle demande")
@ -121,7 +121,7 @@ function BOB_getBooths() {
var boothList = $(".classBoothItem");
var jqBoothContainer = $('#idViewContainer');
$.get('/booth/toprint', function(pResponse) {
$.get('/print/list/todo', function(pResponse) {
if( pResponse.error ) {
console.log('Printer: ', pResponse.error)
} else {
@ -129,14 +129,14 @@ function BOB_getBooths() {
}
})
$.get('/booth/list/'+BOB_var_lastBooth, function (pResponse) {
$.get('/booth/list/'+BOB_var_lastBooth, function(pResponse) {
if( pResponse.inProgress > 0 ) {
$('#idViewLoading').fadeIn();
} else {
$('#idViewLoading').fadeOut();
}
pResponse.booths.forEach(function (pBoothName) {
pResponse.booths.forEach(function(pBoothName) {
var jqImg = $('<img>');
var jqMenu = $('<div>');
var jqBtnDownload = $('<button>');
@ -145,12 +145,12 @@ function BOB_getBooths() {
jqImg.css('opacity', '0');
jqImg.css('transition', 'opacity 1s');
jqImg.data('booth', pBoothName);
jqImg.attr('src', "/booth/build/result/low/" + pBoothName);
jqImg.attr('src', "/img/final_ld/"+pBoothName);
jqImg.addClass('classBoothItem');
jqImg.on('click', function() {
BOB_var_selectedBoothId = pBoothName;
$('#idMenu-preview').attr('src', "/booth/build/result/low/" + pBoothName);
$('#idMenu-preview').attr('src', "/img/final_ld/"+pBoothName);
$('#idMenu').fadeIn();
})
Loading…
Cancel
Save