Browse Source

first init

master
P.BARRY 7 years ago
commit
052944aa01
29 changed files with 2588 additions and 0 deletions
  1. 2
      .gitignore
  2. 85
      config.js
  3. 25
      index.js
  4. 1328
      package-lock.json
  5. 32
      package.json
  6. 129
      server/booth.js
  7. 56
      server/index.js
  8. 5
      server/public/css/bootstrap.min.css
  9. 163
      server/public/css/cover.css
  10. 8
      server/public/css/style.css
  11. 10
      server/public/css/view.css
  12. 7
      server/public/js/bootstrap.min.js
  13. 4
      server/public/js/jquery-1.11.2.min.js
  14. 3
      server/public/js/socket.io-1.3.4.js
  15. 8
      server/public/js/socket.io.js
  16. 1
      server/public/js/socket.io.js.map
  17. BIN
      server/public/rsc/common/294.GIF
  18. BIN
      server/public/rsc/footer/cloclo.jpg
  19. BIN
      server/public/rsc/footer/m-30ansv2.jpg
  20. BIN
      server/public/rsc/footer/m-cloclo.jpg
  21. BIN
      server/public/rsc/footer/m-clotilde60.jpg
  22. BIN
      server/public/rsc/template/default.jpg
  23. BIN
      server/public/waiting.gif
  24. BIN
      server/public/waiting2.gif
  25. 172
      server/views/index.ejs
  26. 226
      server/views/scope.ejs
  27. 62
      server/views/test.ejs
  28. 107
      tools/tools-gphoto2.js
  29. 155
      tools/tools-photobooth.js

2
.gitignore

@ -0,0 +1,2 @@
*node_modules*
output/

85
config.js

@ -0,0 +1,85 @@
const os = require('os')
const path = require('path')
const mkdirp = require('mkdirp')
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: ''
},
footer: 'm-30ansv2.jpg'
}
config.server = {
ipAddress: getIpAddress(),
port: 3111
}
config.killZone = {
top: 2,
bottom: 2,
left: 25,
right: 25
}
// 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(__dirname, './output/final')
config.paths.final_ld = path.resolve(__dirname, './output/final_ld')
config.paths.prebuilt = path.resolve(__dirname, './output/cache')
config.paths.original = path.resolve(__dirname, './output/original')
config.paths.toprint = path.resolve(__dirname, './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
}

25
index.js

@ -0,0 +1,25 @@
const gphoto2 = require('./tools/tools-gphoto2')
const server = require('./server')
// gphoto2.init(function (pErr) {
// console.log('DONE')
let app = server()
// })
// // 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
// });
// })

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

32
package.json

@ -0,0 +1,32 @@
{
"name": "bobinoscope-v2",
"version": "1.0.0",
"description": "Code for bobinoscope",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "P.BARRY",
"license": "ISC",
"dependencies": {
"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/*"
]
}
}

129
server/booth.js

@ -0,0 +1,129 @@
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: '/tmp/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) {
res.json({ error: pErr, 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 = '/tmp/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('/tmp/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

56
server/index.js

@ -0,0 +1,56 @@
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 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 () {
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)
return app
}

5
server/public/css/bootstrap.min.css
File diff suppressed because it is too large
View File

163
server/public/css/cover.css

@ -0,0 +1,163 @@
/*
* Globals
*/
/* Links */
a,
a:focus,
a:hover {
color: #fff;
}
/* Custom default button */
.btn-default,
.btn-default:hover,
.btn-default:focus {
color: #333;
text-shadow: none; /* Prevent inheritence from `body` */
background-color: #fff;
border: 1px solid #fff;
}
/*
* Base structure
*/
html,
body {
height: 100%;
background-color: #333;
}
body {
color: #fff;
text-align: center;
text-shadow: 0 1px 3px rgba(0,0,0,.5);
}
/* Extra markup and styles for table-esque vertical and horizontal centering */
.site-wrapper {
display: table;
width: 100%;
height: 100%; /* For at least Firefox */
min-height: 100%;
-webkit-box-shadow: inset 0 0 100px rgba(0,0,0,.5);
box-shadow: inset 0 0 100px rgba(0,0,0,.5);
}
.site-wrapper-inner {
display: table-cell;
vertical-align: top;
}
.cover-container {
margin-right: auto;
margin-left: auto;
}
/* Padding for spacing */
.inner {
padding: 30px;
}
/*
* Header
*/
.masthead-brand {
margin-top: 10px;
margin-bottom: 10px;
}
.masthead-nav > li {
display: inline-block;
}
.masthead-nav > li + li {
margin-left: 20px;
}
.masthead-nav > li > a {
padding-right: 0;
padding-left: 0;
font-size: 16px;
font-weight: bold;
color: #fff; /* IE8 proofing */
color: rgba(255,255,255,.75);
border-bottom: 2px solid transparent;
}
.masthead-nav > li > a:hover,
.masthead-nav > li > a:focus {
background-color: transparent;
border-bottom-color: #a9a9a9;
border-bottom-color: rgba(255,255,255,.25);
}
.masthead-nav > .active > a,
.masthead-nav > .active > a:hover,
.masthead-nav > .active > a:focus {
color: #fff;
border-bottom-color: #fff;
}
@media (min-width: 768px) {
.masthead-brand {
float: left;
}
.masthead-nav {
float: right;
}
}
/*
* Cover
*/
.cover {
padding: 0 20px;
}
.cover .btn-lg {
padding: 10px 20px;
font-weight: bold;
}
/*
* Footer
*/
.mastfoot {
color: #999; /* IE8 proofing */
color: rgba(255,255,255,.5);
}
/*
* Affix and center
*/
@media (min-width: 768px) {
/* Pull out the header and footer */
.masthead {
position: fixed;
top: 0;
}
.mastfoot {
position: fixed;
bottom: 0;
}
/* Start the vertical centering */
.site-wrapper-inner {
vertical-align: middle;
}
/* Handle the widths */
.masthead,
.mastfoot,
.cover-container {
width: 100%; /* Must be percentage or pixels for horizontal alignment */
}
}
@media (min-width: 992px) {
.masthead,
.mastfoot,
.cover-container {
width: 700px;
}
}

8
server/public/css/style.css

@ -0,0 +1,8 @@
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}

