Skip to content

Commit ef63046

Browse files
committed
feat: init
1 parent be4b874 commit ef63046

File tree

13 files changed

+613
-0
lines changed

13 files changed

+613
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.idea

README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# asciinema parser
2+
3+
# 一、这是什么?解决了什么问题?
4+
5+
这个网站[https://asciinema.org/](https://asciinema.org/)是一个命令行录屏分享网站,它定义了一套`Ascii Cast`格式的文件来存储录屏内容,这个库就是用来解析`Ascii Cast`文件的,支持[v1](https://github.com/asciinema/asciinema/blob/develop/doc/asciicast-v1.md)[v2](https://github.com/asciinema/asciinema/blob/develop/doc/asciicast-v2.md)两个版本。
6+
7+
# 二、 API代码示例
8+
9+
## 2.1 检查录屏文件是哪个版本
10+
11+
```go
12+
package main
13+
14+
import (
15+
"context"
16+
"fmt"
17+
asciinema_parser "github.com/golang-infrastructure/go-asciinema-parser"
18+
)
19+
20+
func main() {
21+
22+
asciiCastV2String := `{"version": 2, "width": 80, "height": 24, "timestamp": 1504467315, "title": "Demo", "env": {"TERM": "xterm-256color", "SHELL": "/bin/zsh"}}
23+
[0.248848, "o", "\u001b[1;31mHello \u001b[32mWorld!\u001b[0m\n"]
24+
[1.001376, "o", "That was ok\rThis is better."]
25+
[2.143733, "o", " "]
26+
[6.541828, "o", "Bye!"]`
27+
28+
// 识别版本
29+
version, err := asciinema_parser.DetectVersion(context.Background(), asciiCastV2String)
30+
if err != nil {
31+
panic(err)
32+
}
33+
fmt.Println(version) // Output: 2
34+
35+
}
36+
```
37+
38+
## 2.2 解析V1格式的录屏软件
39+
40+
```go
41+
package main
42+
43+
import (
44+
"context"
45+
"fmt"
46+
asciinema_parser "github.com/golang-infrastructure/go-asciinema-parser"
47+
)
48+
49+
func main() {
50+
51+
asciiCastV1String := `{
52+
"version": 1,
53+
"width": 80,
54+
"height": 24,
55+
"duration": 1.515658,
56+
"command": "/bin/zsh",
57+
"title": "",
58+
"env": {
59+
"TERM": "xterm-256color",
60+
"SHELL": "/bin/zsh"
61+
},
62+
"stdout": [
63+
[
64+
0.248848,
65+
"\u001b[1;31mHello \u001b[32mWorld!\u001b[0m\n"
66+
],
67+
[
68+
1.001376,
69+
"I am \rThis is on the next line."
70+
]
71+
]
72+
}`
73+
74+
v1, err := asciinema_parser.ParseV1(context.Background(), []byte(asciiCastV1String))
75+
if err != nil {
76+
panic(err)
77+
}
78+
fmt.Println(fmt.Sprintf("%v", v1))
79+
80+
}
81+
```
82+
83+
## 2.3 解析V2格式的录屏软件
84+
85+
```go
86+
package main
87+
88+
import (
89+
"context"
90+
"fmt"
91+
asciinema_parser "github.com/golang-infrastructure/go-asciinema-parser"
92+
)
93+
94+
func main() {
95+
96+
asciiCastV2String := `{"version": 2, "width": 80, "height": 24, "timestamp": 1504467315, "title": "Demo", "env": {"TERM": "xterm-256color", "SHELL": "/bin/zsh"}}
97+
[0.248848, "o", "\u001b[1;31mHello \u001b[32mWorld!\u001b[0m\n"]
98+
[1.001376, "o", "That was ok\rThis is better."]
99+
[2.143733, "o", " "]
100+
[6.541828, "o", "Bye!"]`
101+
102+
v2, err := asciinema_parser.ParseV2(context.Background(), []byte(asciiCastV2String))
103+
if err != nil {
104+
panic(err)
105+
}
106+
fmt.Println(fmt.Sprintf("%v", v2))
107+
108+
}
109+
```
110+
111+
112+

ascii_cast_v1.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package asciinema_parser
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
// ------------------------------------------------- --------------------------------------------------------------------
8+
9+
// AsciiCastV1 v1格式的规范文档: https://github.com/asciinema/asciinema/blob/develop/doc/asciicast-v1.md
10+
type AsciiCastV1 struct {
11+
Version Version `json:"version"`
12+
13+
Width uint `json:"width" yaml:"width" mapstructure:"width"`
14+
Height uint `json:"height" yaml:"height" mapstructure:"height"`
15+
Duration float64 `json:"duration" yaml:"duration" mapstructure:"duration"`
16+
Command string `json:"command" yaml:"command" mapstructure:"command"`
17+
Title string `json:"title" yaml:"title" mapstructure:"title"`
18+
Env map[string]string `json:"env" yaml:"env" mapstructure:"env"`
19+
StdoutFrames []Frame `json:"stdout" yaml:"stdout" mapstructure:"stdout"`
20+
}
21+
22+
func (x *AsciiCastV1) Check() error {
23+
24+
if x.Version != Version1 {
25+
return fmt.Errorf("AsciiCast version %v is not equals 1", x.Version)
26+
}
27+
28+
return nil
29+
}
30+
31+
// ------------------------------------------------- --------------------------------------------------------------------
32+
33+
type Frame []any
34+
35+
func (x Frame) GetDelayE() (float64, error) {
36+
if len(x) > 0 {
37+
v, ok := x[0].(float64)
38+
if !ok {
39+
return 0, fmt.Errorf("cast %s to float failed", x[0])
40+
}
41+
return v, nil
42+
} else {
43+
return 0, nil
44+
}
45+
}
46+
47+
func (x Frame) GetDelay() float64 {
48+
v, _ := x.GetDelayE()
49+
return v
50+
}
51+
52+
func (x Frame) GetDataE() (string, error) {
53+
if len(x) > 1 {
54+
v, ok := x[1].(string)
55+
if !ok {
56+
return "", fmt.Errorf("cast %s to string failed", x[0])
57+
}
58+
return v, nil
59+
} else {
60+
return "", nil
61+
}
62+
}
63+
64+
func (x Frame) GetData() string {
65+
v, _ := x.GetDataE()
66+
return v
67+
}
68+
69+
// ------------------------------------------------- --------------------------------------------------------------------

ascii_cast_v2.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package asciinema_parser
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
)
8+
9+
// 格式规范文档: https://github.com/asciinema/asciinema/blob/develop/doc/asciicast-v2.md
10+
type AsciiCastV2 struct {
11+
12+
// 表示文件的格式版本
13+
Version Version `json:"version" yaml:"version" mapstructure:"version"`
14+
15+
// 屏幕的宽度,但是是列
16+
Width uint `json:"width" yaml:"width" mapstructure:"width"`
17+
18+
// 屏幕的高度,但是是行
19+
Height uint `json:"height" yaml:"height" mapstructure:"height"`
20+
21+
// 录制开始时间
22+
Timestamp uint64 `json:"timestamp" yaml:"timestamp" mapstructure:"timestamp"`
23+
time time.Time
24+
25+
// 录制持续时间
26+
Duration float64 `json:"duration" yaml:"duration" mapstructure:"duration"`
27+
28+
Command string `json:"command" yaml:"command" mapstructure:"command"`
29+
30+
Title string `json:"title" yaml:"title" mapstructure:"title"`
31+
32+
Theme *Theme `json:"theme" yaml:"theme" mapstructure:"theme"`
33+
34+
//
35+
IdleTimeLimit float64 `json:"idle_time_limit" yaml:"idle_time_limit" mapstructure:"idle_time_limit"`
36+
37+
// 相关环境变量
38+
EnvMap map[string]string `json:"env" yaml:"env" mapstructure:"env"`
39+
40+
EventStream []*Event `json:"event_stream" yaml:"event_stream" mapstructure:"event_stream"`
41+
}
42+
43+
func (x *AsciiCastV2) GetTime() time.Time {
44+
if x.time.IsZero() {
45+
x.time = time.Unix(int64(x.Timestamp), 0)
46+
}
47+
return x.time
48+
}
49+
50+
// ------------------------------------------------- --------------------------------------------------------------------
51+
52+
// Theme 可以设置一个初始化的主题
53+
type Theme struct {
54+
55+
// 主题的前景色
56+
FrontColor string `json:"fg" yaml:"fg" mapstructure:"fg"`
57+
58+
// 主题的背景色
59+
BackgroundColor string `json:"bg" yaml:"bg" mapstructure:"bg"`
60+
61+
// 8或者16个颜色的调色板
62+
Palette string `json:"palette" yaml:"palette" mapstructure:"palette"`
63+
}
64+
65+
// ------------------------------------------------- --------------------------------------------------------------------
66+
67+
type EventType string
68+
69+
const (
70+
EventTypeUnknown = ""
71+
EventTypeOutput = "o"
72+
EventTypeInput = "i"
73+
)
74+
75+
func ParseEventType(eventType string) (EventType, error) {
76+
switch strings.ToLower(eventType) {
77+
case EventTypeOutput:
78+
return EventTypeOutput, nil
79+
case EventTypeInput:
80+
return EventTypeInput, nil
81+
default:
82+
return EventTypeUnknown, fmt.Errorf("value %s is not a valid event type", eventType)
83+
}
84+
}
85+
86+
// ------------------------------------------------- --------------------------------------------------------------------
87+
88+
// Event 事件流中的某一个事件
89+
type Event struct {
90+
cast *AsciiCastV2
91+
92+
// 时间发生的时间距开始时间的偏移
93+
Delay float64 `json:"delay" yaml:"delay" mapstructure:"delay"`
94+
95+
// 事件的类型,v2的话是有output和input
96+
EventType EventType `json:"event_type" yaml:"event_type" mapstructure:"event_type"`
97+
98+
// 事件的数据,通常是显示的内容
99+
EventData string `json:"event_data" yaml:"event_data" mapstructure:"event_data"`
100+
}
101+
102+
func (x *Event) GetEventTime() time.Time {
103+
return x.cast.GetTime().Add(time.Millisecond * time.Duration(x.Delay*1000))
104+
}
105+
106+
func (x *Event) GetAsciiCast() *AsciiCastV2 {
107+
return x.cast
108+
}
109+
110+
// ------------------------------------------------- --------------------------------------------------------------------

