Browse Source

Display playback position

master
Trevor Slocum 11 months ago
parent
commit
d6824a35ba
  1. 37
      command.go
  2. 42
      editor.go
  3. 4
      main.go
  4. 29
      player.go
  5. 55
      viewer.go

37
command.go

@ -4,8 +4,41 @@ import (
"bufio"
"encoding/json"
"log"
"net"
"time"
)
var (
commandConn net.Conn
commandsIn = make(chan *command)
commandsOut = make(chan *command)
)
type commandType int
const (
commandStatus = 1
commandLoad = 2
commandStop = 3
commandPlay = 4
commandPause = 5
commandResume = 6
)
type command struct {
Type commandType
D time.Duration
S string
}
func sendCommand(c *command) {
if commandConn == nil {
return
}
commandsOut <- c
}
func handleWrite() {
var b []byte
var err error
@ -19,10 +52,10 @@ func handleWrite() {
}
func handleRead() {
var c *Command
var c *command
scanner := bufio.NewScanner(commandConn)
for scanner.Scan() {
c = &Command{}
c = &command{}
err := json.Unmarshal(scanner.Bytes(), c)
if err != nil {
log.Fatalf("failed to unmarshal %s: %s", string(scanner.Bytes()), err)

42
editor.go

@ -2,6 +2,7 @@ package main
import (
"flag"
"fmt"
"log"
"net"
"strings"
@ -12,17 +13,27 @@ import (
"gitlab.com/tslocum/cview"
)
var editorPaused bool
var (
app *cview.Application
editorPaused bool
statusBuffer *cview.TextView
)
func handleEditor() {
go handleRead()
go handleWrite()
for c := range commandsIn {
log.Fatal(c)
switch c.Type {
case commandStatus:
// TODO cache sent time and update realtime
statusBuffer.SetText(fmt.Sprintf("%d:%02d", int(c.D.Minutes())%60, int(c.D.Seconds())%60))
app.Draw()
}
}
}
func Connect(address string) error {
func connectToViewer(address string) error {
var err error
if strings.HasPrefix(address, "/") {
commandConn, err = net.Dial("unix", address)
@ -43,12 +54,12 @@ func runEditor(viewer, single, force bool, controlAddress string) {
log.Fatal("supply the path to an asciinema .cast file")
}
c, err := loadCast(filePath)
err := loadCast(filePath)
if err != nil {
log.Fatalf("failed to load asciinema screencast: %s", err)
}
err = Connect(controlAddress)
err = connectToViewer(controlAddress)
if err != nil {
if !single {
log.Fatalf("failed to connect to viwer: %s", err)
@ -64,26 +75,25 @@ func runEditor(viewer, single, force bool, controlAddress string) {
app.QueueUpdate(func() {
w, h := app.GetScreenSize()
if (uint(w) < c.Header.Width || uint(h) < c.Header.Height) && !force {
log.Fatalf("recording dimensions (%dx%d) are larger than the current terminal (%dx%d). add --force to force playback", c.Header.Width, c.Header.Height, w, h)
if (uint(w) < loadedCast.Header.Width || uint(h) < loadedCast.Header.Height) && !force {
log.Fatalf("recording dimensions (%dx%d) are larger than the current terminal (%dx%d). add --force to force playback", loadedCast.Header.Width, loadedCast.Header.Height, w, h)
}
})
app.EnableMouse(true)
tv := cview.NewTextView()
tv.SetText("WIP")
statusBuffer = cview.NewTextView()
app.SetRoot(tv, true)
app.SetRoot(statusBuffer, true)
var cursor time.Duration
go func() {
if single {
app.Suspend(playFunc(c, cursor))
app.Suspend(playFunc(cursor))
} else if !viewer {
sendCommand(&Command{CommandLoad, 0, filePath})
sendCommand(&Command{CommandPlay, 30 * time.Second, ""})
sendCommand(&command{commandLoad, 0, filePath})
sendCommand(&command{commandPlay, 60 * time.Second, ""})
}
}()
@ -100,16 +110,16 @@ func runEditor(viewer, single, force bool, controlAddress string) {
}
doPlay := func(ev *tcell.EventKey) *tcell.EventKey {
sendCommand(&Command{CommandPlay, 0, ""})
sendCommand(&command{commandPlay, 0, ""})
return nil
}
doPause := func(ev *tcell.EventKey) *tcell.EventKey {
editorPaused = !editorPaused
if editorPaused {
sendCommand(&Command{CommandPause, 0, ""})
sendCommand(&command{commandPause, 0, ""})
} else {
sendCommand(&Command{CommandResume, 0, ""})
sendCommand(&command{commandResume, 0, ""})
}
return nil
}

4
main.go

@ -4,12 +4,8 @@ import (
"flag"
"os"
"path"
"gitlab.com/tslocum/cview"
)
var app *cview.Application
func main() {
var (
force bool

29
player.go

@ -10,29 +10,33 @@ import (
var (
playerCursor time.Duration
loadedCast *cast.Cast
interrupt = make(chan struct{})
)
func loadCast(filePath string) (*cast.Cast, error) {
func loadCast(filePath string) error {
file, err := os.Open(filePath)
if err != nil {
return nil, err
return err
}
c, err := cast.Decode(file)
if err != nil {
return nil, err
return err
}
return c, nil
loadedCast = c
return nil
}
func playCast(c *cast.Cast, at time.Duration) {
// TODO maintain playing goroutine, unpause / fast forward when possible rather than reinstancing
func play(at time.Duration) {
resetScreen()
disableEcho()
var lastPing time.Time
start := time.Now().Add(at * -1)
for _, ev := range c.EventStream {
for _, ev := range loadedCast.EventStream {
if ev.Type == "i" {
continue // TODO
}
@ -40,6 +44,7 @@ func playCast(c *cast.Cast, at time.Duration) {
select {
case <-interrupt:
playerCursor = time.Since(start)
sendCommand(&command{commandStatus, playerCursor, ""})
return
default:
}
@ -50,18 +55,26 @@ func playCast(c *cast.Cast, at time.Duration) {
select {
case <-interrupt:
playerCursor = time.Since(start)
sendCommand(&command{commandStatus, playerCursor, ""})
return
case <-t.C:
}
playerCursor = time.Since(start)
if time.Since(lastPing) >= 1*time.Second {
sendCommand(&command{commandStatus, playerCursor, ""})
lastPing = time.Now()
}
}
fmt.Print(ev.Data)
}
resetScreen()
fmt.Print("Playback complete.")
}
func playFunc(c *cast.Cast, at time.Duration) func() {
func playFunc(at time.Duration) func() {
return func() {
playCast(c, at)
play(at)
}
}

55
viewer.go

@ -8,43 +8,11 @@ import (
"os/signal"
"strings"
"syscall"
"time"
"golang.org/x/sys/unix"
"github.com/cirocosta/asciinema-edit/cast"
)
var (
commandsIn = make(chan *Command)
commandsOut = make(chan *Command)
commandListener net.Listener
commandConn net.Conn
loadedCast *cast.Cast
)
type CommandType int
const (
CommandStatus = 1
CommandLoad = 2
CommandStop = 3
CommandPlay = 4
CommandPause = 5
CommandResume = 6
)
type Command struct {
Type CommandType
D time.Duration
S string
}
func sendCommand(c *Command) {
commandsOut <- c
}
var commandListener net.Listener
func resetScreen() {
cmd := exec.Command("tput", "reset")
@ -64,7 +32,7 @@ func disableEcho() {
}
}
func Host(address string) error {
func hostViewer(address string) error {
var initialized bool
var err error
if strings.HasPrefix(address, "/") {
@ -94,18 +62,21 @@ func handleViewer() {
var err error
for c := range commandsIn {
switch c.Type {
case CommandLoad:
loadedCast, err = loadCast(c.S)
case commandLoad:
err = loadCast(c.S)
if err != nil {
log.Fatalf("failed to load cast at %s: %s", c.S, err)
}
case CommandPlay:
case commandStop:
interruptPlayback()
resetScreen()
case commandPlay:
interruptPlayback()
go playCast(loadedCast, c.D)
case CommandPause:
go play(c.D)
case commandPause:
interruptPlayback()
case CommandResume:
go playCast(loadedCast, playerCursor)
case commandResume:
go play(playerCursor)
}
}
}
@ -124,7 +95,7 @@ func runViewer(controlAddress string) {
os.Exit(0)
}()
err := Host(controlAddress)
err := hostViewer(controlAddress)
if err != nil {
log.Fatalf("failed to host viwer: %s", err)
}

Loading…
Cancel
Save