docs: update README with detailed desktop features and setup requirements
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
// Package history retains a bounded in-memory caption transcript for the GUI.
|
||||
package history
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"tea.chunkbyte.com/kato/captioneer/src/captions"
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
CreatedAt time.Time
|
||||
Text string
|
||||
StartedAt time.Duration
|
||||
EndedAt time.Duration
|
||||
}
|
||||
|
||||
type Snapshot struct {
|
||||
Finals []Entry
|
||||
Provisional *Entry
|
||||
}
|
||||
|
||||
type Sink struct {
|
||||
mu sync.Mutex
|
||||
capacity int
|
||||
enabled bool
|
||||
finals []Entry
|
||||
provisional *Entry
|
||||
subscribers map[chan Snapshot]struct{}
|
||||
closed bool
|
||||
}
|
||||
|
||||
func New(capacity int, enabled bool) *Sink {
|
||||
if capacity < 1 {
|
||||
capacity = 1
|
||||
}
|
||||
return &Sink{
|
||||
capacity: capacity,
|
||||
enabled: enabled,
|
||||
subscribers: make(map[chan Snapshot]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Sink) Publish(event captions.Event) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.closed {
|
||||
return errors.New("caption history is closed")
|
||||
}
|
||||
if !s.enabled {
|
||||
return nil
|
||||
}
|
||||
now := time.Now()
|
||||
switch event.Kind {
|
||||
case captions.Provisional:
|
||||
s.provisional = &Entry{CreatedAt: now, Text: event.Text, StartedAt: event.StartedAt, EndedAt: event.EndedAt}
|
||||
case captions.Final:
|
||||
s.provisional = nil
|
||||
s.finals = append(s.finals, Entry{CreatedAt: now, Text: event.Text, StartedAt: event.StartedAt, EndedAt: event.EndedAt})
|
||||
if len(s.finals) > s.capacity {
|
||||
s.finals = append([]Entry(nil), s.finals[len(s.finals)-s.capacity:]...)
|
||||
}
|
||||
case captions.Hide:
|
||||
s.provisional = nil
|
||||
}
|
||||
s.publishLocked()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sink) SetEnabled(enabled bool) {
|
||||
s.mu.Lock()
|
||||
s.enabled = enabled
|
||||
if !enabled {
|
||||
s.provisional = nil
|
||||
}
|
||||
s.publishLocked()
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *Sink) Clear() {
|
||||
s.mu.Lock()
|
||||
s.finals = nil
|
||||
s.provisional = nil
|
||||
s.publishLocked()
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *Sink) Snapshot() Snapshot {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.snapshotLocked()
|
||||
}
|
||||
|
||||
func (s *Sink) Transcript() string {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
lines := make([]string, len(s.finals))
|
||||
for index, entry := range s.finals {
|
||||
lines[index] = entry.Text
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func (s *Sink) Subscribe(buffer int) (<-chan Snapshot, func()) {
|
||||
if buffer < 1 {
|
||||
buffer = 1
|
||||
}
|
||||
updates := make(chan Snapshot, buffer)
|
||||
s.mu.Lock()
|
||||
s.subscribers[updates] = struct{}{}
|
||||
updates <- s.snapshotLocked()
|
||||
s.mu.Unlock()
|
||||
return updates, func() {
|
||||
s.mu.Lock()
|
||||
if _, ok := s.subscribers[updates]; ok {
|
||||
delete(s.subscribers, updates)
|
||||
close(updates)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Sink) Close() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.closed {
|
||||
return nil
|
||||
}
|
||||
s.closed = true
|
||||
for subscriber := range s.subscribers {
|
||||
close(subscriber)
|
||||
}
|
||||
s.subscribers = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Sink) snapshotLocked() Snapshot {
|
||||
snapshot := Snapshot{Finals: append([]Entry(nil), s.finals...)}
|
||||
if s.provisional != nil {
|
||||
copy := *s.provisional
|
||||
snapshot.Provisional = ©
|
||||
}
|
||||
return snapshot
|
||||
}
|
||||
|
||||
func (s *Sink) publishLocked() {
|
||||
snapshot := s.snapshotLocked()
|
||||
for subscriber := range s.subscribers {
|
||||
select {
|
||||
case subscriber <- snapshot:
|
||||
default:
|
||||
select {
|
||||
case <-subscriber:
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case subscriber <- snapshot:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user