1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
package errors
import (
"fmt"
"github.com/samber/lo"
"strings"
)
type Type int
const (
// GetRoomInfo means failed to read live room information
GetRoomInfo Type = iota
// GetLiveInfo means failed to get live information
GetLiveInfo
// StreamCopy means an error occurred while reading stream video data
StreamCopy
// LiveEnded means the live is ended
LiveEnded
// DanmakuServerConnection means failed to connect to danmaku server, which provides danmaku and other control data
DanmakuServerConnection
// Heartbeat means an error occurred while sending heartbeat message, which may indicate a broken network connection
Heartbeat
// InitialLiveStatus means failed to get the live status for the first time, which happens in the early stage
InitialLiveStatus
// DanmakuExchangeRead means an error occurred while reading danmaku datagram from the server,
// which may indicate a broken network connection
DanmakuExchangeRead
// GetDanmakuServerInfo means failed to get danmaku server info
GetDanmakuServerInfo
// RecoverLiveStatusChecker means failed to restart live status checker
RecoverLiveStatusChecker
// FileCreation means failed to create a file
FileCreation
// InvalidLiveInfo means the live info is insufficient for recording
InvalidLiveInfo
// LiveStatusWatch means the live status watcher encountered an unrecoverable error
LiveStatusWatch
// Unknown means the error type is unexpected and is not suitable to retry for safety
Unknown
// InvalidAuthProtocol means authentication failed because the protocol is invalid,
// which may indicate the protocol implementation is outdated
InvalidAuthProtocol
// MessageDecompression means the message cannot be decompressed, and we cannot understand its content
MessageDecompression
// JsonDecode means we cannot decode a datum which is expected to be a JSON object string
JsonDecode
)
var recoverableErrors = []Type{
GetRoomInfo,
GetLiveInfo,
StreamCopy,
LiveEnded,
DanmakuServerConnection,
Heartbeat,
InitialLiveStatus,
DanmakuExchangeRead,
GetDanmakuServerInfo,
RecoverLiveStatusChecker,
}
var errorStrings = map[Type]string{
GetRoomInfo: "failed to get living room information",
GetLiveInfo: "failed to get live info",
StreamCopy: "stream copy was unexpectedly interrupted",
LiveEnded: "live is ended",
DanmakuServerConnection: "failed to connect to danmaku server",
Heartbeat: "heartbeat failed",
InitialLiveStatus: "check initial live status failed",
DanmakuExchangeRead: "failed to read exchange from server",
GetDanmakuServerInfo: "cannot get notification server info",
RecoverLiveStatusChecker: "when recovering from a previous error, another error occurred",
FileCreation: "failed to create file",
InvalidLiveInfo: "invalid live info",
LiveStatusWatch: "failed to watch live status",
Unknown: "unexpected error type",
InvalidAuthProtocol: "authentication failed, invalid protocol",
MessageDecompression: "failed to decompress server message",
JsonDecode: "invalid JSON response from server",
}
func (t Type) String() string {
if s, ok := errorStrings[t]; ok {
return s
}
return fmt.Sprintf("<Type %v>", int(t))
}
type taskError struct {
typ Type
err []error
}
func (e *taskError) Message() string {
//TODO implement me
panic("implement me")
}
func (e *taskError) IsRecoverable() bool {
return lo.Contains(recoverableErrors, e.typ)
}
func (e *taskError) Unwrap() []error {
return e.err
}
func (e taskError) Error() string {
sb := strings.Builder{}
if e.IsRecoverable() {
sb.WriteString("recoverable task error")
} else {
sb.WriteString("unrecoverable task error")
}
sb.WriteString(": ")
sb.WriteString(fmt.Sprintf("%v", e.typ))
if len(e.err) > 0 {
sb.WriteString(", ")
for i := range e.err {
sb.WriteString(fmt.Sprintf("%v", e.err[i]))
if i != len(e.err)-1 {
sb.WriteString(", ")
}
}
}
return sb.String()
}
type TaskError interface {
// IsRecoverable reports if this task error is safe to retry.
IsRecoverable() bool
// Unwrap returns the underneath errors which cause the task error.
Unwrap() []error
// Message returns the detailed task-specific error message.
Message() string
// Error returns this error as string
Error() string
}
func NewError(typ Type, err ...error) TaskError {
return &taskError{
typ: typ,
err: err,
}
}
|