10
server/public/css/view.css

@ -0,0 +1,10 @@
html,
body {
height: 100%;
background-color: #333;
}
body {
color: #fff;
text-align: center;
text-shadow: 0 1px 3px rgba(0,0,0,.5);
}

7
server/public/js/bootstrap.min.js
File diff suppressed because it is too large
View File

4
server/public/js/jquery-1.11.2.min.js
File diff suppressed because it is too large
View File

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

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/common/294.GIF

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

BIN
server/public/rsc/footer/cloclo.jpg

Before After
Width: 1240  |  Height: 1778  |  Size: 266 KiB

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

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

BIN
server/public/rsc/footer/m-cloclo.jpg

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

BIN
server/public/rsc/footer/m-clotilde60.jpg

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

BIN
server/public/rsc/template/default.jpg

Before After
Width: 1600  |  Height: 2391  |  Size: 53 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

172
server/views/index.ejs

@ -0,0 +1,172 @@
<!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="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'/>
</div>
<div id="idViewContainer">
</div>
</div>
<div id="idMenu" style="display:none">
<div id="idMenu-title">Bobinoscope</div>
<img id="idMenu-preview" src=""/>
<br/>
<div>
<button id="idMenu-btnDownload">Télécharger</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>
</html>
<style type="text/css">
#idMenu {
background: #000;
opacity: 1;
position: fixed;
width: 100%;
padding: 1em;
top:0;
left:0;
bottom: 0;
right: 0;
font-size: 1.2em;
}
.classBoothItem {
margin: 5px 0
}
#idMenu button{
padding: 0.5em;
}
#idMenu-title {
padding: 0.3em;
font-size: 1.3em;
}
</style>
<script type="text/javascript">
var socket = io('/');
socket.on('connect', function() {
console.log("connect")
})
socket.on('boothState', function (data) {
BOB_getBooths();
})
$(document).ready(function() {
BOB_getBooths();
if( $(window).width() < 500 ) {
$('#idMain').css('width' ,'100%');
$('#idMenu-preview').css('width', 60 * $(window).width() / 100 )
} else {
$('#idMenu-preview').css('width', 500 )
}
$('#idMenu').click(function() {
$(this).fadeOut();
})
$('#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");
}
})
$('#idMenu-btnPrint').click(function() {
console.log(BOB_var_printer.todos, BOB_var_selectedBoothId)
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) {
if( pResponse.error ) {
console.log("Printer: ", pResponse.error)
alert("Echec ! Essayez d'envoyer une nouvelle demande")
} else {
alert("Demande prise en compte !\n\nVotre bobinogramme sera disponible près du Livre d'Or d'ici 1 heure")
}
})
}
} else {
alert("Une demande a déjà été envoyée.\nVérifiez près du Livre d'Or si votre bobinogramme ne s'y trouve pas.")
}
})
})
var BOB_var_lastBooth = "0";
var BOB_var_selectedBoothId = "";
var BOB_var_printer = {
todos : []
}
function BOB_getBooths() {
var boothList = $(".classBoothItem");
var jqBoothContainer = $('#idViewContainer');
$.get('/booth/toprint', function(pResponse) {
if( pResponse.error ) {
console.log('Printer: ', pResponse.error)
} else {
BOB_var_printer.todos = pResponse.todos
}
})
$.get('/booth/list/'+BOB_var_lastBooth, function (pResponse) {
if( pResponse.inProgress > 0 ) {
$('#idViewLoading').fadeIn();
} else {
$('#idViewLoading').fadeOut();
}
pResponse.booths.forEach(function (pBoothName) {
var jqImg = $('<img>');
var jqMenu = $('<div>');
var jqBtnDownload = $('<button>');
var jqBtnPrint = $('<button>');
jqImg.css('width', '100%');
jqImg.css('opacity', '0');
jqImg.css('transition', 'opacity 1s');
jqImg.data('booth', pBoothName);
jqImg.attr('src', "/booth/build/result/low/" + pBoothName);
jqImg.addClass('classBoothItem');
jqImg.on('click', function() {
BOB_var_selectedBoothId = pBoothName;
$('#idMenu-preview').attr('src', "/booth/build/result/low/" + pBoothName);
$('#idMenu').fadeIn();
})
jqImg.on('load', function() {
jqImg.css('opacity', '1');
})
if( pBoothName.localeCompare(BOB_var_lastBooth) > 0 ) {
BOB_var_lastBooth = pBoothName;
}
jqBoothContainer.prepend(jqImg);
})
})
console.log('-----------------');
}
</script>