examples/detect_version/main.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
asciinema_parser "github.com/golang-infrastructure/go-asciinema-parser"
7+
)
8+
9+
func main() {
10+
11+
asciiCastV2String := `{"version": 2, "width": 80, "height": 24, "timestamp": 1504467315, "title": "Demo", "env": {"TERM": "xterm-256color", "SHELL": "/bin/zsh"}}
12+
[0.248848, "o", "\u001b[1;31mHello \u001b[32mWorld!\u001b[0m\n"]
13+
[1.001376, "o", "That was ok\rThis is better."]
14+
[2.143733, "o", " "]
15+
[6.541828, "o", "Bye!"]`
16+
17+
// 识别版本
18+
version, err := asciinema_parser.DetectVersion(context.Background(), asciiCastV2String)
19+
if err != nil {
20+
panic(err)
21+
}
22+
fmt.Println(version) // Output: 2
23+
24+
}

examples/parse_v1/main.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
asciinema_parser "github.com/golang-infrastructure/go-asciinema-parser"
7+
)
8+
9+
func main() {
10+
11+
asciiCastV1String := `{
12+
"version": 1,
13+
"width": 80,
14+
"height": 24,
15+
"duration": 1.515658,
16+
"command": "/bin/zsh",
17+
"title": "",
18+
"env": {
19+
"TERM": "xterm-256color",
20+
"SHELL": "/bin/zsh"
21+
},
22+
"stdout": [
23+
[
24+
0.248848,
25+
"\u001b[1;31mHello \u001b[32mWorld!\u001b[0m\n"
26+
],
27+
[
28+
1.001376,
29+
"I am \rThis is on the next line."
30+
]
31+
]
32+
}`
33+
34+
v1, err := asciinema_parser.ParseV1(context.Background(), []byte(asciiCastV1String))
35+
if err != nil {
36+
panic(err)
37+
}
38+
fmt.Println(fmt.Sprintf("%v", v1))
39+
40+
}

examples/parse_v2/main.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
asciinema_parser "github.com/golang-infrastructure/go-asciinema-parser"
7+
)
8+
9+
func main() {
10+
11+
asciiCastV2String := `{"version": 2, "width": 80, "height": 24, "timestamp": 1504467315, "title": "Demo", "env": {"TERM": "xterm-256color", "SHELL": "/bin/zsh"}}
12+
[0.248848, "o", "\u001b[1;31mHello \u001b[32mWorld!\u001b[0m\n"]
13+
[1.001376, "o", "That was ok\rThis is better."]
14+
[2.143733, "o", " "]
15+
[6.541828, "o", "Bye!"]`
16+
17+
v2, err := asciinema_parser.ParseV2(context.Background(), []byte(asciiCastV2String))
18+
if err != nil {
19+
panic(err)
20+
}
21+
fmt.Println(fmt.Sprintf("%v", v2))
22+
23+
}

0 commit comments

Comments
 (0)