from pathlib import Path from contextlib import contextmanager from asyncio import sleep from tempfile import TemporaryDirectory from datetime import date from re import sub from nicegui import ui, run from slugify import slugify _recording = None def _removeParens(string): return sub(r'\(.*?\)', '', string).strip() @contextmanager def _disable(button: ui.button): button.disable() try: yield finally: button.enable() async def _stopRecording(recordButton, uploadButton, recorder, wavFile): with _disable(recordButton): global _recording _recording = recorder.stopRecording() await run.cpu_bound(_recording.writeWav, wavFile) uploadButton.enable() def _makeRecordCallback(playButton, recordButton, uploadButton, soloTool, recorder, wavFile): async def f(): if recorder.recording: await _stopRecording(recordButton, uploadButton, recorder, wavFile) else: if soloTool.playingAdHoc: soloTool.backToNormal() uploadButton.disable() recorder.startRecording() playButton.enable() return f def _makePlayCallback(playButton, recordButton, uploadButton, soloTool, recorder, wavFile): async def f(): with _disable(playButton): if recorder.recording: await _stopRecording(recordButton, uploadButton, recorder, wavFile) if soloTool.playingAdHoc: soloTool.backToNormal() else: soloTool.playAdHoc(wavFile) soloTool.play() return f def _makeUploadCallback(playButton, recordButton, uploadButton, tempDir, sessionManager, getCurrentSong): async def f(): with ui.dialog() as dialog, ui.card(): defaultName = f"{slugify(_removeParens(getCurrentSong()))}.mp3" fileName = ui.input(label='File name', value=defaultName) with ui.row(): ui.button('Upload', color='positive', on_click=lambda: dialog.submit(fileName.value)) ui.button('Cancel', color='negative' ,on_click=lambda: dialog.submit(None)) fileName = await dialog if fileName is None: return playButton.disable() recordButton.disable() uploadButton.disable() def on_dismiss(): playButton.enable() recordButton.enable() uploadButton.enable() n = ui.notification(timeout=None, position='bottom-right', type='ongoing', spinner=True, on_dismiss=on_dismiss, icon='check') n.message = f'Converting to .mp3...' mp3File = Path(tempDir.name) / fileName await run.cpu_bound(_recording.writeMp3, mp3File) n.message = 'Uploading...' folderName = date.today().isoformat() try: await run.io_bound(sessionManager.saveRecording, mp3File, f"{folderName}/{fileName}") except: n.spinner = False n.icon = 'error' n.message = 'Upload failed!' n.close_button = 'Close' return n.spinner = False n.message = 'Done!' await sleep(2) n.dismiss() return f def recordingControls(soloTool, recorder, sessionManager, getCurrentSong): tempDir = TemporaryDirectory(prefix="solotool-") wavFile = Path(tempDir.name) / "st_recording.wav" with ui.button_group().classes('').style('height: 40px'): recordButton = ui.button(icon='fiber_manual_record', color='negative') \ .bind_icon_from(recorder, 'recording', lambda recording: 'radio_button_unchecked' if recording else 'fiber_manual_record') playButton = ui.button(icon='hearing') \ .bind_icon_from(soloTool, 'playingAdHoc', lambda adHoc: 'close' if adHoc else 'hearing') playButton.disable() uploadButton = ui.button(icon='cloud_upload') uploadButton.disable() recordButton.on_click(_makeRecordCallback(playButton, recordButton, uploadButton, soloTool, recorder, wavFile)) playButton.on_click(_makePlayCallback(playButton, recordButton, uploadButton, soloTool, recorder, wavFile)) uploadButton.on_click(_makeUploadCallback(playButton, recordButton, uploadButton, tempDir, sessionManager, getCurrentSong))