56
56
57
57
< div class ="hidden md:block self-stretch w-px bg-gray-300 dark:bg-gray-700 mx-4 "> </ div >
58
58
59
- < input type ="text " id ="filename " value =" ChordsPy "
59
+ < input type ="text " id ="filename "
60
60
class ="flex-1 h-12 px-4 border-r-0 rounded-l-lg focus:outline-none focus:ring-0 bg-gray-100 border border-gray-110 dark:bg-gray-800 dark:border-gray-700 dark:text-white "
61
61
placeholder ="Enter recording name ">
62
62
< span class ="h-12 px-4 bg-gray-200 text-gray-500 rounded-r-lg dark:bg-gray-700 dark:text-gray-400 inline-flex items-center justify-center text-base ">
66
66
< button id ="record-btn " class ="h-12 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors font-semibold px-6 ">
67
67
Start Recording
68
68
</ button >
69
+ < div id ="recording-status " class ="flex items-center hidden ">
70
+ </ div >
69
71
</ div >
70
72
< div id ="connection-status " class ="mt-2 text-sm text-gray-600 dark:text-gray-400 hidden ">
71
73
< span id ="status-icon " class ="mr-2 "> </ span >
@@ -182,6 +184,7 @@ <h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-1">${app.ti
182
184
const disconnectBtn = document . getElementById ( 'disconnect-btn' ) ;
183
185
const recordBtn = document . getElementById ( 'record-btn' ) ;
184
186
const filenameInput = document . getElementById ( 'filename' ) ;
187
+ const recordingStatus = document . getElementById ( 'recording-status' ) ;
185
188
const statusDiv = document . getElementById ( 'connection-status' ) ;
186
189
const statusText = document . getElementById ( 'status-text' ) ;
187
190
const statusIcon = document . getElementById ( 'status-icon' ) ;
@@ -193,9 +196,48 @@ <h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-1">${app.ti
193
196
let selectedProtocol = null ;
194
197
let selectedBleDevice = null ;
195
198
let isConnected = false ;
199
+ let isRecording = false ;
196
200
let eventSource = null ;
197
201
let isScanning = false ;
198
202
203
+ // Function to generate timestamp for filename
204
+ function getTimestamp ( ) {
205
+ const now = new Date ( ) ;
206
+ const year = now . getFullYear ( ) ;
207
+ const month = String ( now . getMonth ( ) + 1 ) . padStart ( 2 , '0' ) ;
208
+ const day = String ( now . getDate ( ) ) . padStart ( 2 , '0' ) ;
209
+ const hours = String ( now . getHours ( ) ) . padStart ( 2 , '0' ) ;
210
+ const minutes = String ( now . getMinutes ( ) ) . padStart ( 2 , '0' ) ;
211
+ const seconds = String ( now . getSeconds ( ) ) . padStart ( 2 , '0' ) ;
212
+ return `${ year } ${ month } ${ day } _${ hours } ${ minutes } ${ seconds } ` ;
213
+ }
214
+
215
+ // Initialize filename with default value
216
+ function initializeFilename ( ) {
217
+ const defaultName = `ChordsPy_${ getTimestamp ( ) } ` ;
218
+ filenameInput . value = defaultName ;
219
+ filenameInput . placeholder = defaultName ;
220
+ }
221
+
222
+ // Sanitize filename input - replace spaces and dots with underscores
223
+ function sanitizeFilename ( filename ) {
224
+ // Remove leading/trailing whitespace
225
+ filename = filename . trim ( ) ;
226
+ // Replace spaces and dots with underscores
227
+ filename = filename . replace ( / [ \s . ] + / g, '_' ) ;
228
+ return filename ;
229
+ }
230
+
231
+ // Handle filename input changes
232
+ filenameInput . addEventListener ( 'input' , function ( ) {
233
+ // Only sanitize for display (don't modify the actual value)
234
+ const cursorPos = this . selectionStart ;
235
+ const displayValue = sanitizeFilename ( this . value ) ;
236
+ this . value = displayValue ;
237
+ // Restore cursor position after display update
238
+ this . setSelectionRange ( cursorPos , cursorPos ) ;
239
+ } ) ;
240
+
199
241
// Handle protocol selection
200
242
connectionBtns . forEach ( btn => {
201
243
btn . addEventListener ( 'click' , ( ) => {
@@ -416,6 +458,8 @@ <h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-1">${app.ti
416
458
417
459
showStatus ( `Connected via ${ selectedProtocol . toUpperCase ( ) } ` , 'fa-check-circle' , 'text-green-500' ) ;
418
460
461
+ // Start console updates
462
+ startConsoleUpdates ( ) ;
419
463
}
420
464
421
465
// Handle disconnect button
@@ -444,18 +488,108 @@ <h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-1">${app.ti
444
488
statusDiv . classList . add ( 'hidden' ) ;
445
489
selectedProtocol = null ;
446
490
selectedBleDevice = null ;
491
+
492
+ // Stop recording if active
493
+ if ( isRecording ) {
494
+ toggleRecording ( ) ;
495
+ }
496
+
497
+ // Stop console updates
498
+ if ( eventSource ) {
499
+ eventSource . close ( ) ;
500
+ eventSource = null ;
501
+ }
447
502
}
448
503
} catch ( error ) {
449
504
console . error ( 'Disconnection error:' , error ) ;
450
- showStatus ( 'Disconnection failed' , 'fa-times-circle' , 'text-red-500' ) ;
451
505
// Return to disconnect state if disconnection failed
452
506
connectingBtn . classList . add ( 'hidden' ) ;
453
507
disconnectBtn . classList . remove ( 'hidden' ) ;
454
508
}
455
509
} ) ;
456
510
511
+ // Start console updates via SSE
512
+ function startConsoleUpdates ( ) {
513
+ if ( eventSource ) {
514
+ eventSource . close ( ) ;
515
+ }
516
+
517
+ eventSource = new EventSource ( '/console_updates' ) ;
518
+
519
+ eventSource . onmessage = function ( e ) {
520
+ console . log ( 'Console update:' , e . data ) ;
521
+ } ;
522
+
523
+ eventSource . onerror = function ( ) {
524
+ console . error ( 'EventSource failed' ) ;
525
+ if ( eventSource ) {
526
+ eventSource . close ( ) ;
527
+ eventSource = null ;
528
+ }
529
+ } ;
530
+ }
531
+
532
+ // Handle record button
533
+ recordBtn . addEventListener ( 'click' , toggleRecording ) ;
534
+
535
+ // In the toggleRecording function
536
+ function toggleRecording ( ) {
537
+ if ( ! isConnected ) {
538
+ showAlert ( 'Please Start a stream before recording.' ) ;
539
+ return ;
540
+ }
541
+
542
+ // Get the filename (already sanitized in the display)
543
+ let filename = filenameInput . value . trim ( ) ;
544
+
545
+ // If empty, use default (pass null to let server generate default)
546
+ if ( filename === '' ) {
547
+ filename = null ;
548
+ }
549
+
550
+ if ( isRecording ) {
551
+ // Stop recording
552
+ fetch ( '/stop_recording' , { method : 'POST' } )
553
+ . then ( response => response . json ( ) )
554
+ . then ( data => {
555
+ if ( data . status === 'recording_stopped' ) {
556
+ isRecording = false ;
557
+ recordBtn . innerHTML = 'Start Recording' ;
558
+ recordBtn . classList . remove ( 'bg-gray-500' ) ;
559
+ recordBtn . classList . add ( 'bg-red-500' , 'hover:bg-red-600' ) ;
560
+ recordingStatus . classList . add ( 'hidden' ) ;
561
+ }
562
+ } )
563
+ . catch ( error => {
564
+ console . error ( 'Error stopping recording:' , error ) ;
565
+ } ) ;
566
+ } else {
567
+ // Start recording - send the filename (or null for default)
568
+ fetch ( '/start_recording' , {
569
+ method : 'POST' ,
570
+ headers : { 'Content-Type' : 'application/json' , } ,
571
+ body : JSON . stringify ( { filename : filename } )
572
+ } )
573
+ . then ( response => response . json ( ) )
574
+ . then ( data => {
575
+ if ( data . status === 'recording_started' ) {
576
+ isRecording = true ;
577
+ recordBtn . innerHTML = 'Stop Recording' ;
578
+ recordBtn . classList . remove ( 'bg-red-500' , 'hover:bg-red-600' ) ;
579
+ recordBtn . classList . add ( 'bg-gray-500' ) ;
580
+ recordingStatus . classList . remove ( 'hidden' ) ;
581
+ }
582
+ } )
583
+ . catch ( error => {
584
+ console . error ( 'Error starting recording:' , error ) ;
585
+ showAlert ( 'Failed to start recording: ' + error . message ) ;
586
+ } ) ;
587
+ }
588
+ }
589
+
457
590
// Initial setup
458
591
connectBtn . disabled = true ; // Disable connect button until protocol is selected
592
+ initializeFilename ( ) ; // Set default filename with timestamp
459
593
460
594
function showStatus ( text , icon , colorClass ) {
461
595
statusText . textContent = text ;
@@ -478,6 +612,7 @@ <h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-1">${app.ti
478
612
connectBtn . classList . add ( 'hidden' ) ;
479
613
connectingBtn . classList . add ( 'hidden' ) ;
480
614
disconnectBtn . classList . remove ( 'hidden' ) ;
615
+ startConsoleUpdates ( ) ;
481
616
}
482
617
} else {
483
618
// If not connected, update the frontend
@@ -486,6 +621,17 @@ <h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-1">${app.ti
486
621
disconnectBtn . classList . add ( 'hidden' ) ;
487
622
connectingBtn . classList . add ( 'hidden' ) ;
488
623
connectBtn . classList . remove ( 'hidden' ) ;
624
+
625
+ // Stop recording if active
626
+ if ( isRecording ) {
627
+ toggleRecording ( ) ;
628
+ }
629
+
630
+ // Stop console updates
631
+ if ( eventSource ) {
632
+ eventSource . close ( ) ;
633
+ eventSource = null ;
634
+ }
489
635
}
490
636
}
491
637
} )
0 commit comments