Danisz commited on
Commit
b6110c3
·
1 Parent(s): 7dc9c1f

Upload blob

Browse files
Files changed (2) hide show
  1. .gitignore +4 -0
  2. app.py +166 -22
.gitignore CHANGED
@@ -1 +1,5 @@
1
  .venv
 
 
 
 
 
1
  .venv
2
+ playground/
3
+ uploaded_files/
4
+ assets/
5
+ __pycache__/
app.py CHANGED
@@ -1,14 +1,20 @@
 
 
1
  import flet as ft
2
- import tempfile
3
  import sys
4
- import base64
5
  import logging
 
 
 
 
 
6
 
7
  # Set up basic logging configuration
8
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
9
  logger = logging.getLogger(__name__)
10
 
11
- def main(page: ft.Page):
12
  page.title = "Simple Audio Recorder"
13
 
14
  # Log the operating system platform
@@ -26,8 +32,6 @@ def main(page: ft.Page):
26
 
27
  page.update()
28
 
29
- temp_recording_file = None
30
-
31
  def start_recording(e):
32
  record_button.disabled = True
33
  stop_button.disabled = True
@@ -36,33 +40,33 @@ def main(page: ft.Page):
36
  recording_status.value = "Preparing to record..."
37
  page.update()
38
 
39
- nonlocal temp_recording_file
40
- temp_recording_file = tempfile.NamedTemporaryFile(delete=False, suffix=".wav")
41
- logger.info(f"Starting recording to {temp_recording_file.name}")
42
-
43
- audio_recorder.start_recording(temp_recording_file.name, wait_timeout=2)
44
 
45
  # The actual recording start will be handled in update_recording_status
46
 
47
- def stop_recording(e):
48
  record_button.disabled = False
49
  stop_button.disabled = True
50
  play_button.disabled = False
51
 
52
  page.update()
53
  nonlocal audio_player
54
- result_uri = audio_recorder.stop_recording()
55
  logger.info("Result URI: %s", result_uri)
56
-
 
 
 
57
  # Create audio_player after recording is complete
58
- if temp_recording_file:
59
- with open(temp_recording_file.name, "rb") as audio_file:
60
- audio_base64 = base64.b64encode(audio_file.read()).decode('utf-8')
61
- audio_data_uri = f"data:audio/wav;base64,{audio_base64}"
62
- audio_player = ft.Audio(src=result_uri)
63
- page.overlay.append(audio_player)
64
 
65
  page.update()
 
 
 
 
66
 
67
  def play_recording(e):
68
  if audio_player:
@@ -78,7 +82,7 @@ def main(page: ft.Page):
78
  page.update()
79
 
80
  record_button = ft.ElevatedButton("Start Recording", on_click=start_recording)
81
- stop_button = ft.ElevatedButton("Stop Recording", on_click=stop_recording, disabled=True)
82
  play_button = ft.ElevatedButton("Play Recording", on_click=play_recording, disabled=True)
83
 
84
  def handle_list_devices(e):
@@ -106,4 +110,144 @@ def main(page: ft.Page):
106
 
107
  page.update()
108
 
