diff --git a/routes/screens/__init__.py b/routes/screens/__init__.py index adfe0646..6912fe9e 100644 --- a/routes/screens/__init__.py +++ b/routes/screens/__init__.py @@ -1 +1 @@ -from . import screens_new, screen_rename, screen_move, screen_delete, screen_edit, screen_set_aspect +from . import screens_new, screen_rename, screen_move, screen_delete, screen_edit, screen_set_aspect, screen_set_background_music diff --git a/routes/screens/screen_set_background_music.py b/routes/screens/screen_set_background_music.py new file mode 100644 index 00000000..8b8e5676 --- /dev/null +++ b/routes/screens/screen_set_background_music.py @@ -0,0 +1,15 @@ +from flask import request, jsonify + +import screens + +from core.flask_app import app + + +@app.route("/game//screens//background-music", methods=["POST"]) +def screen_set_background_music(slug, screen_id): + """Mémorise l'URL de musique de fond de cet écran, en AJAX — même patron + que screen_set_aspect.""" + body = request.get_json(silent=True) or {} + url = body.get("url", "") + screens.set_screen_background_music(slug, screen_id, url) + return jsonify({"ok": True}) diff --git a/screens/__init__.py b/screens/__init__.py index eecae938..c66966d6 100644 --- a/screens/__init__.py +++ b/screens/__init__.py @@ -71,6 +71,7 @@ from .screens_repo.delete_screen import delete_screen from .screens_repo.move_screen import move_screen from .screens_repo.screens_referencing import screens_referencing from .screens_repo.set_screen_aspect import set_screen_aspect +from .screens_repo.set_screen_background_music import set_screen_background_music from .elements.list_elements import list_elements from .elements.get_element import get_element diff --git a/screens/labels/action_type_labels.py b/screens/labels/action_type_labels.py index aa24a6a1..7200e82e 100644 --- a/screens/labels/action_type_labels.py +++ b/screens/labels/action_type_labels.py @@ -10,5 +10,6 @@ ACTION_TYPE_LABELS = { "attendre": "Attendre quelques secondes avant de continuer", "declencher_evenement": "Déclencher un événement", "ajouter_ligne": "Ajouter une ligne à un objet", + "jouer_son": "Jouer un son", "rien": "Ne rien faire", } diff --git a/screens/screens_repo/ensure_schema.py b/screens/screens_repo/ensure_schema.py index 49dff2bb..6ea6a26c 100644 --- a/screens/screens_repo/ensure_schema.py +++ b/screens/screens_repo/ensure_schema.py @@ -107,6 +107,13 @@ def ensure_schema(slug): # que la fenêtre du joueur n'a pas la même proportion que celle # utilisée à la conception. conn.execute("ALTER TABLE _screens ADD COLUMN preview_aspect TEXT NOT NULL DEFAULT '9/16'") + if "background_music_url" not in screen_cols: + # Phase 6 — musique de fond de cet écran (URL) : démarrée en boucle + # à l'affichage de l'écran, arrêtée au changement d'écran (voir + # runScreenBackgroundMusic() dans static/js/play/screens.js). + # Indépendante de l'action "Jouer un son" (effet sonore ponctuel, + # non bouclé, sur n'importe quel déclencheur). + conn.execute("ALTER TABLE _screens ADD COLUMN background_music_url TEXT") action_cols = {r["name"] for r in conn.execute("PRAGMA table_info(_actions)").fetchall()} for col_name, col_def in [ ("target_element_id", "INTEGER REFERENCES _screen_elements(id)"), diff --git a/screens/screens_repo/set_screen_background_music.py b/screens/screens_repo/set_screen_background_music.py new file mode 100644 index 00000000..f1c6eeec --- /dev/null +++ b/screens/screens_repo/set_screen_background_music.py @@ -0,0 +1,17 @@ +import db + +from .ensure_schema import ensure_schema + + +def set_screen_background_music(slug, screen_id, url): + """Mémorise l'URL de musique de fond de cet écran (voir le commentaire + sur la colonne background_music_url dans ensure_schema.py). Chaîne vide + -> NULL (pas de musique), comme un simple champ texte qu'on vide.""" + ensure_schema(slug) + conn = db.connect(slug) + conn.execute( + "UPDATE _screens SET background_music_url = ? WHERE id = ?", + (url or None, screen_id), + ) + conn.commit() + conn.close() diff --git a/static/js/play/actions.js b/static/js/play/actions.js index afddc87e..b4676890 100644 --- a/static/js/play/actions.js +++ b/static/js/play/actions.js @@ -220,6 +220,13 @@ function runActionNode(node) { window.lastInsertedRowId = json.row_id; return refreshRuntimeData(); }); + } else if (node.action_type === 'jouer_son') { + // Phase 6 — effet sonore ponctuel : non bouclé, indépendant de la + // musique de fond de l'écran (runScreenBackgroundMusic() dans + // screens.js, qui elle boucle et s'arrête au changement d'écran). + // Fire-and-forget : ne bloque jamais la suite du graphe. + if (node.data_value) new Audio(node.data_value).play().catch(function() {}); + return Promise.resolve(); } // 'rien' : aucune action volontairement. return Promise.resolve(); diff --git a/static/js/play/screens.js b/static/js/play/screens.js index cfc644ee..66cb69c7 100644 --- a/static/js/play/screens.js +++ b/static/js/play/screens.js @@ -41,6 +41,27 @@ function showScreen(id) { runScreenShowTriggers(id); runScreenTimerTriggers(id); runAnimationTimeline(id); + runScreenBackgroundMusic(id); +} + +// Phase 6 — musique de fond par écran (voir screen-bg-music-url dans +// screen_edit.html) : démarrée en boucle à l'affichage de l'écran, +// arrêtée au changement d'écran — un seul Audio actif à la fois (pas un +// tableau comme activeTimerTriggerIntervals dans triggers.js, une +// musique de fond ne se cumule jamais avec une autre). +var currentBackgroundAudio = null; + +function runScreenBackgroundMusic(screenId) { + if (currentBackgroundAudio) { + currentBackgroundAudio.pause(); + currentBackgroundAudio = null; + } + var screenData = screensData.find(function(s) { return String(s.id) === String(screenId); }); + var url = screenData && screenData.background_music_url; + if (!url) return; + currentBackgroundAudio = new Audio(url); + currentBackgroundAudio.loop = true; + currentBackgroundAudio.play().catch(function() {}); } // Voir le commentaire sur #playFrame dans le