Spaces:
Running
Running
Création des boutons
Browse files- jericho/main.py +114 -0
- jericho/projects/fish/signals.json +0 -195
- jericho/projects/fish/status.txt +0 -1
- jericho/projects/hugo/signals.json +0 -0
- jericho/projects/hugo/status.txt +0 -1
- jericho/static/index.html +28 -1
- jericho/static/main.js +28 -1
- jericho/static/style.css +18 -0
jericho/main.py
CHANGED
|
@@ -245,6 +245,8 @@ class Jericho(ReachyMiniApp):
|
|
| 245 |
self.antennas_enabled = True
|
| 246 |
self.sound_play_requested = False
|
| 247 |
self.play_robot_requested = False
|
|
|
|
|
|
|
| 248 |
self.current_commands = []
|
| 249 |
self.current_timestamps = []
|
| 250 |
self.current_project_name = None
|
|
@@ -291,6 +293,9 @@ class Jericho(ReachyMiniApp):
|
|
| 291 |
return len(commands)
|
| 292 |
|
| 293 |
def _set_playback_state(self, project_name: str | None, current_time: float, playing: bool, *, dirty: bool = True, stop_requested: bool = False):
|
|
|
|
|
|
|
|
|
|
| 294 |
with self.playback_lock:
|
| 295 |
self.playback_state.update({
|
| 296 |
"project_name": project_name,
|
|
@@ -415,6 +420,16 @@ class Jericho(ReachyMiniApp):
|
|
| 415 |
raise HTTPException(status_code=500, detail=f"Erreur de suppression: {str(e)}")
|
| 416 |
raise HTTPException(status_code=404, detail="Projet introuvable.")
|
| 417 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
@self.settings_app.post("/api/playback")
|
| 419 |
def sync_playback(payload: PlaybackPayload):
|
| 420 |
|
|
@@ -466,10 +481,109 @@ class Jericho(ReachyMiniApp):
|
|
| 466 |
except Exception as e:
|
| 467 |
raise HTTPException(status_code=500, detail=str(e))
|
| 468 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 469 |
def run(self, reachy_mini: ReachyMini, stop_event: threading.Event):
|
| 470 |
reachy_mini.goto_target(create_head_pose(), antennas=[0.0, 0.0], duration=1.0)
|
| 471 |
time.sleep(1.2)
|
| 472 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 473 |
while not stop_event.is_set():
|
| 474 |
with self.playback_lock:
|
| 475 |
commands_to_play = list(self.current_commands)
|
|
|
|
| 245 |
self.antennas_enabled = True
|
| 246 |
self.sound_play_requested = False
|
| 247 |
self.play_robot_requested = False
|
| 248 |
+
self.manual_emotion = None # émotion choisie via bouton
|
| 249 |
+
self.manual_emotion_lock = threading.Lock()
|
| 250 |
self.current_commands = []
|
| 251 |
self.current_timestamps = []
|
| 252 |
self.current_project_name = None
|
|
|
|
| 293 |
return len(commands)
|
| 294 |
|
| 295 |
def _set_playback_state(self, project_name: str | None, current_time: float, playing: bool, *, dirty: bool = True, stop_requested: bool = False):
|
| 296 |
+
if playing:
|
| 297 |
+
with self.manual_emotion_lock:
|
| 298 |
+
self.manual_emotion = None
|
| 299 |
with self.playback_lock:
|
| 300 |
self.playback_state.update({
|
| 301 |
"project_name": project_name,
|
|
|
|
| 420 |
raise HTTPException(status_code=500, detail=f"Erreur de suppression: {str(e)}")
|
| 421 |
raise HTTPException(status_code=404, detail="Projet introuvable.")
|
| 422 |
|
| 423 |
+
class EmotionPayload(BaseModel):
|
| 424 |
+
emotion: str | None = None # None ou "" = annuler
|
| 425 |
+
|
| 426 |
+
@self.settings_app.post("/api/emotion")
|
| 427 |
+
def set_emotion(payload: EmotionPayload):
|
| 428 |
+
emotion = (payload.emotion or "").strip().upper() or None
|
| 429 |
+
with self.manual_emotion_lock:
|
| 430 |
+
self.manual_emotion = emotion
|
| 431 |
+
return {"status": "ok", "emotion": emotion}
|
| 432 |
+
|
| 433 |
@self.settings_app.post("/api/playback")
|
| 434 |
def sync_playback(payload: PlaybackPayload):
|
| 435 |
|
|
|
|
| 481 |
except Exception as e:
|
| 482 |
raise HTTPException(status_code=500, detail=str(e))
|
| 483 |
|
| 484 |
+
def _run_manual_emotion_loop(self, reachy_mini: ReachyMini, stop_event: threading.Event):
|
| 485 |
+
"""Thread secondaire : anime les antennes selon l'émotion choisie via bouton."""
|
| 486 |
+
ANTENNA_MAX = np.deg2rad(120)
|
| 487 |
+
ANTENNA_MAX_alt = np.deg2rad(45)
|
| 488 |
+
ANTENNA_MAX_col = np.deg2rad(110)
|
| 489 |
+
COLERE_PERIOD = 5.0 # répétition colère toutes les 5 s
|
| 490 |
+
COLERE_ANIM = 1.0 # durée de l'animation colère (s)
|
| 491 |
+
|
| 492 |
+
current = None
|
| 493 |
+
t_start = time.time()
|
| 494 |
+
colere_last = 0.0
|
| 495 |
+
|
| 496 |
+
while not stop_event.is_set():
|
| 497 |
+
with self.manual_emotion_lock:
|
| 498 |
+
emotion = self.manual_emotion
|
| 499 |
+
with self.playback_lock:
|
| 500 |
+
video_playing = bool(self.playback_state.get("playing", False))
|
| 501 |
+
|
| 502 |
+
# Si une vidéo tourne, on laisse le thread principal gérer les antennes
|
| 503 |
+
if video_playing or emotion is None:
|
| 504 |
+
current = None
|
| 505 |
+
time.sleep(0.05)
|
| 506 |
+
continue
|
| 507 |
+
|
| 508 |
+
if emotion != current:
|
| 509 |
+
current = emotion
|
| 510 |
+
t_start = time.time()
|
| 511 |
+
colere_last = time.time() if emotion == "COLERE" else 0.0
|
| 512 |
+
|
| 513 |
+
t_state = time.time() - t_start
|
| 514 |
+
|
| 515 |
+
# Calcul des antennes selon l'émotion
|
| 516 |
+
if emotion == "COLERE":
|
| 517 |
+
elapsed_since_last = time.time() - colere_last
|
| 518 |
+
if elapsed_since_last >= COLERE_PERIOD:
|
| 519 |
+
colere_last = time.time()
|
| 520 |
+
elapsed_since_last = 0.0
|
| 521 |
+
|
| 522 |
+
anim_t = elapsed_since_last # temps dans le cycle
|
| 523 |
+
if anim_t < COLERE_ANIM:
|
| 524 |
+
# Animation colère
|
| 525 |
+
if anim_t < 0.15:
|
| 526 |
+
angle = -1.0 * (anim_t / 0.15) * ANTENNA_MAX_col
|
| 527 |
+
elif anim_t < 0.40:
|
| 528 |
+
angle = (-1.0 + 1.0 * ((anim_t - 0.15) / 0.15)) * ANTENNA_MAX_col
|
| 529 |
+
elif anim_t < 0.65:
|
| 530 |
+
angle = -1.0 * ((anim_t - 0.3) / 0.15) * ANTENNA_MAX_col
|
| 531 |
+
else:
|
| 532 |
+
angle = -1.0 * ANTENNA_MAX_col
|
| 533 |
+
left_ant = angle
|
| 534 |
+
right_ant = -angle
|
| 535 |
+
else:
|
| 536 |
+
# Entre les répétitions : position neutre
|
| 537 |
+
neutral_t = anim_t - COLERE_ANIM
|
| 538 |
+
angle = 1.0 * (1.0 - math.exp(-14.0 * neutral_t)) * ANTENNA_MAX_alt
|
| 539 |
+
left_ant = -(angle - 0.3)
|
| 540 |
+
right_ant = (angle - 0.3)
|
| 541 |
+
elif emotion == "JOIE":
|
| 542 |
+
oscillation = 0.5 * math.sin(2 * math.pi * 4.0 * t_state)
|
| 543 |
+
angle = (0.5 + oscillation) * ANTENNA_MAX_alt
|
| 544 |
+
left_ant = -angle
|
| 545 |
+
right_ant = angle
|
| 546 |
+
elif emotion == "CONTENT":
|
| 547 |
+
oscillation = 0.8 * math.sin(2 * math.pi * 0.5 * t_state)
|
| 548 |
+
angle = oscillation * ANTENNA_MAX_alt
|
| 549 |
+
left_ant = right_ant = angle
|
| 550 |
+
elif emotion == "SURPRISE":
|
| 551 |
+
angle = 1.0 * (1.0 - math.exp(-14.0 * t_state)) * ANTENNA_MAX_alt
|
| 552 |
+
left_ant = -(angle - 0.8)
|
| 553 |
+
right_ant = (angle - 0.8)
|
| 554 |
+
elif emotion == "TRISTESSE":
|
| 555 |
+
target = -1.0 * (1.0 - math.exp(-1.5 * t_state)) * ANTENNA_MAX
|
| 556 |
+
left_ant = target - 0.2
|
| 557 |
+
right_ant = -(target - 0.2)
|
| 558 |
+
elif emotion == "PEUR":
|
| 559 |
+
tremblement = 0.1 * math.sin(2 * math.pi * 15.0 * t_state)
|
| 560 |
+
angle = -1.0 * (1.0 - math.exp(-1.5 * t_state) + tremblement) * ANTENNA_MAX
|
| 561 |
+
left_ant = angle - 0.2
|
| 562 |
+
right_ant = -(angle - 0.2)
|
| 563 |
+
else: # NEUTRE
|
| 564 |
+
angle = 1.0 * (1.0 - math.exp(-14.0 * t_state)) * ANTENNA_MAX_alt
|
| 565 |
+
left_ant = -(angle - 0.3)
|
| 566 |
+
right_ant = (angle - 0.3)
|
| 567 |
+
|
| 568 |
+
clamp = lambda v: float(max(-ANTENNA_MAX, min(ANTENNA_MAX, v)))
|
| 569 |
+
try:
|
| 570 |
+
reachy_mini.set_target(antennas=[clamp(left_ant), clamp(right_ant)])
|
| 571 |
+
except Exception:
|
| 572 |
+
pass
|
| 573 |
+
|
| 574 |
+
time.sleep(0.02)
|
| 575 |
+
|
| 576 |
def run(self, reachy_mini: ReachyMini, stop_event: threading.Event):
|
| 577 |
reachy_mini.goto_target(create_head_pose(), antennas=[0.0, 0.0], duration=1.0)
|
| 578 |
time.sleep(1.2)
|
| 579 |
|
| 580 |
+
emotion_thread = threading.Thread(
|
| 581 |
+
target=self._run_manual_emotion_loop,
|
| 582 |
+
args=(reachy_mini, stop_event),
|
| 583 |
+
daemon=True,
|
| 584 |
+
)
|
| 585 |
+
emotion_thread.start()
|
| 586 |
+
|
| 587 |
while not stop_event.is_set():
|
| 588 |
with self.playback_lock:
|
| 589 |
commands_to_play = list(self.current_commands)
|
jericho/projects/fish/signals.json
DELETED
|
@@ -1,195 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"video": "C:\\Users\\hugov\\reachy_mini\\my_apps\\jericho\\jericho\\projects\\fish\\input.mp4",
|
| 3 |
-
"output_video": "C:\\Users\\hugov\\reachy_mini\\my_apps\\jericho\\jericho\\projects\\fish\\output_annotated.mp4",
|
| 4 |
-
"fps": 29.97,
|
| 5 |
-
"width": 256,
|
| 6 |
-
"height": 144,
|
| 7 |
-
"total_frames": 6812,
|
| 8 |
-
"output_total_frames": 7,
|
| 9 |
-
"skipped_no_face_frames": 6800,
|
| 10 |
-
"timeline": "compact_face_only",
|
| 11 |
-
"frames": [
|
| 12 |
-
{
|
| 13 |
-
"frame": 0,
|
| 14 |
-
"timestamp_s": 0.0,
|
| 15 |
-
"face_detected": true,
|
| 16 |
-
"face_cx": 0.5041,
|
| 17 |
-
"face_cy": 0.5993,
|
| 18 |
-
"face_w": 0.1426,
|
| 19 |
-
"face_h": 0.2724,
|
| 20 |
-
"bbox_xmin": 0.4328,
|
| 21 |
-
"bbox_ymin": 0.4631,
|
| 22 |
-
"bbox_xmax": 0.5754,
|
| 23 |
-
"bbox_ymax": 0.7355,
|
| 24 |
-
"yaw": 20.61,
|
| 25 |
-
"pitch": -5.88,
|
| 26 |
-
"roll": -24.4,
|
| 27 |
-
"ear_left": 0.3552,
|
| 28 |
-
"ear_right": 0.3423,
|
| 29 |
-
"mar": 0.4183,
|
| 30 |
-
"smile": 0.9454,
|
| 31 |
-
"brow_raise_left": 0.0973,
|
| 32 |
-
"brow_raise_right": 0.1283,
|
| 33 |
-
"brow_furrow": 0.3575,
|
| 34 |
-
"emotion": "CONTENT",
|
| 35 |
-
"original_frame": 362,
|
| 36 |
-
"original_timestamp_s": 12.0787
|
| 37 |
-
},
|
| 38 |
-
{
|
| 39 |
-
"frame": 1,
|
| 40 |
-
"timestamp_s": 0.0334,
|
| 41 |
-
"face_detected": true,
|
| 42 |
-
"face_cx": 0.4867,
|
| 43 |
-
"face_cy": 0.5864,
|
| 44 |
-
"face_w": 0.1723,
|
| 45 |
-
"face_h": 0.2947,
|
| 46 |
-
"bbox_xmin": 0.4005,
|
| 47 |
-
"bbox_ymin": 0.439,
|
| 48 |
-
"bbox_xmax": 0.5729,
|
| 49 |
-
"bbox_ymax": 0.7337,
|
| 50 |
-
"yaw": 19.71,
|
| 51 |
-
"pitch": -2.08,
|
| 52 |
-
"roll": -39.22,
|
| 53 |
-
"ear_left": 0.3216,
|
| 54 |
-
"ear_right": 0.3117,
|
| 55 |
-
"mar": 0.3816,
|
| 56 |
-
"smile": 0.7823,
|
| 57 |
-
"brow_raise_left": 0.0676,
|
| 58 |
-
"brow_raise_right": 0.1177,
|
| 59 |
-
"brow_furrow": 0.3827,
|
| 60 |
-
"emotion": "NEUTRE",
|
| 61 |
-
"original_frame": 2441,
|
| 62 |
-
"original_timestamp_s": 81.448
|
| 63 |
-
},
|
| 64 |
-
{
|
| 65 |
-
"frame": 2,
|
| 66 |
-
"timestamp_s": 0.0667,
|
| 67 |
-
"face_detected": true,
|
| 68 |
-
"face_cx": 0.4762,
|
| 69 |
-
"face_cy": 0.5905,
|
| 70 |
-
"face_w": 0.1745,
|
| 71 |
-
"face_h": 0.2462,
|
| 72 |
-
"bbox_xmin": 0.3889,
|
| 73 |
-
"bbox_ymin": 0.4674,
|
| 74 |
-
"bbox_xmax": 0.5634,
|
| 75 |
-
"bbox_ymax": 0.7136,
|
| 76 |
-
"yaw": 43.6,
|
| 77 |
-
"pitch": 9.84,
|
| 78 |
-
"roll": -60.06,
|
| 79 |
-
"ear_left": 0.3956,
|
| 80 |
-
"ear_right": 0.3608,
|
| 81 |
-
"mar": 0.3857,
|
| 82 |
-
"smile": 1.0639,
|
| 83 |
-
"brow_raise_left": 0.0065,
|
| 84 |
-
"brow_raise_right": 0.0484,
|
| 85 |
-
"brow_furrow": 0.5031,
|
| 86 |
-
"emotion": "JOIE",
|
| 87 |
-
"original_frame": 2501,
|
| 88 |
-
"original_timestamp_s": 83.45
|
| 89 |
-
},
|
| 90 |
-
{
|
| 91 |
-
"frame": 3,
|
| 92 |
-
"timestamp_s": 0.1001,
|
| 93 |
-
"face_detected": true,
|
| 94 |
-
"face_cx": 0.4605,
|
| 95 |
-
"face_cy": 0.5879,
|
| 96 |
-
"face_w": 0.1804,
|
| 97 |
-
"face_h": 0.2179,
|
| 98 |
-
"bbox_xmin": 0.3703,
|
| 99 |
-
"bbox_ymin": 0.4789,
|
| 100 |
-
"bbox_xmax": 0.5508,
|
| 101 |
-
"bbox_ymax": 0.6969,
|
| 102 |
-
"yaw": 51.33,
|
| 103 |
-
"pitch": 1.17,
|
| 104 |
-
"roll": -72.7,
|
| 105 |
-
"ear_left": 0.4808,
|
| 106 |
-
"ear_right": 0.5752,
|
| 107 |
-
"mar": 0.3477,
|
| 108 |
-
"smile": 0.8995,
|
| 109 |
-
"brow_raise_left": 0.0225,
|
| 110 |
-
"brow_raise_right": 0.0464,
|
| 111 |
-
"brow_furrow": 0.4005,
|
| 112 |
-
"emotion": "PEUR",
|
| 113 |
-
"original_frame": 5331,
|
| 114 |
-
"original_timestamp_s": 177.8777
|
| 115 |
-
},
|
| 116 |
-
{
|
| 117 |
-
"frame": 4,
|
| 118 |
-
"timestamp_s": 0.1335,
|
| 119 |
-
"face_detected": true,
|
| 120 |
-
"face_cx": 0.4809,
|
| 121 |
-
"face_cy": 0.604,
|
| 122 |
-
"face_w": 0.1627,
|
| 123 |
-
"face_h": 0.2473,
|
| 124 |
-
"bbox_xmin": 0.3996,
|
| 125 |
-
"bbox_ymin": 0.4803,
|
| 126 |
-
"bbox_xmax": 0.5622,
|
| 127 |
-
"bbox_ymax": 0.7276,
|
| 128 |
-
"yaw": 28.46,
|
| 129 |
-
"pitch": -2.9,
|
| 130 |
-
"roll": -43.45,
|
| 131 |
-
"ear_left": 0.3534,
|
| 132 |
-
"ear_right": 0.4038,
|
| 133 |
-
"mar": 0.4107,
|
| 134 |
-
"smile": 0.8494,
|
| 135 |
-
"brow_raise_left": 0.0586,
|
| 136 |
-
"brow_raise_right": 0.0748,
|
| 137 |
-
"brow_furrow": 0.4035,
|
| 138 |
-
"emotion": "NEUTRE",
|
| 139 |
-
"original_frame": 5662,
|
| 140 |
-
"original_timestamp_s": 188.9221
|
| 141 |
-
},
|
| 142 |
-
{
|
| 143 |
-
"frame": 5,
|
| 144 |
-
"timestamp_s": 0.1668,
|
| 145 |
-
"face_detected": true,
|
| 146 |
-
"face_cx": 0.4915,
|
| 147 |
-
"face_cy": 0.6195,
|
| 148 |
-
"face_w": 0.1447,
|
| 149 |
-
"face_h": 0.2324,
|
| 150 |
-
"bbox_xmin": 0.4192,
|
| 151 |
-
"bbox_ymin": 0.5033,
|
| 152 |
-
"bbox_xmax": 0.5639,
|
| 153 |
-
"bbox_ymax": 0.7357,
|
| 154 |
-
"yaw": 27.66,
|
| 155 |
-
"pitch": -3.79,
|
| 156 |
-
"roll": -36.34,
|
| 157 |
-
"ear_left": 0.3412,
|
| 158 |
-
"ear_right": 0.448,
|
| 159 |
-
"mar": 0.3767,
|
| 160 |
-
"smile": 0.764,
|
| 161 |
-
"brow_raise_left": 0.0665,
|
| 162 |
-
"brow_raise_right": 0.099,
|
| 163 |
-
"brow_furrow": 0.3344,
|
| 164 |
-
"emotion": "NEUTRE",
|
| 165 |
-
"original_frame": 6053,
|
| 166 |
-
"original_timestamp_s": 201.9684
|
| 167 |
-
},
|
| 168 |
-
{
|
| 169 |
-
"frame": 6,
|
| 170 |
-
"timestamp_s": 0.2002,
|
| 171 |
-
"face_detected": true,
|
| 172 |
-
"face_cx": 0.4813,
|
| 173 |
-
"face_cy": 0.5952,
|
| 174 |
-
"face_w": 0.1594,
|
| 175 |
-
"face_h": 0.2209,
|
| 176 |
-
"bbox_xmin": 0.4016,
|
| 177 |
-
"bbox_ymin": 0.4848,
|
| 178 |
-
"bbox_xmax": 0.561,
|
| 179 |
-
"bbox_ymax": 0.7057,
|
| 180 |
-
"yaw": 45.55,
|
| 181 |
-
"pitch": 5.15,
|
| 182 |
-
"roll": -64.75,
|
| 183 |
-
"ear_left": 0.3412,
|
| 184 |
-
"ear_right": 0.4439,
|
| 185 |
-
"mar": 0.3823,
|
| 186 |
-
"smile": 0.8951,
|
| 187 |
-
"brow_raise_left": 0.0063,
|
| 188 |
-
"brow_raise_right": 0.0339,
|
| 189 |
-
"brow_furrow": 0.4389,
|
| 190 |
-
"emotion": "NEUTRE",
|
| 191 |
-
"original_frame": 6777,
|
| 192 |
-
"original_timestamp_s": 226.1259
|
| 193 |
-
}
|
| 194 |
-
]
|
| 195 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
jericho/projects/fish/status.txt
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
ERROR: NO_FACE_DETECTED
|
|
|
|
|
|
jericho/projects/hugo/signals.json
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
jericho/projects/hugo/status.txt
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
DONE
|
|
|
|
|
|
jericho/static/index.html
CHANGED
|
@@ -10,7 +10,7 @@
|
|
| 10 |
<header>
|
| 11 |
<h1>Jericho projet : Reachy Mini</h1>
|
| 12 |
<div id="conteneurHeader">
|
| 13 |
-
<img src="
|
| 14 |
<div id="robot-status">Prêt</div>
|
| 15 |
</div>
|
| 16 |
</header>
|
|
@@ -51,6 +51,33 @@
|
|
| 51 |
</p>
|
| 52 |
</div>
|
| 53 |
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
</main>
|
| 55 |
|
| 56 |
<script src="/static/main.js"></script>
|
|
|
|
| 10 |
<header>
|
| 11 |
<h1>Jericho projet : Reachy Mini</h1>
|
| 12 |
<div id="conteneurHeader">
|
| 13 |
+
<img src="static/images/logoReachyMini.png" alt="Photo de Reachy" width="10%" />
|
| 14 |
<div id="robot-status">Prêt</div>
|
| 15 |
</div>
|
| 16 |
</header>
|
|
|
|
| 51 |
</p>
|
| 52 |
</div>
|
| 53 |
</section>
|
| 54 |
+
|
| 55 |
+
<section class="card">
|
| 56 |
+
<h2>Les émotions</h2>
|
| 57 |
+
<div id="listeEmotions">
|
| 58 |
+
<button id="joie">
|
| 59 |
+
Joie
|
| 60 |
+
</button>
|
| 61 |
+
<button id="content">
|
| 62 |
+
Content
|
| 63 |
+
</button>
|
| 64 |
+
<button id="surprise">
|
| 65 |
+
Surprise
|
| 66 |
+
</button>
|
| 67 |
+
<button id="colere">
|
| 68 |
+
Colère
|
| 69 |
+
</button>
|
| 70 |
+
<button id="peur">
|
| 71 |
+
Peur
|
| 72 |
+
</button>
|
| 73 |
+
<button id="tristesse">
|
| 74 |
+
Tristesse
|
| 75 |
+
</button>
|
| 76 |
+
<button id="neutre">
|
| 77 |
+
Neutre
|
| 78 |
+
</button>
|
| 79 |
+
</div>
|
| 80 |
+
</section>
|
| 81 |
</main>
|
| 82 |
|
| 83 |
<script src="/static/main.js"></script>
|
jericho/static/main.js
CHANGED
|
@@ -70,6 +70,25 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 70 |
if (currentLoadedProject) videoPlayer.play();
|
| 71 |
};
|
| 72 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
});
|
| 74 |
|
| 75 |
document.getElementById('import-btn').onclick = function() {
|
|
@@ -208,6 +227,14 @@ window.loadVideoPlayer = async function(name, url) {
|
|
| 208 |
await sendPlayback('stop', { projectName: currentLoadedProject, playing: false });
|
| 209 |
}
|
| 210 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
currentLoadedProject = name;
|
| 212 |
document.getElementById('player-section').classList.remove('hidden');
|
| 213 |
document.getElementById('current-video-title').textContent = 'Projet : ' + name;
|
|
@@ -254,4 +281,4 @@ videoPlayer.addEventListener('ended', () => {
|
|
| 254 |
robotStatus.textContent = 'Terminé';
|
| 255 |
sendPlayback('stop', { playing: false });
|
| 256 |
stopSyncLoop();
|
| 257 |
-
});
|
|
|
|
| 70 |
if (currentLoadedProject) videoPlayer.play();
|
| 71 |
};
|
| 72 |
}
|
| 73 |
+
|
| 74 |
+
// Boutons d'émotions
|
| 75 |
+
const emotionIds = ['joie', 'content', 'surprise', 'colere', 'peur', 'tristesse', 'neutre'];
|
| 76 |
+
emotionIds.forEach(id => {
|
| 77 |
+
const el = document.getElementById(id);
|
| 78 |
+
if (!el) return;
|
| 79 |
+
el.addEventListener('click', async () => {
|
| 80 |
+
// Highlight du bouton actif
|
| 81 |
+
emotionIds.forEach(eid => document.getElementById(eid)?.classList.remove('active-emotion'));
|
| 82 |
+
el.classList.add('active-emotion');
|
| 83 |
+
|
| 84 |
+
await fetch('/api/emotion', {
|
| 85 |
+
method: 'POST',
|
| 86 |
+
headers: { 'Content-Type': 'application/json' },
|
| 87 |
+
body: JSON.stringify({ emotion: id.toUpperCase() })
|
| 88 |
+
});
|
| 89 |
+
robotStatus.textContent = `Émotion : ${el.textContent.trim()}`;
|
| 90 |
+
});
|
| 91 |
+
});
|
| 92 |
});
|
| 93 |
|
| 94 |
document.getElementById('import-btn').onclick = function() {
|
|
|
|
| 227 |
await sendPlayback('stop', { projectName: currentLoadedProject, playing: false });
|
| 228 |
}
|
| 229 |
|
| 230 |
+
// Annuler l'émotion manuelle active
|
| 231 |
+
document.querySelectorAll('#listeEmotions button').forEach(b => b.classList.remove('active-emotion'));
|
| 232 |
+
await fetch('/api/emotion', {
|
| 233 |
+
method: 'POST',
|
| 234 |
+
headers: { 'Content-Type': 'application/json' },
|
| 235 |
+
body: JSON.stringify({ emotion: null })
|
| 236 |
+
});
|
| 237 |
+
|
| 238 |
currentLoadedProject = name;
|
| 239 |
document.getElementById('player-section').classList.remove('hidden');
|
| 240 |
document.getElementById('current-video-title').textContent = 'Projet : ' + name;
|
|
|
|
| 281 |
robotStatus.textContent = 'Terminé';
|
| 282 |
sendPlayback('stop', { playing: false });
|
| 283 |
stopSyncLoop();
|
| 284 |
+
});
|
jericho/static/style.css
CHANGED
|
@@ -122,3 +122,21 @@ input[type="text"], input[type="number"] {
|
|
| 122 |
color: #74424a;
|
| 123 |
margin-bottom: 0;
|
| 124 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
color: #74424a;
|
| 123 |
margin-bottom: 0;
|
| 124 |
}
|
| 125 |
+
|
| 126 |
+
#listeEmotions{
|
| 127 |
+
button{
|
| 128 |
+
color : #a34952;
|
| 129 |
+
background-color: white;
|
| 130 |
+
font-family:'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
|
| 131 |
+
font-weight: lighter;
|
| 132 |
+
border-color: #ffffff;
|
| 133 |
+
border-radius: 10%;
|
| 134 |
+
margin-top : auto;
|
| 135 |
+
margin-bottom: auto;
|
| 136 |
+
padding-top : 0.7%;
|
| 137 |
+
padding-bottom : 0.7%;
|
| 138 |
+
font-size: 1.3em;
|
| 139 |
+
margin-right : 2.5%;
|
| 140 |
+
width : 11%;
|
| 141 |
+
}
|
| 142 |
+
}
|