109
- app = ft.app(target=main, export_asgi_app=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from contextlib import asynccontextmanager
2
+
3
  import flet as ft
4
+ import flet.fastapi as flet_fastapi
5
  import sys
 
6
  import logging
7
+ import os
8
+ from fastapi import FastAPI, File, UploadFile, HTTPException, Form
9
+ from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
10
+ from collections import OrderedDict
11
+ import base64
12
 
13
  # Set up basic logging configuration
14
+ logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
15
  logger = logging.getLogger(__name__)
16
 
17
+ async def main(page: ft.Page):
18
  page.title = "Simple Audio Recorder"
19
 
20
  # Log the operating system platform
 
32
 
33
  page.update()
34
 
 
 
35
  def start_recording(e):
36
  record_button.disabled = True
37
  stop_button.disabled = True
 
40
  recording_status.value = "Preparing to record..."
41
  page.update()
42
 
43
+ logger.info("Starting recording")
44
+ audio_recorder.start_recording("")
 
 
 
45
 
46
  # The actual recording start will be handled in update_recording_status
47
 
48
+ async def stop_recording_flet(e):
49
  record_button.disabled = False
50
  stop_button.disabled = True
51
  play_button.disabled = False
52
 
53
  page.update()
54
  nonlocal audio_player
55
+ result_uri = await audio_recorder.stop_recording_async()
56
  logger.info("Result URI: %s", result_uri)
57
+
58
+ logger.info("="*100)
59
+ logger.info(f"Session ID: {page.session_id}")
60
+
61
  # Create audio_player after recording is complete
62
+ audio_player = ft.Audio(src=result_uri)
63
+ page.overlay.append(audio_player)
 
 
 
 
64
 
65
  page.update()
66
+ logger.info(page.controls)
67
+
68
+ # Use launch_url to load the upload page in the hidden iframe
69
+ page.launch_url(f"/upload-blob/?blob_uri={result_uri}&session_id={page.session_id}", web_window_name="hidden-iframe")
70
 
71
  def play_recording(e):
72
  if audio_player:
 
82
  page.update()
83
 
84
  record_button = ft.ElevatedButton("Start Recording", on_click=start_recording)
85
+ stop_button = ft.ElevatedButton("Stop Recording", on_click=stop_recording_flet, disabled=True)
86
  play_button = ft.ElevatedButton("Play Recording", on_click=play_recording, disabled=True)
87
 
88
  def handle_list_devices(e):
 
110
 
111
  page.update()
112
 
113
+
114
+ @asynccontextmanager
115
+ async def lifespan(app: FastAPI):
116
+ await flet_fastapi.app_manager.start()
117
+ yield
118
+ await flet_fastapi.app_manager.shutdown()
119
+
120
+
121
+ app = FastAPI(lifespan=lifespan)
122
+
123
+ app.mount("/flet-app", flet_fastapi.app(main))
124
+
125
+
126
+ # Directory where files will be saved (current working directory)
127
+ UPLOAD_DIRECTORY = os.path.join(os.getcwd(), "uploaded_files")
128
+
129
+ @app.get("/", response_class=HTMLResponse)
130
+ async def serve_main_page():
131
+ html_content = """
132
+ <!DOCTYPE html>
133
+ <html lang="en">
134
+ <head>
135
+ <meta charset="UTF-8">
136
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
137
+ <title>Audio Recorder App</title>
138
+ <style>
139
+ body, html {
140
+ margin: 0;
141
+ padding: 0;
142
+ height: 100%;
143
+ overflow: hidden;
144
+ }
145
+ #flet-app-frame {
146
+ width: 100%;
147
+ height: 100%;
148
+ border: none;
149
+ position: absolute;
150
+ top: 0;
151
+ left: 0;
152
+ }
153
+ </style>
154
+ </head>
155
+ <body>
156
+ <iframe id="flet-app-frame" src="/flet-app"></iframe>
157
+ <iframe name="hidden-iframe" style="display:none;"></iframe>
158
+ </body>
159
+ </html>
160
+ """
161
+ return HTMLResponse(content=html_content)
162
+
163
+ @app.get("/upload-blob/", response_class=HTMLResponse)
164
+ async def serve_upload_page(blob_uri: str, session_id: str):
165
+ logger.info(f"Blob URI: {blob_uri}")
166
+ logger.info(f"Session ID: {session_id}")
167
+ html_content = f"""
168
+ <!DOCTYPE html>
169
+ <html lang="en">
170
+ <head>
171
+ <meta charset="UTF-8">
172
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
173
+ <title>Upload Blob</title>
174
+ </head>
175
+ <body>
176
+ <script>
177
+ async function sendBlobToServer(blobUri, sessionId) {{
178
+ try {{
179
+ const blob = await fetch(blobUri).then(r => r.blob());
180
+ const base64data = await new Promise((resolve) => {{
181
+ const reader = new FileReader();
182
+ reader.onloadend = () => resolve(reader.result.split(',')[1]);
183
+ reader.readAsDataURL(blob);
184
+ }});
185
+
186
+ const response = await fetch('/store-audio/', {{
187
+ method: 'POST',
188
+ headers: {{
189
+ 'Content-Type': 'application/json',
190
+ }},
191
+ body: JSON.stringify({{
192
+ session_id: sessionId,
193
+ audio_data: base64data
194
+ }})
195
+ }});
196
+
197
+ const result = await response.json();
198
+ console.log(result);
199
+ }} catch (error) {{
200
+ console.error("Error uploading blob:", error);
201
+ }}
202
+ }}
203
+
204
+ sendBlobToServer("{blob_uri}", "{session_id}");
205
+ </script>
206
+ </body>
207
+ </html>
208
+ """
209
+ return HTMLResponse(content=html_content)
210
+
211
+ @app.post("/upload-blob/")
212
+ async def upload_blob(file: UploadFile = File(...), session_id: str = Form(...)):
213
+ logger.info(f"Uploading file: {file.filename}")
214
+ logger.info(f"Session ID: {session_id}")
215
+
216
+ # Create a directory for the session if it doesn't exist
217
+ session_directory = os.path.join(UPLOAD_DIRECTORY, session_id)
218
+ os.makedirs(session_directory, exist_ok=True)
219
+
220
+ # Save the file in the session directory
221
+ file_location = os.path.join(session_directory, file.filename)
222
+ with open(file_location, "wb") as f:
223
+ f.write(await file.read())
224
+
225
+ return JSONResponse(content={"info": f"file '{file.filename}' saved at '{file_location}'"})
226
+
227
+ # Use OrderedDict instead of a regular dictionary
228
+ audio_storage = OrderedDict()
229
+
230
+ @app.post("/store-audio/")
231
+ async def store_audio(data: dict):
232
+ session_id = data.get('session_id')
233
+ audio_data = data.get('audio_data')
234
+
235
+ if not session_id or not audio_data:
236
+ raise HTTPException(status_code=400, detail="Missing session_id or audio_data")
237
+
238
+ # Store the base64 audio data in memory
239
+ audio_storage[session_id] = audio_data
240
+
241
+ print(f"Audio data stored for session {session_id}")
242
+
243
+ return JSONResponse(content={"info": f"Audio data stored for session {session_id}: {audio_data[:100]}"})
244
+
245
+ @app.get("/get-audio/{session_id}")
246
+ async def get_audio(session_id: str):
247
+ if session_id not in audio_storage:
248
+ raise HTTPException(status_code=404, detail="Audio not found")
249
+
250
+ # Pop the audio data from the storage
251
+ audio_data = audio_storage.pop(session_id)
252
+
253
+ return JSONResponse(content={"audio_data": audio_data})