Handle shutdown signals
This commit is contained in:
parent
9ee301fbeb
commit
123dc0160b
2 changed files with 122 additions and 13 deletions
|
|
@ -10,6 +10,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"lel/internal/aiprocess"
|
||||
"lel/internal/audio"
|
||||
"lel/internal/clip"
|
||||
"lel/internal/config"
|
||||
|
|
@ -24,6 +25,7 @@ const (
|
|||
StateIdle State = "idle"
|
||||
StateRecording State = "recording"
|
||||
StateTranscribing State = "transcribing"
|
||||
StateProcessing State = "processing"
|
||||
)
|
||||
|
||||
type Daemon struct {
|
||||
|
|
@ -31,6 +33,7 @@ type Daemon struct {
|
|||
x11 *x11.Conn
|
||||
log *log.Logger
|
||||
inj inject.Backend
|
||||
ai aiprocess.Processor
|
||||
|
||||
mu sync.Mutex
|
||||
state State
|
||||
|
|
@ -41,9 +44,9 @@ type Daemon struct {
|
|||
stateCh chan State
|
||||
}
|
||||
|
||||
func New(cfg config.Config, x *x11.Conn, logger *log.Logger, inj inject.Backend) *Daemon {
|
||||
func New(cfg config.Config, x *x11.Conn, logger *log.Logger, inj inject.Backend, ai aiprocess.Processor) *Daemon {
|
||||
r := &audio.Recorder{Input: cfg.FfmpegInput}
|
||||
return &Daemon{cfg: cfg, x11: x, log: logger, inj: inj, state: StateIdle, ffmpeg: r, stateCh: make(chan State, 4)}
|
||||
return &Daemon{cfg: cfg, x11: x, log: logger, inj: inj, ai: ai, state: StateIdle, ffmpeg: r, stateCh: make(chan State, 4)}
|
||||
}
|
||||
|
||||
func (d *Daemon) UpdateConfig(cfg config.Config) {
|
||||
|
|
@ -61,6 +64,24 @@ func (d *Daemon) UpdateBackend(inj inject.Backend) {
|
|||
d.mu.Unlock()
|
||||
}
|
||||
|
||||
func (d *Daemon) UpdateAI(proc aiprocess.Processor) {
|
||||
d.mu.Lock()
|
||||
d.ai = proc
|
||||
d.mu.Unlock()
|
||||
}
|
||||
|
||||
func (d *Daemon) setState(state State) {
|
||||
d.mu.Lock()
|
||||
d.state = state
|
||||
d.notify(state)
|
||||
d.mu.Unlock()
|
||||
}
|
||||
|
||||
func (d *Daemon) setStateLocked(state State) {
|
||||
d.state = state
|
||||
d.notify(state)
|
||||
}
|
||||
|
||||
func (d *Daemon) State() State {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
|
@ -79,8 +100,7 @@ func (d *Daemon) Toggle() {
|
|||
d.log.Printf("record start failed: %v", err)
|
||||
}
|
||||
case StateRecording:
|
||||
d.state = StateTranscribing
|
||||
d.notify(StateTranscribing)
|
||||
d.setStateLocked(StateTranscribing)
|
||||
d.mu.Unlock()
|
||||
go d.stopAndProcess("user")
|
||||
return
|
||||
|
|
@ -90,6 +110,32 @@ func (d *Daemon) Toggle() {
|
|||
d.mu.Unlock()
|
||||
}
|
||||
|
||||
func (d *Daemon) StopRecording(reason string) {
|
||||
d.mu.Lock()
|
||||
if d.state != StateRecording {
|
||||
d.mu.Unlock()
|
||||
return
|
||||
}
|
||||
d.setStateLocked(StateTranscribing)
|
||||
d.mu.Unlock()
|
||||
go d.stopAndProcess(reason)
|
||||
}
|
||||
|
||||
func (d *Daemon) WaitForIdle(ctx context.Context) bool {
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
if d.State() == StateIdle {
|
||||
return true
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Daemon) startRecordingLocked() error {
|
||||
if d.state != StateIdle {
|
||||
return errors.New("not idle")
|
||||
|
|
@ -114,8 +160,7 @@ func (d *Daemon) startRecordingLocked() error {
|
|||
d.mu.Unlock()
|
||||
return
|
||||
}
|
||||
d.state = StateTranscribing
|
||||
d.notify(StateTranscribing)
|
||||
d.setStateLocked(StateTranscribing)
|
||||
d.mu.Unlock()
|
||||
go d.stopAndProcess("timeout")
|
||||
})
|
||||
|
|
@ -171,8 +216,21 @@ func (d *Daemon) stopAndProcess(reason string) {
|
|||
status = "whisper failed: " + err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
d.log.Printf("transcript: %s", text)
|
||||
|
||||
if d.ai != nil && d.cfg.AIEnabled {
|
||||
d.setState(StateProcessing)
|
||||
aiCtx, cancel := context.WithTimeout(context.Background(), time.Duration(d.cfg.AITimeoutSec)*time.Second)
|
||||
cleaned, err := d.ai.Process(aiCtx, text)
|
||||
cancel()
|
||||
if err != nil {
|
||||
d.log.Printf("ai process failed: %v", err)
|
||||
} else if cleaned != "" {
|
||||
text = cleaned
|
||||
}
|
||||
}
|
||||
|
||||
d.log.Printf("output: %s", text)
|
||||
clipCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
if err := clip.WriteClipboard(clipCtx, text); err != nil {
|
||||
|
|
@ -190,10 +248,7 @@ func (d *Daemon) stopAndProcess(reason string) {
|
|||
}
|
||||
|
||||
func (d *Daemon) setIdle(msg string) {
|
||||
d.mu.Lock()
|
||||
d.state = StateIdle
|
||||
d.notify(StateIdle)
|
||||
d.mu.Unlock()
|
||||
d.setState(StateIdle)
|
||||
d.log.Printf("idle (%s)", msg)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue