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:
|
|
|
|
}
|
|
|
|
}
|