Surn commited on
Commit
4302853
·
1 Parent(s): d779f16

Fix audio looping and update version to 0.2.12

Browse files

Updated the version number in `__init__.py` to 0.2.12.
Added a `loop` argument to `_mount_background_audio` in `audio.py`
to control audio looping, defaulting to `True`. Adjusted the
function to dynamically set or remove the `loop` attribute.
Modified `_game_over_content` in `ui.py` to play congratulations
music once by setting `loop=False`. Refactored `_render_game_over`
to separate handling of congratulations and background music,
ensuring correct looping behavior. Removed unnecessary
`remount_background_audio` logic. Updated README.md with a
changelog entry for version 0.2.12, highlighting the fix for
music looping on the congratulations screen.

Files changed (4) hide show
  1. README.md +3 -0
  2. battlewords/__init__.py +1 -1
  3. battlewords/audio.py +23 -4
  4. battlewords/ui.py +14 -10
README.md CHANGED
@@ -106,6 +106,9 @@ docker run -p 8501:8501 battlewords
106
 
107
  ## Changelog
108
 
 
 
 
109
  -0.2.11
110
  - update timer to be live during gameplay, but reset with each action
111
  - compact design
 
106
 
107
  ## Changelog
108
 
109
+ -0.2.12
110
+ - fix music looping on congratulations screen
111
+
112
  -0.2.11
113
  - update timer to be live during gameplay, but reset with each action
114
  - compact design
battlewords/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.2.11"
2
  __all__ = ["models", "generator", "logic", "ui"]
 
1
+ __version__ = "0.2.12"
2
  __all__ = ["models", "generator", "logic", "ui"]
battlewords/audio.py CHANGED
@@ -34,8 +34,15 @@ def _load_audio_data_url(path: str) -> str:
34
  encoded = base64.b64encode(fp.read()).decode("ascii")
35
  return f"data:{mime};base64,{encoded}"
36
 
37
- def _mount_background_audio(enabled: bool, src_data_url: Optional[str], volume: float) -> None:
38
- """Create/update a single hidden <audio> element in the top page and play/pause it."""
 
 
 
 
 
 
 
39
  from streamlit.components.v1 import html as _html
40
 
41
  if not enabled or not src_data_url:
@@ -55,6 +62,8 @@ def _mount_background_audio(enabled: bool, src_data_url: Optional[str], volume:
55
 
56
  # Clamp volume
57
  vol = max(0.0, min(1.0, float(volume)))
 
 
58
  # Inject or update a single persistent audio element and make sure it starts after interaction if autoplay is blocked
59
  _html(
60
  f"""
@@ -66,10 +75,20 @@ def _mount_background_audio(enabled: bool, src_data_url: Optional[str], volume:
66
  audio = doc.createElement('audio');
67
  audio.id = 'bw-bg-audio';
68
  audio.style.display = 'none';
69
- audio.setAttribute('loop', '');
70
- audio.setAttribute('autoplay', '');
71
  doc.body.appendChild(audio);
72
  }}
 
 
 
 
 
 
 
 
 
 
 
 
73
  const newSrc = "{src_data_url}";
74
  if (audio.src !== newSrc) {{
75
  audio.src = newSrc;
 
34
  encoded = base64.b64encode(fp.read()).decode("ascii")
35
  return f"data:{mime};base64,{encoded}"
36
 
37
+ def _mount_background_audio(enabled: bool, src_data_url: Optional[str], volume: float, loop: bool = True) -> None:
38
+ """Create/update a single hidden <audio> element in the top page and play/pause it.
39
+
40
+ Args:
41
+ enabled: Whether the background audio should be active.
42
+ src_data_url: data: URL for the audio source.
43
+ volume: 0.0–1.0 volume level.
44
+ loop: Whether the audio should loop (default True).
45
+ """
46
  from streamlit.components.v1 import html as _html
47
 
48
  if not enabled or not src_data_url:
 
62
 
63
  # Clamp volume
64
  vol = max(0.0, min(1.0, float(volume)))
65
+ should_loop = "true" if loop else "false"
66
+
67
  # Inject or update a single persistent audio element and make sure it starts after interaction if autoplay is blocked
68
  _html(
69
  f"""
 
75
  audio = doc.createElement('audio');
76
  audio.id = 'bw-bg-audio';
77
  audio.style.display = 'none';
 
 
78
  doc.body.appendChild(audio);
79
  }}
