Display playback position
This commit is contained in:
parent
e08e836dd7
commit
d6824a35ba
37
command.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
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
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
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
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()
|
||||
go playCast(loadedCast, c.D)
|
||||
case CommandPause:
|
||||
resetScreen()
|
||||
case commandPlay:
|
||||
interruptPlayback()
|
||||
case CommandResume:
|
||||
go playCast(loadedCast, playerCursor)
|
||||
go play(c.D)
|
||||
case commandPause:
|
||||
interruptPlayback()
|
||||
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…
Reference in New Issue