Archime commited on
Commit
010fb88
·
1 Parent(s): a4c6261

add Managemen sessions

Browse files
Files changed (2) hide show
  1. app.py +44 -24
  2. app/session_utils.py +102 -0
app.py CHANGED
@@ -7,21 +7,32 @@ from pydub import AudioSegment
7
  import time
8
  import os
9
  import spaces
10
- import uuid
11
  from app.utils import generate_coturn_config
12
 
13
-
 
 
 
 
 
 
 
 
 
 
 
14
  EXAMPLE_FILES = ["data/bonjour.wav", "data/bonjour2.wav"]
15
  DEFAULT_FILE = EXAMPLE_FILES[0]
16
 
17
 
 
18
  @spaces.GPU
19
  def read_and_stream_audio(filepath_to_stream: str, session_id: str):
20
  """
21
  Stream audio chunks for a specific session.
22
  Stops when the corresponding stop flag file exists.
23
  """
24
- stop_file = f"/tmp/stream_stop_flag_{session_id}.txt"
25
  logging.debug(f"[{session_id}] read_and_stream_audio() started with file: {filepath_to_stream}")
26
 
27
  if not filepath_to_stream or not os.path.exists(filepath_to_stream):
@@ -34,9 +45,10 @@ def read_and_stream_audio(filepath_to_stream: str, session_id: str):
34
  return
35
 
36
  # Clear any leftover stop flag
37
- if os.path.exists(stop_file):
38
- os.remove(stop_file)
39
- logging.debug(f"[{session_id}] Old stop flag cleared.")
 
40
 
41
  try:
42
  segment = AudioSegment.from_file(filepath_to_stream)
@@ -46,7 +58,7 @@ def read_and_stream_audio(filepath_to_stream: str, session_id: str):
46
  for i, chunk in enumerate(segment[::chunk_ms]):
47
  if os.path.exists(stop_file):
48
  logging.info(f"[{session_id}] Stop flag detected at chunk {i+1}. Ending stream.")
49
- os.remove(stop_file)
50
  break
51
 
52
  iter_start = time.perf_counter()
@@ -72,34 +84,26 @@ def read_and_stream_audio(filepath_to_stream: str, session_id: str):
72
  logging.error(f"[{session_id}] Error during streaming: {e}", exc_info=True)
73
  raise
74
  finally:
75
- if os.path.exists(stop_file):
76
- os.remove(stop_file)
77
- logging.debug(f"[{session_id}] Stop file cleaned up in finally block.")
78
  logging.debug(f"[{session_id}] Exiting read_and_stream_audio().")
79
 
80
 
 
81
  def stop_streaming(session_id: str):
82
- """Create the stop flag file for this user's session."""
83
- stop_file = f"/tmp/stream_stop_flag_{session_id}.txt"
84
- with open(stop_file, "w") as f:
85
- f.write("1")
86
- logging.info(f"[{session_id}] Stop button clicked → stop flag file created.")
87
  return None
88
 
89
 
90
- def generate_session_id():
91
- sid = str(uuid.uuid4())
92
- logging.debug(f"[{sid}] New session created.")
93
- return sid
94
-
95
-
96
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
97
  gr.Markdown(
98
  "## Application 'Streamer' WebRTC (multi-utilisateur)\n"
99
- "Chaque utilisateur contrôle son propre flux audio."
100
  )
101
 
102
- # Each user gets their own session ID (unique per tab)
103
  session_id = gr.State(value=generate_session_id)
104
  active_filepath = gr.State(value=DEFAULT_FILE)
105
 
@@ -128,12 +132,14 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
128
  with gr.Column():
129
  gr.Text()
130
 
 
131
  def set_new_file(filepath):
132
  return filepath if filepath else DEFAULT_FILE
133
 
134
  main_audio.change(fn=set_new_file, inputs=[main_audio], outputs=[active_filepath])
135
  main_audio.stop_recording(fn=set_new_file, inputs=[main_audio], outputs=[active_filepath])
136
 
 
137
  def start_streaming_ui(session_id):
138
  logging.debug(f"[{session_id}] UI: Start clicked → disabling controls.")