80
+
81
+ // Ensure loop is explicitly set every time, even if element already exists
82
+ const shouldLoop = {should_loop};
83
+ audio.loop = shouldLoop;
84
+ if (shouldLoop) {{
85
+ audio.setAttribute('loop', '');
86
+ }} else {{
87
+ audio.removeAttribute('loop');
88
+ }}
89
+ audio.autoplay = true;
90
+ audio.setAttribute('autoplay', '');
91
+
92
  const newSrc = "{src_data_url}";
93
  if (audio.src !== newSrc) {{
94
  audio.src = newSrc;
battlewords/ui.py CHANGED
@@ -1094,10 +1094,9 @@ def _game_over_content(state: GameState) -> None:
1094
  congrats_music_path = os.path.join(music_dir, "congratulations.mp3")
1095
  if os.path.exists(congrats_music_path):
1096
  src_url = _load_audio_data_url(congrats_music_path)
1097
- # Play at full volume (or use previous music_volume setting)
1098
- _mount_background_audio(True, src_url, (st.session_state.get("music_volume", 100)) / 100)
1099
  else:
1100
- # Fallback: keep previous music or silence
1101
  _mount_background_audio(False, None, 0.0)
1102
 
1103
  # Set end_time if not already set
@@ -1208,20 +1207,25 @@ else:
1208
  _game_over_content(state)
1209
 
1210
  def _render_game_over(state: GameState):
1211
- # Determine visibility
1212
  visible = bool(st.session_state.get("show_gameover_overlay", True)) and is_game_over(state)
1213
- if not visible and st.session_state.get("remount_background_audio", False):
1214
- # Remount background audio when modal is dismissed (X or button)
1215
- music_dir = _get_music_dir()
 
 
 
 
 
 
 
 
 
1216
  background_path = os.path.join(music_dir, "background.mp3")
1217
  if os.path.exists(background_path):
1218
  src_url = _load_audio_data_url(background_path)
1219
  _mount_background_audio(True, src_url, (st.session_state.get("music_volume", 100)) / 100)
1220
  else:
1221
  _mount_background_audio(False, None, 0.0)
1222
- st.session_state["remount_background_audio"] = False # reset flag
1223
- if visible:
1224
- _game_over_dialog(state)
1225
 
1226
  def _sort_wordlist(filename):
1227
  import time # Add this import
 
1094
  congrats_music_path = os.path.join(music_dir, "congratulations.mp3")
1095
  if os.path.exists(congrats_music_path):
1096
  src_url = _load_audio_data_url(congrats_music_path)
1097
+ # Play once (no loop) at configured volume
1098
+ _mount_background_audio(True, src_url, (st.session_state.get("music_volume", 100)) / 100, loop=False)
1099
  else:
 
1100
  _mount_background_audio(False, None, 0.0)
1101
 
1102
  # Set end_time if not already set
 
1207
  _game_over_content(state)
1208
 
1209
  def _render_game_over(state: GameState):
 
1210
  visible = bool(st.session_state.get("show_gameover_overlay", True)) and is_game_over(state)
1211
+ music_dir = _get_music_dir()
1212
+ if visible:
1213
+ # Mount congratulations music (play once, do not loop)
1214
+ congrats_music_path = os.path.join(music_dir, "congratulations.mp3")
1215
+ if os.path.exists(congrats_music_path):
1216
+ src_url = _load_audio_data_url(congrats_music_path)
1217
+ _mount_background_audio(True, src_url, (st.session_state.get("music_volume", 100)) / 100, loop=False)
1218
+ else:
1219
+ _mount_background_audio(False, None, 0.0)
1220
+ _game_over_dialog(state)
1221
+ else:
1222
+ # Mount background music (loop by default)
1223
  background_path = os.path.join(music_dir, "background.mp3")
1224
  if os.path.exists(background_path):
1225
  src_url = _load_audio_data_url(background_path)
1226
  _mount_background_audio(True, src_url, (st.session_state.get("music_volume", 100)) / 100)
1227
  else:
1228
  _mount_background_audio(False, None, 0.0)
 
 
 
1229
 
1230
  def _sort_wordlist(filename):
1231
  import time # Add this import