From 63286ab9461407d189875013e9b8d6922c6bd640 Mon Sep 17 00:00:00 2001 From: Jacques Supcik <jacques.supcik@hefr.ch> Date: Mon, 27 Jan 2025 16:28:14 +0100 Subject: [PATCH] Add score display and update video source paths; enhance Prettier configuration --- .prettierrc | 25 +++- html/css/site.css | 8 ++ html/index.html | 12 +- html/js/site.js | 328 +++++++++++++++++++++++++--------------------- justfile | 3 + test.http | 67 +++++++++- 6 files changed, 282 insertions(+), 161 deletions(-) diff --git a/.prettierrc b/.prettierrc index ab28f3e..459ab67 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,24 @@ { - "tabWidth": 4 -} \ No newline at end of file + "tabWidth": 4, + "overrides": [ + { + "files": [ + "*.yml", + "*.yaml" + ], + "options": { + "tabWidth": 2 + } + }, + { + "files": [ + "*.js" + ], + "options": { + "tabWidth": 2, + "singleQuote": true, + "semi": false + } + } + ] +} diff --git a/html/css/site.css b/html/css/site.css index 57cafb8..f6e4bcf 100644 --- a/html/css/site.css +++ b/html/css/site.css @@ -134,6 +134,14 @@ body { text-align: center; } +#score { + font-size: 22pt; + font-weight: bold; + margin-top: 320px; + text-align: center; + display: none; +} + #gauges-container { z-index: 1; display: flex; diff --git a/html/index.html b/html/index.html index 2c1072c..4131b75 100644 --- a/html/index.html +++ b/html/index.html @@ -44,15 +44,21 @@ </div> <div id="animation-container" class="game-inside"> - + <video id="animation"> + <source src="" type="video/mp4"> + Your browser does not support the video tag. + </video> </div> - <div id="score-container" class="game-inside"> + <div id="score-container" class="game-inside"> + <div id="score"> + <span id="score-value">--</span> + </div> </div> </div> <div class="video-container"> <video id="video" autoplay muted loop> - <source src="/media/HEIA-FR-Panels-768x384-whith-videos.mp4" type="video/mp4"> + <source src="/movie/HEIA-FR-Panels-768x384-FR-DE.mp4" type="video/mp4"> Your browser does not support the video tag. </video> </div> diff --git a/html/js/site.js b/html/js/site.js index 2ac9d24..f363a83 100644 --- a/html/js/site.js +++ b/html/js/site.js @@ -1,190 +1,220 @@ -var startTime = Date.now(); -var interval = undefined; +let startTime = Date.now() // Used for chrono +let interval // Used for chrono +const durationActive = {} function play_video(obj) { - console.log("Playing video : ", obj); - video_id = obj.id || "video"; - const v = $(video_id); - - if (v.length == 0) { - console.log("Video element not found"); - return; - } - - if (obj.loop) { - v.attr("loop", "loop"); - } else { - v.removeAttr("loop"); - } - - v.find("source").attr("src", obj.src); - v[0].currentTime = 0; - v[0].load(); + console.log('Playing video : ', obj) + let video_id = '#video' + if ('id' in obj) { + video_id = '#' + obj['id'] + } + const v = $(video_id).first() + + const doPlay = function () { + v.attr('src', obj.src) + v.attr('currentTime', 0) + $.each(v, function (_, i) { + i.load() + }) if (obj.volume > 0) { - v[0].volume = obj.volume; - } - v[0].play(); - - fade_in = obj["fade-in"] || 0; - fade_out = obj["fade-out"] || 0; - duration = obj["duration"] || 0; - - if (fade_in > 0 && fade_out > 0 && duration > fade_in + fade_out) { - v.fadeIn(fade_in * 1000) - .delay((duration - fade_in - fade_out) * 1000) - .fadeOut(fade_out * 1000, function () { - v[0].pause(); - v[0].currentTime = 0; - }); - } else if (fade_in > 0 && fade_out > 0) { - v.fadeIn(fade_in * 1000).fadeOut(fade_out * 1000, function () { - v[0].pause(); - v[0].currentTime = 0; - }); - } else if (fade_in > 0) { - v.fadeIn(fade_in * 1000); + v.attr('volume', obj.volume) } + $.each(v, function (_, i) { + i.play() + }) + } + + const doStop = function () { + $.each(v, function (_, i) { + i.pause() + }) + v.attr('currentTime', 0) + } + + if (obj.loop) { + v.attr('loop', true) + } else { + v.removeAttr('loop') + } + + let fadeIn = 200 + if ('fade-in' in obj) fadeIn = obj['fade-in'] * 1000 + let prevFadeOut = 200 + if ('prev-fade-out' in obj) prevFadeOut = obj['prev-fade-out'] * 1000 + let fadeOut = 0 + if ('fade-out' in obj) fadeOut = obj['fade-out'] * 1000 + let duration = 0 + if ('duration' in obj) duration = obj.duration * 1000 + + if (durationActive[video_id] !== undefined) { + clearTimeout(durationActive[video_id]) + durationActive[video_id] = undefined + } + v.stop(true, true) + v.clearQueue() + + if (fadeOut > 0 && duration > fadeIn + fadeOut) { + v.fadeOut(prevFadeOut, doPlay).fadeIn(fadeIn) + durationActive[video_id] = setTimeout(() => { + v.fadeOut(fadeOut, function () { + doStop() + durationActive[video_id] = undefined + }) + }, duration - fadeIn - fadeOut) + } else if (fadeOut > 0) { + v.fadeOut(prevFadeOut, doPlay).fadeIn(fadeIn).fadeOut(fadeOut, doStop) + } else { + v.fadeOut(prevFadeOut, doPlay).fadeIn(fadeIn) + } } function stop_video(obj) { - console.log("Stopping video"); - video_id = obj.id || "video"; - const v = $(video_id); - - if (v.length == 0) { - console.log("Video element not found"); - return; - } - - fade_out = obj["fade-out"] || 0; - - if (fade_out > 0) { - v.fadeOut(fade_out * 1000, function () { - v.hide(); - v[0].pause(); - v[0].currentTime = 0; - }); - } else { - v.hide(); - v[0].pause(); - v[0].currentTime = 0; - } + console.log('Stopping video') + let video_id = '#video' + if ('id' in obj) { + video_id = '#' + obj['id'] + } + const v = $(video_id).first() + + const doStop = function () { + $.each(v, function (_, i) { + i.pause() + }) + v.attr('currentTime', 0) + } + + let fadeOut = 200 + if ('fade-out' in obj) fadeOut = obj['fade-out'] * 1000 + + if (durationActive[video_id] !== undefined) { + clearTimeout(durationActive[video_id]) + durationActive[video_id] = undefined + } + + v.fadeOut(fadeOut, doStop) } function start_game(obj) { - console.log("Starting game"); + console.log('Starting game') } // ----- Teams ----- function set_teams(teama, teamb) { - $("#team-left").html(teama); - $("#team-right").html(teamb); + $('#team-left').html(teama) + $('#team-right').html(teamb) } // ----- Gauges ----- function set_left_gauge(value) { - $("#gauge-left").height(value + "%"); - $("#level-left").html(value + "%"); + $('#gauge-left').height(value + '%') + $('#level-left').html(value + '%') } function set_right_gauge(value) { - $("#gauge-right").height(value + "%"); - $("#level-right").html(value + "%"); + $('#gauge-right').height(value + '%') + $('#level-right').html(value + '%') } // ----- Chrono ----- function start_chrono() { - startTime = Date.now(); - if (interval !== undefined) { - clearInterval(interval); - } - interval = setInterval(function () { - var elapsedTime = Date.now() - startTime; - $("#timer-value").html( - luxon.DateTime.fromMillis(elapsedTime).toFormat("mm:ss.uu") - ); - }); + startTime = Date.now() + if (interval !== undefined) { + clearInterval(interval) + } + interval = setInterval(function () { + const elapsedTime = Date.now() - startTime + $('#timer-value').html( + luxon.DateTime.fromMillis(elapsedTime).toFormat('mm:ss.uu') + ) + $('#score-value').html( + luxon.DateTime.fromMillis(elapsedTime).toFormat('mm:ss.uu') + ) + }) } function stop_chrono() { - if (interval !== undefined) { - clearInterval(interval); - } + if (interval !== undefined) { + clearInterval(interval) + } } function reset_chrono() { - stop_chrono(); - $("#timer-value").html(luxon.DateTime.fromMillis(0).toFormat("mm:ss.uu")); + stop_chrono() + $('#timer-value').html(luxon.DateTime.fromMillis(0).toFormat('mm:ss.uu')) + $('#score-value').html(luxon.DateTime.fromMillis(0).toFormat('mm:ss.uu')) } // MQTT client function init_client(broker_url, base_topic) { - console.log("Connecting to broker:", broker_url); - const client = mqtt.connect(broker_url); - - client.on("connect", function (connack) { - console.log("Connected to broker"); - console.log("Subscribing to topic:", base_topic + "#"); - client.subscribe(base_topic + "#", { qos: 1 }); - }); - - const v = $("#video"); - v[0].addEventListener("ended", (event) => { - client.publish(base_topic + "/video/ended", "true"); - }); - - client.on("message", function (topic, message) { - // console.log( - // "Received message:", - // topic.toString(), - // " : ", - // message.toString() - // ); - - var obj; - try { - obj = JSON.parse(message); - } catch (error) { - console.log("Error parsing message"); - obj = {}; - } - - // console.log("Parsed message:", obj); - switch (topic) { - case base_topic + "reload": - location.reload(true); - break; - case base_topic + "gauge/left": - set_left_gauge(obj.value); - break; - case base_topic + "gauge/right": - set_right_gauge(obj.value); - break; - case base_topic + "chrono/start": - start_chrono(); - break; - case base_topic + "chrono/stop": - stop_chrono(); - break; - case base_topic + "chrono/reset": - reset_chrono(); - break; - case base_topic + "video/play": - play_video(obj); - break; - case base_topic + "video/stop": - stop_video(obj); - break; - case base_topic + "game/start": - start_game(obj); - break; - default: - console.log("Unknown topic:", topic); - } - }); + console.log('Connecting to broker:', broker_url) + const client = mqtt.connect(broker_url) + + client.on('connect', function (connack) { + console.log('Connected to broker') + console.log('Subscribing to topic:', base_topic + '#') + client.subscribe(base_topic + '#', { qos: 1 }) + }) + + const v = $('#video') + v[0].addEventListener('ended', (event) => { + client.publish(base_topic + '/video/ended', 'true') + }) + + client.on('message', function (topic, message) { + // console.log( + // "Received message:", + // topic.toString(), + // " : ", + // message.toString() + // ); + + let obj + try { + obj = JSON.parse(message) + } catch (error) { + console.log('Error parsing message') + obj = {} + } + + // console.log("Parsed message:", obj); + switch (topic) { + case base_topic + 'reload': + location.reload(true) + break + case base_topic + 'gauge/left': + set_left_gauge(obj.value) + break + case base_topic + 'gauge/right': + set_right_gauge(obj.value) + break + case base_topic + 'chrono/start': + start_chrono() + break + case base_topic + 'chrono/stop': + stop_chrono() + break + case base_topic + 'chrono/reset': + reset_chrono() + break + case base_topic + 'video/play': + play_video(obj) + break + case base_topic + 'video/stop': + stop_video(obj) + break + case base_topic + 'score/show': + $('#score').fadeIn('fast') + break + case base_topic + 'score/hide': + $('#score').fadeOut('fast') + break + default: + console.log('Unknown topic:', topic) + } + }) } diff --git a/justfile b/justfile index 918c161..95df2e3 100644 --- a/justfile +++ b/justfile @@ -1,5 +1,8 @@ up: docker compose --file docker/compose-dev.yaml up --build --force-recreate +up-nginx: + docker compose --file docker/compose-dev.yaml up nginx --build --force-recreate + down: docker compose --file docker/compose-dev.yaml down \ No newline at end of file diff --git a/test.http b/test.http index 6f48d8a..30552d1 100644 --- a/test.http +++ b/test.http @@ -1,13 +1,66 @@ MQTT tcp://localhost:1883 -Topic: heia/ms/rpi/top/text/show +Topic: heiafr/ms/top/gauge/left -{"html":"HELLO", "fade-in": 5} -``` +{ + "value": 90 +} +### +MQTT tcp://localhost:1883 +Topic: heiafr/ms/top/gauge/right + +{ + "value": 70 +} +### +MQTT tcp://localhost:1883 +Topic: heiafr/ms/top/chrono/start + +1 +### +MQTT tcp://localhost:1883 +Topic: heiafr/ms/top/chrono/stop + +1 +### +MQTT tcp://localhost:1883 +Topic: heiafr/ms/top/chrono/reset + +1 +### +MQTT tcp://localhost:1883 +Topic: heiafr/ms/top/video/play + +{ + "id": "animation", + "src": "movie/Panel-256x384-COMPTEUR-START.mp4", + "duration": 6, + "fade-out": 1, + "fade-in": 0 +} +### +MQTT tcp://localhost:1883 +Topic: heiafr/ms/top/video/play + +{ + "id": "animation", + "src": "movie/Panel-256x384-WINNER-RED.mp4", + "fade-in": 1 +} +### +MQTT tcp://localhost:1883 +Topic: heiafr/ms/top/video/stop + +{ + "id": "animation", + "fade-out": 2 +} +### +MQTT tcp://localhost:1883 +Topic: heiafr/ms/top/score/show -``` http +1 ### MQTT tcp://localhost:1883 -Topic: heia/ms/rpi/top/text/hide +Topic: heiafr/ms/top/score/hide -{"fade-out": 5} -``` \ No newline at end of file +1 \ No newline at end of file -- GitLab