226
server/views/scope.ejs

@ -0,0 +1,226 @@
<!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="" 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,
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').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) / <%= renderingTime %> * 100;
$('#idProgressBar').css('width', progress+'%');
if( progress < 100 ) {
BOB_lauchFakeProgressBar();
}
}, 500)
}
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_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', '/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

@ -0,0 +1,62 @@
<!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>

107
tools/tools-gphoto2.js

@ -0,0 +1,107 @@
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, pCallback) {
exec(`gphoto2 ${pArgs}`, function (pErr, pStdout, pStderr) {
if (pErr) {
console.log(`[!] Please install gphoto2`.red)
console.log(`sudo apt-get install gphoto2`)
process.exit(1)
}
let error = null
let outInLines = []
if (/Error/i.test(pStderr)) {
error = pStderr
}
if (pStdout) {
outInLines = splitLines(pStdout)
}
pCallback(error, outInLines)
})
}
tools.checkVersion = function (pCb) {
console.log('[-] Checking gphoto2 installation...'.bgBlue)
execGphoto2('--version', function (pErr, pStdout) {
console.log(`OK: ${pStdout[0]}`.green)
pCb()
})
}
tools.checkCameraConnection = function (pCb) {
console.log('[-] Checking camera connection...'.bgBlue)
execGphoto2('--auto-detect', function (pErr, pStdout) {
let device = pStdout[2]
if (!device) {
console.log(`[!] Camera not connected`.red)
console.log('1. Connect camera via USB')
console.log('2. Turn on the camera')
process.exit(1)
}
console.log(`OK: ${device}`.green)
pCb()
})
}
tools.__takeOnePicture = function (pPath, pCb) {
execGphoto2(`--capture-image-and-download --filename=${pPath} --force-overwrite`, pCb)
}
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])
mkdirp.sync(path.join(CFG.paths.prebuilt, pBoothId))
tools.__takeOnePicture(destPict, function (pErr) {
// If error occurs
if (pErr) return pCallback(pErr)
mkdirp(destPath, function () {
fs.copy(destPict, 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')
process.exit(1)
}
console.log(`OK: ${output}`.green)
pCb()
})
// opn(output)
// .then(function () {
// console.log('OK'.green)
// pCb()
// })
})
async.waterfall(tasks, pCb)
}
module.exports = tools

155
tools/tools-photobooth.js

@ -0,0 +1,155 @@
var gm = require('gm').subClass({ imageMagick: true })
var async = require('async')
var path = require('path')
var fs = require('fs')
var mkdirp = require('mkdirp')
var CFG = require('../config')
var tools = {}
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)
})
}
tools.buildBooth = function (pBoothId, pCallback) {
// Create output prebuild directory
mkdirp.sync(path.join(CFG.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)
console.log('Resizing ' + dstPict)
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)
pCb()
})
},
function (pErr) {
console.log('Building booth')
switch (CFG.style) {
default :
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)
var printedDate = new Date(parseInt(pBoothId))
outPict.fill(CFG.background)
outPict.drawRectangle(0, 0, CFG.booths[pType].resolution.width, CFG.booths[pType].resolution.height)
// Draw footer
outPict.draw('image Over 0,2100 1600,250 \'' + CFG.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) + '\''
// console.log(cmdDraw);
outPict.draw(cmdDraw)
})
outPict.fill('#666')
outPict.pointSize(20)
outPict.draw('text 1250,2350 "' + printedDate.toUTCString() + '"')
outPict.write(finalPict, function (pErr) {
if (pErr) {
console.log('Write outfile')
console.log(pErr)
pCallback(pErr)
} else {
gm(finalPict)
.resize(400, null)
.write(finalLdPict, function (pErr) {
pCallback(pErr)
})
}
})
}
module.exports = tools
tools.init = function () {
BOB_updateConfig()
}
// 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)
} else {
async.map(pFiles,
function (pFile, pCb) {
// Get name of template
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: []
}
// Get reslution of current template
gm(CFG.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)
}
pCb()
})
},
function (pErr) {
})
}
})
}
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)
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
})
})
console.log('INIT DONE')
}
Loading…
Cancel
Save