import json import os import signal import subprocess import sys import time from pathlib import Path from openai import OpenAI # flag to control recording recording = True def parse_config() -> tuple[str, str]: config_path = Path.home() / ".config/simplewhispr/config.json" if not config_path.exists(): raise Exception(f"fatal: config file not found at {config_path}") try: with open(config_path, "r") as f: config = json.load(f) api_key = config.get("openai_api_key") model = config.get("model", "gpt-4o-mini-transcribe") if not api_key: raise ValueError("fatal: 'openai_api_key' not found in config file.") return api_key, model except Exception as e: raise Exception(f"fatal: configuration error: {e}") def handle_sigusr1(signum, frame): global recording print("\ninfo: SIGUSR1 received. stopping recording.") recording = False # register signal handler for SIGUSR1 signal.signal(signal.SIGUSR1, handle_sigusr1) def transcribe_audio(filename: str) -> str: api_key, model = parse_config() client = OpenAI(api_key=api_key) with open(filename, "rb") as audio_file: transcription = client.audio.transcriptions.create( model=model, file=audio_file, ) return transcription.text def grab_recording() -> str: output_filename = "/tmp/simplewhispr-recording.wav" # start ffmpeg recording cmd = ["ffmpeg", "-f", "pulse", "-i", "default", "-y", output_filename] print( f"info: starting recording to {output_filename}. send SIGUSR1 (kill -usr1 {os.getpid()}) to stop." ) process = subprocess.Popen( cmd, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True ) # wait for SIGUSR1 while recording: time.sleep(0.5) if process.poll() is not None: print("fatal: ffmpeg exited unexpectedly.") sys.exit(1) # stop recording process.terminate() process.wait() print("info: recording stopped.") return output_filename def main(): with open("/tmp/simplewhispr.pid", "w") as f: f.write(str(os.getpid())) recording_file = grab_recording() print("info: transcribing...") transcription = transcribe_audio(recording_file) print(f"info: transcription: {transcription}") # use wtype to type the output subprocess.run(["wtype", transcription]) if os.path.exists("/tmp/simplewhispr.pid"): os.remove("/tmp/simplewhispr.pid") if __name__ == "__main__": main()