asciinema-editor/player.go

198 lines
3.6 KiB
Go
Raw Normal View History

2020-10-23 17:58:10 +00:00
package main
import (
"fmt"
2020-10-23 20:38:54 +00:00
"log"
2020-10-23 17:58:10 +00:00
"os"
"time"
"github.com/cirocosta/asciinema-edit/cast"
)
var (
2020-10-28 01:21:54 +00:00
playerCursor time.Duration
loadedCast *cast.Cast
loadedCastLength time.Duration
playing bool
castCommand = make(chan *command)
interrupt = make(chan struct{})
goToTime = make(chan time.Duration)
playerPaused bool
2020-10-23 17:58:10 +00:00
)
2020-10-23 20:38:54 +00:00
func handleCast() {
for c := range castCommand {
if commandConn != nil && !single && !viewer {
sendCommand(c)
continue
}
switch c.Type {
case commandLoad:
2020-10-28 01:21:54 +00:00
//interruptPlayback()
playerCursor = 0
2020-10-23 20:38:54 +00:00
err := loadCast(c.S)
if err != nil {
log.Fatalf("failed to load cast at %s: %s", c.S, err)
}
case commandStop:
interruptPlayback()
resetScreen()
case commandPlay:
if !playing {
go play(c.D)
} else {
goToTime <- c.D
}
case commandPause:
2020-10-28 01:21:54 +00:00
playerPaused = true
//interruptPlayback()
2020-10-23 20:38:54 +00:00
case commandResume:
2020-10-28 01:21:54 +00:00
playerPaused = false
/*if playing {
2020-10-23 20:38:54 +00:00
interruptPlayback()
2020-10-28 01:21:54 +00:00
go play(playerCursor)
2020-10-23 20:38:54 +00:00
} else {
2020-10-28 01:21:54 +00:00
//goToTime <- c.D
}*/
2020-10-23 20:38:54 +00:00
}
}
}
2020-10-23 19:28:10 +00:00
func loadCast(filePath string) error {
2020-10-23 20:38:54 +00:00
select {
case interrupt <- struct{}{}:
default:
}
playing = false
2020-10-23 17:58:10 +00:00
file, err := os.Open(filePath)
if err != nil {
2020-10-23 19:28:10 +00:00
return err
2020-10-23 17:58:10 +00:00
}
c, err := cast.Decode(file)
if err != nil {
2020-10-23 19:28:10 +00:00
return err
2020-10-23 17:58:10 +00:00
}
2020-10-28 01:21:54 +00:00
length := time.Duration(c.EventStream[len(c.EventStream)-1].Time * float64(time.Second))
loadedCast, loadedCastLength = c, length
2020-10-23 19:28:10 +00:00
return nil
2020-10-23 17:58:10 +00:00
}
2020-10-23 19:28:10 +00:00
// TODO maintain playing goroutine, unpause / fast forward when possible rather than reinstancing
func play(at time.Duration) {
2020-10-23 20:38:54 +00:00
playing = true
2020-10-23 17:58:10 +00:00
resetScreen()
disableEcho()
2020-10-23 19:28:10 +00:00
var lastPing time.Time
2020-10-23 20:38:54 +00:00
var fastForward time.Duration
2020-10-23 17:58:10 +00:00
start := time.Now().Add(at * -1)
2020-10-23 19:28:10 +00:00
for _, ev := range loadedCast.EventStream {
2020-10-28 01:21:54 +00:00
if playerPaused {
for {
time.Sleep(10 * time.Millisecond)
if !playerPaused {
break
}
select {
case <-interrupt:
break
default:
}
}
}
2020-10-23 20:38:54 +00:00
t := time.Duration(ev.Time * float64(time.Second))
2020-10-23 17:58:10 +00:00
if ev.Type == "i" {
2020-10-23 20:38:54 +00:00
continue
} else if fastForward > 0 && t <= fastForward {
fmt.Print(ev.Data)
continue
2020-10-23 17:58:10 +00:00
}
select {
case <-interrupt:
2020-10-23 20:38:54 +00:00
playing = false
2020-10-23 17:58:10 +00:00
playerCursor = time.Since(start)
2020-10-23 20:38:54 +00:00
sendCommand(&command{commandStatus, playerCursor, "stopped"})
2020-10-23 17:58:10 +00:00
return
2020-10-23 20:38:54 +00:00
case d := <-goToTime:
if d < playerCursor {
go play(d)
return
} else if d > playerCursor {
fastForward = d
start = time.Now().Add(fastForward * -1)
fmt.Print(ev.Data)
continue
}
2020-10-23 17:58:10 +00:00
default:
}
if time.Since(start) < t {
t := time.NewTimer(t - time.Since(start))
select {
case <-interrupt:
2020-10-23 20:38:54 +00:00
playing = false
2020-10-23 17:58:10 +00:00
playerCursor = time.Since(start)
2020-10-23 20:38:54 +00:00
status := "stopped"
if playing {
status = "playing"
}
sendCommand(&command{commandStatus, playerCursor, status})
2020-10-23 17:58:10 +00:00
return
2020-10-23 20:38:54 +00:00
case d := <-goToTime:
if d < playerCursor {
go play(d)
return
} else if d > playerCursor {
fastForward = d
start = time.Now().Add(fastForward * -1)
fmt.Print(ev.Data)
continue
}
2020-10-23 17:58:10 +00:00
case <-t.C:
}
playerCursor = time.Since(start)
2020-10-28 01:21:54 +00:00
if time.Since(lastPing) >= 10*time.Millisecond {
2020-10-23 20:38:54 +00:00
status := "stopped"
if playing {
status = "playing"
}
sendCommand(&command{commandStatus, playerCursor, status})
2020-10-23 19:28:10 +00:00
lastPing = time.Now()
}
2020-10-23 17:58:10 +00:00
}
fmt.Print(ev.Data)
}
2020-10-23 19:28:10 +00:00
2020-10-23 20:38:54 +00:00
playing = false
sendCommand(&command{commandStatus, playerCursor, "stopped"})
2020-10-23 19:28:10 +00:00
resetScreen()
2020-10-23 20:38:54 +00:00
if !single {
fmt.Print("Playback complete.")
}
2020-10-23 17:58:10 +00:00
}
2020-10-23 19:28:10 +00:00
func playFunc(at time.Duration) func() {
2020-10-23 17:58:10 +00:00
return func() {
2020-10-23 19:28:10 +00:00
play(at)
2020-10-23 17:58:10 +00:00
}
}
func interruptPlayback() {
select {
case interrupt <- struct{}{}:
default:
}
}