2014-08-03 19:50:39 +02:00
|
|
|
package commands
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
2014-11-02 19:04:19 +01:00
|
|
|
"time"
|
2014-08-03 19:50:39 +02:00
|
|
|
|
|
|
|
|
"github.com/asciinema/asciinema-cli/api"
|
|
|
|
|
"github.com/asciinema/asciinema-cli/cli"
|
|
|
|
|
"github.com/asciinema/asciinema-cli/terminal"
|
|
|
|
|
"github.com/asciinema/asciinema-cli/util"
|
|
|
|
|
)
|
|
|
|
|
|
2014-11-12 20:54:50 +01:00
|
|
|
type RecordCommand struct {
|
|
|
|
|
Cfg *util.Config
|
2014-11-15 13:33:20 +01:00
|
|
|
API api.API
|
2014-11-12 20:54:50 +01:00
|
|
|
Terminal terminal.Terminal
|
|
|
|
|
Command string
|
|
|
|
|
Title string
|
|
|
|
|
NoConfirm bool
|
2014-11-28 23:59:41 +01:00
|
|
|
MaxWait uint
|
2014-11-12 20:54:50 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-15 13:33:20 +01:00
|
|
|
func NewRecordCommand(api api.API, cfg *util.Config) cli.Command {
|
2014-11-12 20:54:50 +01:00
|
|
|
return &RecordCommand{
|
2014-11-15 13:33:20 +01:00
|
|
|
API: api,
|
2014-11-12 20:54:50 +01:00
|
|
|
Cfg: cfg,
|
2014-08-03 20:48:48 +02:00
|
|
|
Terminal: terminal.New(),
|
|
|
|
|
}
|
2014-11-12 20:54:50 +01:00
|
|
|
}
|
2014-08-03 19:50:39 +02:00
|
|
|
|
2014-11-12 20:54:50 +01:00
|
|
|
func (c *RecordCommand) RegisterFlags(flags *flag.FlagSet) {
|
2014-08-03 19:50:39 +02:00
|
|
|
flags.StringVar(
|
2014-11-12 20:54:50 +01:00
|
|
|
&c.Command,
|
2014-08-03 19:50:39 +02:00
|
|
|
"c",
|
2014-11-12 20:54:50 +01:00
|
|
|
defaultRecCommand(c.Cfg.Record.Command),
|
2014-12-14 14:01:33 +01:00
|
|
|
"command to record",
|
2014-08-03 19:50:39 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
flags.StringVar(
|
2014-11-12 20:54:50 +01:00
|
|
|
&c.Title,
|
2014-08-03 19:50:39 +02:00
|
|
|
"t",
|
|
|
|
|
"",
|
2014-12-14 14:01:33 +01:00
|
|
|
"set asciicast title",
|
2014-08-03 19:50:39 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
flags.BoolVar(
|
2014-11-12 20:54:50 +01:00
|
|
|
&c.NoConfirm,
|
2014-08-03 19:50:39 +02:00
|
|
|
"y",
|
|
|
|
|
false,
|
2014-09-07 12:00:01 +02:00
|
|
|
"upload without asking for confirmation",
|
2014-08-03 19:50:39 +02:00
|
|
|
)
|
2014-11-28 23:59:41 +01:00
|
|
|
|
|
|
|
|
flags.UintVar(
|
|
|
|
|
&c.MaxWait,
|
|
|
|
|
"max-wait",
|
|
|
|
|
0,
|
2014-12-14 14:01:33 +01:00
|
|
|
"reduce recorded terminal inactivity to maximum of <max-wait> seconds (0 turns off)",
|
2014-11-28 23:59:41 +01:00
|
|
|
)
|
2014-08-03 19:50:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *RecordCommand) Execute(args []string) error {
|
|
|
|
|
rows, cols, _ := c.Terminal.Size()
|
|
|
|
|
if rows > 30 || cols > 120 {
|
|
|
|
|
util.Warningf("Current terminal size is %vx%v.", cols, rows)
|
|
|
|
|
util.Warningf("It may be too big to be properly replayed on smaller screens.")
|
|
|
|
|
util.Warningf("You can now resize it. Press <Enter> to start recording.")
|
2014-08-03 20:57:15 +02:00
|
|
|
util.ReadLine()
|
2014-08-03 19:50:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
util.Printf("Asciicast recording started.")
|
2014-11-29 00:00:16 +01:00
|
|
|
util.Printf(`Hit Ctrl-D or type "exit" to finish.`)
|
2014-08-03 19:50:39 +02:00
|
|
|
|
2014-11-28 23:59:41 +01:00
|
|
|
stdout := NewStream(c.MaxWait)
|
2014-08-03 19:50:39 +02:00
|
|
|
|
|
|
|
|
err := c.Terminal.Record(c.Command, stdout)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-02 19:04:19 +01:00
|
|
|
stdout.Close()
|
|
|
|
|
|
2014-08-03 19:50:39 +02:00
|
|
|
util.Printf("Asciicast recording finished.")
|
|
|
|
|
|
2014-08-09 21:52:00 +02:00
|
|
|
if !c.NoConfirm {
|
|
|
|
|
util.Printf("Press <Enter> to upload, <Ctrl-C> to cancel.")
|
|
|
|
|
util.ReadLine()
|
|
|
|
|
}
|
2014-08-03 19:50:39 +02:00
|
|
|
|
|
|
|
|
rows, cols, _ = c.Terminal.Size()
|
|
|
|
|
|
2014-11-15 13:33:20 +01:00
|
|
|
url, err := c.API.CreateAsciicast(stdout.Frames, stdout.Duration(), cols, rows, c.Command, c.Title)
|
2014-08-03 19:50:39 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Println(url)
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func defaultRecCommand(recCommand string) string {
|
|
|
|
|
if recCommand == "" {
|
|
|
|
|
recCommand = os.Getenv("SHELL")
|
|
|
|
|
|
|
|
|
|
if recCommand == "" {
|
|
|
|
|
recCommand = "/bin/sh"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return recCommand
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-02 19:04:19 +01:00
|
|
|
type Stream struct {
|
|
|
|
|
Frames []api.Frame
|
2014-11-28 23:59:41 +01:00
|
|
|
elapsedTime time.Duration
|
2014-11-02 19:04:19 +01:00
|
|
|
lastWriteTime time.Time
|
2014-11-28 23:59:41 +01:00
|
|
|
maxWait time.Duration
|
2014-08-03 19:50:39 +02:00
|
|
|
}
|
|
|
|
|
|
2014-11-28 23:59:41 +01:00
|
|
|
func NewStream(maxWait uint) *Stream {
|
2014-11-02 19:04:19 +01:00
|
|
|
now := time.Now()
|
|
|
|
|
|
|
|
|
|
return &Stream{
|
|
|
|
|
lastWriteTime: now,
|
2014-11-28 23:59:41 +01:00
|
|
|
maxWait: time.Duration(maxWait) * time.Second,
|
2014-11-02 19:04:19 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Stream) Write(p []byte) (int, error) {
|
|
|
|
|
frame := api.Frame{}
|
2014-11-28 23:59:41 +01:00
|
|
|
frame.Delay = s.incrementElapsedTime().Seconds()
|
2014-11-02 19:04:19 +01:00
|
|
|
frame.Data = make([]byte, len(p))
|
|
|
|
|
copy(frame.Data, p)
|
|
|
|
|
s.Frames = append(s.Frames, frame)
|
2014-11-28 23:59:41 +01:00
|
|
|
|
2014-08-03 19:50:39 +02:00
|
|
|
return len(p), nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-02 19:04:19 +01:00
|
|
|
func (s *Stream) Close() {
|
2014-11-28 23:59:41 +01:00
|
|
|
s.incrementElapsedTime()
|
2014-11-02 19:04:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Stream) Duration() time.Duration {
|
2014-11-28 23:59:41 +01:00
|
|
|
return s.elapsedTime
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Stream) incrementElapsedTime() time.Duration {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
d := now.Sub(s.lastWriteTime)
|
|
|
|
|
|
|
|
|
|
if s.maxWait > 0 && d > s.maxWait {
|
|
|
|
|
d = s.maxWait
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.elapsedTime += d
|
|
|
|
|
s.lastWriteTime = now
|
|
|
|
|
|
|
|
|
|
return d
|
2014-08-03 19:50:39 +02:00
|
|
|
}
|