summaryrefslogtreecommitdiff
path: root/bilibili
diff options
context:
space:
mode:
authorKeuin <[email protected]>2023-07-02 16:12:13 +0800
committerKeuin <[email protected]>2023-07-02 16:12:13 +0800
commit76a7674dae069e0be15bed6af8450ad214f8dde2 (patch)
tree5b0f1b09ce3c212c5dcc88d53667421cd3d5ac9d /bilibili
parent6f7987593a9db5500fa56fef0ec1845a02df9876 (diff)
Refactor: distinct task errors with type enum.
Diffstat (limited to 'bilibili')
-rw-r--r--bilibili/errors/errors.go43
-rw-r--r--bilibili/errors/taskerror.go146
-rw-r--r--bilibili/streaming.go4
3 files changed, 148 insertions, 45 deletions
diff --git a/bilibili/errors/errors.go b/bilibili/errors/errors.go
deleted file mode 100644
index 2b810ed..0000000
--- a/bilibili/errors/errors.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package errors
-
-import (
- "fmt"
-)
-
-/*
-Task errors.
-*/
-
-type RecoverableTaskError struct {
- err error
- message string
-}
-
-func (e *RecoverableTaskError) Error() string {
- return fmt.Sprintf("%v: %v", e.message, e.err)
-}
-
-func (e *RecoverableTaskError) Unwrap() error {
- return e.err
-}
-
-func NewRecoverableTaskError(message string, err error) error {
- return &RecoverableTaskError{message: message, err: err}
-}
-
-type UnrecoverableTaskError struct {
- err error
- message string
-}
-
-func (e *UnrecoverableTaskError) Error() string {
- return fmt.Sprintf("%v: %v", e.message, e.err)
-}
-
-func (e *UnrecoverableTaskError) Unwrap() error {
- return e.err
-}
-
-func NewUnrecoverableTaskError(message string, err error) error {
- return &UnrecoverableTaskError{message: message, err: err}
-}
diff --git a/bilibili/errors/taskerror.go b/bilibili/errors/taskerror.go
new file mode 100644
index 0000000..644a794
--- /dev/null
+++ b/bilibili/errors/taskerror.go
@@ -0,0 +1,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,
+ }
+}
diff --git a/bilibili/streaming.go b/bilibili/streaming.go
index d081a8f..b985bbb 100644
--- a/bilibili/streaming.go
+++ b/bilibili/streaming.go
@@ -4,7 +4,7 @@ import (
"context"
"errors"
"fmt"
- errors2 "github.com/keuin/slbr/bilibili/errors"
+ errs "github.com/keuin/slbr/bilibili/errors"
"github.com/keuin/slbr/common"
"io"
"net/http"
@@ -71,7 +71,7 @@ func (b Bilibili) CopyLiveStream(
out, err = fileCreator()
if err != nil {
b.logger.Error("Cannot open file for writing: %v", err)
- err = errors2.NewUnrecoverableTaskError("failed to create file", err)
+ err = errs.NewError(errs.FileCreation, err)
return
}
_, err = out.Write(initBytes)