139
  return {
@@ -159,7 +165,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
159
  ui_components = [start_button, stop_button, main_audio]
160
 
161
  # --- Streaming event ---
162
- stream_event = webrtc_stream.stream(
163
  fn=read_and_stream_audio,
164
  inputs=[active_filepath, session_id],
165
  outputs=[webrtc_stream],
@@ -185,6 +191,20 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
185
  outputs=ui_components,
186
  )
187
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
  if __name__ == "__main__":
190
  demo.queue(max_size=50, api_open=False).launch(show_api=False, debug=True)
 
7
  import time
8
  import os
9
  import spaces
 
10
  from app.utils import generate_coturn_config
11
 
12
+ # ✅ Import des fonctions externalisées
13
+ from app.session_utils import (
14
+ generate_session_id,
15
+ register_session,
16
+ unregister_session,
17
+ get_active_sessions,
18
+ stop_file_path,
19
+ create_stop_flag,
20
+ clear_stop_flag,
21
+ )
22
+
23
+ # --- Constants ---
24
  EXAMPLE_FILES = ["data/bonjour.wav", "data/bonjour2.wav"]
25
  DEFAULT_FILE = EXAMPLE_FILES[0]
26
 
27
 
28
+ # --- Streaming logic ---
29
  @spaces.GPU
30
  def read_and_stream_audio(filepath_to_stream: str, session_id: str):
31
  """
32
  Stream audio chunks for a specific session.
33
  Stops when the corresponding stop flag file exists.
34
  """
35
+ stop_file = stop_file_path(session_id)
36
  logging.debug(f"[{session_id}] read_and_stream_audio() started with file: {filepath_to_stream}")
37
 
38
  if not filepath_to_stream or not os.path.exists(filepath_to_stream):
 
45
  return
46
 
47
  # Clear any leftover stop flag
48
+ clear_stop_flag(session_id)
49
+
50
+ # Register session
51
+ register_session(session_id, filepath_to_stream)
52
 
53
  try:
54
  segment = AudioSegment.from_file(filepath_to_stream)
 
58
  for i, chunk in enumerate(segment[::chunk_ms]):
59
  if os.path.exists(stop_file):
60
  logging.info(f"[{session_id}] Stop flag detected at chunk {i+1}. Ending stream.")
61
+ clear_stop_flag(session_id)
62
  break
63
 
64
  iter_start = time.perf_counter()
 
84
  logging.error(f"[{session_id}] Error during streaming: {e}", exc_info=True)
85
  raise
86
  finally:
87
+ unregister_session(session_id)
88
+ clear_stop_flag(session_id)
 
89
  logging.debug(f"[{session_id}] Exiting read_and_stream_audio().")
90
 
91
 
92
+ # --- Stop Streaming ---
93
  def stop_streaming(session_id: str):
94
+ create_stop_flag(session_id)
95
+ logging.info(f"[{session_id}] Stop button clicked → stop flag created.")
 
 
 
96
  return None
97
 
98
 
99
+ # --- Gradio UI ---
 
 
 
 
 
100
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
101
  gr.Markdown(
102
  "## Application 'Streamer' WebRTC (multi-utilisateur)\n"
103
+ "Chaque utilisateur contrôle son propre flux audio, avec suivi des sessions actives."
104
  )
105
 
106
+ # State
107
  session_id = gr.State(value=generate_session_id)
108
  active_filepath = gr.State(value=DEFAULT_FILE)
109
 
 
132
  with gr.Column():
133
  gr.Text()
134
 
135
+ # --- Audio selection ---
136
  def set_new_file(filepath):
137
  return filepath if filepath else DEFAULT_FILE
138
 
139
  main_audio.change(fn=set_new_file, inputs=[main_audio], outputs=[active_filepath])
140
  main_audio.stop_recording(fn=set_new_file, inputs=[main_audio], outputs=[active_filepath])
141
 
142
+ # --- UI state updates ---
143
  def start_streaming_ui(session_id):
144
  logging.debug(f"[{session_id}] UI: Start clicked → disabling controls.")
145
  return {
 
165
  ui_components = [start_button, stop_button, main_audio]
166
 
167
  # --- Streaming event ---
168
+ webrtc_stream.stream(
169
  fn=read_and_stream_audio,
170
  inputs=[active_filepath, session_id],
171
  outputs=[webrtc_stream],
 
191
  outputs=ui_components,
192
  )
193
 
194
+ # --- Sessions Table ---
195
+ with gr.Accordion("📊 Sessions actives", open=False):
196
+ sessions_table = gr.DataFrame(
197
+ headers=["session_id", "file", "start_time", "status"],
198
+ interactive=False,
199
+ wrap=True,
200
+ label="Utilisateurs connectés",
201
+ max_height=200,
202
+ )
203
+
204
+ # ✅ Timer pour mise à jour automatique du tableau
205
+ timer = gr.Timer(3.0)
206
+ timer.tick(fn=get_active_sessions, outputs=sessions_table)
207
+
208
 
209
  if __name__ == "__main__":
210
  demo.queue(max_size=50, api_open=False).launch(show_api=False, debug=True)
app/session_utils.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import uuid
4
+ from datetime import datetime
5
+ from app.logger_config import logger as logging
6
+
7
+ ACTIVE_SESSIONS_FILE = "/tmp/active_sessions.json"
8
+
9
+
10
+ # --- ID Management ---
11
+ def generate_session_id() -> str:
12
+ """Génère un identifiant unique de session."""
13
+ sid = str(uuid.uuid4())
14
+ logging.debug(f"[{sid}] New session created.")
15
+ return sid
16
+
17
+
18
+ # --- Active Session Registry ---
19
+ def register_session(session_id: str, filepath: str):
20
+ """Ajoute une session au registre."""
21
+ data = {}
22
+ if os.path.exists(ACTIVE_SESSIONS_FILE):
23
+ with open(ACTIVE_SESSIONS_FILE, "r") as f:
24
+ try:
25
+ data = json.load(f)
26
+ except Exception:
27
+ data = {}
28
+
29
+ data[session_id] = {
30
+ "session_id": session_id,
31
+ "file": filepath,
32
+ "start_time": datetime.utcnow().strftime("%H:%M:%S"),
33
+ "status": "active",
34
+ }
35
+
36
+ with open(ACTIVE_SESSIONS_FILE, "w") as f:
37
+ json.dump(data, f)
38
+
39
+ logging.debug(f"[{session_id}] Registered session in active_sessions.json.")
40
+
41
+
42
+ def unregister_session(session_id: str):
43
+ """Retire une session du registre."""
44
+ if not os.path.exists(ACTIVE_SESSIONS_FILE):
45
+ return
46
+
47
+ try:
48
+ with open(ACTIVE_SESSIONS_FILE, "r") as f:
49
+ data = json.load(f)
50
+ if session_id in data:
51
+ data.pop(session_id)
52
+ with open(ACTIVE_SESSIONS_FILE, "w") as f:
53
+ json.dump(data, f)
54
+ logging.debug(f"[{session_id}] Unregistered session.")
55
+ except Exception as e:
56
+ logging.error(f"[{session_id}] Error unregistering session: {e}")
57
+
58
+
59
+ def get_active_sessions():
60
+ """Retourne les sessions actives sous forme de tableau pour le DataFrame."""
61
+ if not os.path.exists(ACTIVE_SESSIONS_FILE):
62
+ return []
63
+
64
+ try:
65
+ with open(ACTIVE_SESSIONS_FILE, "r") as f:
66
+ data = json.load(f)
67
+
68
+ rows = []
69
+ for session in data.values():
70
+ rows.append([
71
+ session.get("session_id", ""),
72
+ session.get("file", ""),
73
+ session.get("start_time", ""),
74
+ session.get("status", ""),
75
+ ])
76
+ return rows
77
+
78
+ except Exception as e:
79
+ logging.error(f"Erreur lecture sessions actives: {e}")
80
+ return []
81
+
82
+
83
+ # --- Stop Flag Management ---
84
+ def stop_file_path(session_id: str) -> str:
85
+ """Retourne le chemin du fichier stop d'une session."""
86
+ return f"/tmp/stream_stop_flag_{session_id}.txt"
87
+
88
+
89
+ def create_stop_flag(session_id: str):
90
+ """Crée le fichier de stop pour cette session."""
91
+ path = stop_file_path(session_id)
92
+ with open(path, "w") as f:
93
+ f.write("1")
94
+ logging.info(f"[{session_id}] Stop flag file created at {path}.")
95
+
96
+
97
+ def clear_stop_flag(session_id: str):
98
+ """Supprime le fichier de stop s'il existe."""
99
+ path = stop_file_path(session_id)
100
+ if os.path.exists(path):
101
+ os.remove(path)
102
+ logging.debug(f"[{session_id}] Stop flag cleared.")