You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
3.6 KiB
197 lines
3.6 KiB
package main |
|
|
|
import ( |
|
"fmt" |
|
"log" |
|
"os" |
|
"time" |
|
|
|
"github.com/cirocosta/asciinema-edit/cast" |
|
) |
|
|
|
var ( |
|
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 |
|
) |
|
|
|
func handleCast() { |
|
for c := range castCommand { |
|
if commandConn != nil && !single && !viewer { |
|
sendCommand(c) |
|
continue |
|
} |
|
|
|
switch c.Type { |
|
case commandLoad: |
|
//interruptPlayback() |
|
playerCursor = 0 |
|
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: |
|
playerPaused = true |
|
//interruptPlayback() |
|
case commandResume: |
|
playerPaused = false |
|
/*if playing { |
|
interruptPlayback() |
|
go play(playerCursor) |
|
} else { |
|
//goToTime <- c.D |
|
}*/ |
|
} |
|
} |
|
} |
|
|
|
func loadCast(filePath string) error { |
|
select { |
|
case interrupt <- struct{}{}: |
|
default: |
|
} |
|
|
|
playing = false |
|
|
|
file, err := os.Open(filePath) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
c, err := cast.Decode(file) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
length := time.Duration(c.EventStream[len(c.EventStream)-1].Time * float64(time.Second)) |
|
|
|
loadedCast, loadedCastLength = c, length |
|
|
|
return nil |
|
} |
|
|
|
// TODO maintain playing goroutine, unpause / fast forward when possible rather than reinstancing |
|
func play(at time.Duration) { |
|
playing = true |
|
|
|
resetScreen() |
|
disableEcho() |
|
|
|
var lastPing time.Time |
|
var fastForward time.Duration |
|
start := time.Now().Add(at * -1) |
|
for _, ev := range loadedCast.EventStream { |
|
if playerPaused { |
|
for { |
|
time.Sleep(10 * time.Millisecond) |
|
if !playerPaused { |
|
break |
|
} |
|
select { |
|
case <-interrupt: |
|
break |
|
default: |
|
} |
|
} |
|
} |
|
|
|
t := time.Duration(ev.Time * float64(time.Second)) |
|
if ev.Type == "i" { |
|
continue |
|
} else if fastForward > 0 && t <= fastForward { |
|
fmt.Print(ev.Data) |
|
continue |
|
} |
|
|
|
select { |
|
case <-interrupt: |
|
playing = false |
|
playerCursor = time.Since(start) |
|
sendCommand(&command{commandStatus, playerCursor, "stopped"}) |
|
return |
|
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 |
|
} |
|
default: |
|
} |
|
|
|
if time.Since(start) < t { |
|
t := time.NewTimer(t - time.Since(start)) |
|
select { |
|
case <-interrupt: |
|
playing = false |
|
playerCursor = time.Since(start) |
|
status := "stopped" |
|
if playing { |
|
status = "playing" |
|
} |
|
sendCommand(&command{commandStatus, playerCursor, status}) |
|
return |
|
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 |
|
} |
|
case <-t.C: |
|
} |
|
playerCursor = time.Since(start) |
|
if time.Since(lastPing) >= 10*time.Millisecond { |
|
status := "stopped" |
|
if playing { |
|
status = "playing" |
|
} |
|
sendCommand(&command{commandStatus, playerCursor, status}) |
|
lastPing = time.Now() |
|
} |
|
} |
|
fmt.Print(ev.Data) |
|
} |
|
|
|
playing = false |
|
sendCommand(&command{commandStatus, playerCursor, "stopped"}) |
|
resetScreen() |
|
|
|
if !single { |
|
fmt.Print("Playback complete.") |
|
} |
|
} |
|
|
|
func playFunc(at time.Duration) func() { |
|
return func() { |
|
play(at) |
|
} |
|
} |
|
|
|
func interruptPlayback() { |
|
select { |
|
case interrupt <- struct{}{}: |
|
default: |
|
} |
|
}
|
|
|