Files
asciinema/asciicast/asciicast.go

169 lines
3.4 KiB
Go
Raw Permalink Normal View History

package asciicast
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"github.com/asciinema/asciinema/Godeps/_workspace/src/golang.org/x/net/html"
)
type Env struct {
Term string `json:"TERM"`
Shell string `json:"SHELL"`
}
type Duration float64
func (d Duration) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`%.6f`, d)), nil
}
type Asciicast struct {
Version int `json:"version"`
Width int `json:"width"`
Height int `json:"height"`
Duration Duration `json:"duration"`
Command string `json:"command"`
Title string `json:"title"`
Env *Env `json:"env"`
Stdout []Frame `json:"stdout"`
}
func NewAsciicast(width, height int, duration float64, command, title string, frames []Frame, env map[string]string) *Asciicast {
return &Asciicast{
Version: 1,
Width: width,
Height: height,
Duration: Duration(duration),
Command: command,
Title: title,
Env: &Env{Term: env["TERM"], Shell: env["SHELL"]},
Stdout: frames,
}
}
func Save(asciicast *Asciicast, path string) error {
bytes, err := json.MarshalIndent(asciicast, "", " ")
if err != nil {
return err
}
err = ioutil.WriteFile(path, bytes, 0644)
if err != nil {
return err
}
return nil
}
// asciinema play file.json
// asciinema play https://asciinema.org/a/123.json
// asciinema play https://asciinema.org/a/123
2015-09-19 10:52:58 +02:00
// asciinema play ipfs://ipfs/QmbdpNCwqeZgnmAWBCQcs8u6Ts6P2ku97tfKAycE1XY88p
// asciinema play -
func getAttr(t *html.Token, name string) string {
for _, a := range t.Attr {
if a.Key == name {
return a.Val
}
}
return ""
}
func extractJSONURL(htmlDoc io.Reader) (string, error) {
z := html.NewTokenizer(htmlDoc)
for {
tt := z.Next()
switch {
case tt == html.ErrorToken:
return "", fmt.Errorf("expected alternate <link> not found in fetched HTML document")
case tt == html.StartTagToken:
t := z.Token()
if t.Data == "link" && getAttr(&t, "rel") == "alternate" && getAttr(&t, "type") == "application/asciicast+json" {
return getAttr(&t, "href"), nil
}
}
}
}
func getSource(url string) (io.ReadCloser, error) {
var source io.ReadCloser
var isHTML bool
var err error
if strings.HasPrefix(url, "ipfs:/") {
url = fmt.Sprintf("https://ipfs.io/%v", url[6:])
} else if strings.HasPrefix(url, "fs:/") {
url = fmt.Sprintf("https://ipfs.io/%v", url[4:])
}
if url == "-" {
source = os.Stdin
} else if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
resp, err := http.Get(url)
2015-06-23 17:50:09 +02:00
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
resp.Body.Close()
return nil, fmt.Errorf("got status %v when requesting %v", resp.StatusCode, url)
}
source = resp.Body
if strings.HasPrefix(resp.Header.Get("Content-Type"), "text/html") {
isHTML = true
}
} else {
source, err = os.Open(url)
if err != nil {
return nil, err
}
if strings.HasSuffix(url, ".html") {
isHTML = true
}
}
if isHTML {
defer source.Close()
url, err = extractJSONURL(source)
if err != nil {
return nil, err
}
return getSource(url)
}
return source, nil
}
func Load(url string) (*Asciicast, error) {
source, err := getSource(url)
if err != nil {
return nil, err
}
defer source.Close()
dec := json.NewDecoder(source)
asciicast := &Asciicast{}
if err = dec.Decode(asciicast); err != nil {
return nil, err
}
return asciicast, nil
}