summaryrefslogtreecommitdiff
path: root/bilibili/streaming.go
diff options
context:
space:
mode:
Diffstat (limited to 'bilibili/streaming.go')
-rw-r--r--bilibili/streaming.go66
1 files changed, 66 insertions, 0 deletions
diff --git a/bilibili/streaming.go b/bilibili/streaming.go
new file mode 100644
index 0000000..cc915a3
--- /dev/null
+++ b/bilibili/streaming.go
@@ -0,0 +1,66 @@
+package bilibili
+
+import (
+ "bilibili-livestream-archiver/common"
+ "context"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+)
+
+// CopyLiveStream read data from a livestream video stream, copy them to a writer.
+func (b Bilibili) CopyLiveStream(
+ ctx context.Context,
+ roomId common.RoomId,
+ stream StreamingUrlInfo,
+ out io.Writer,
+) (err error) {
+ url := stream.URL
+ if !strings.HasPrefix(url, "https://") &&
+ !strings.HasPrefix(url, "http://") {
+ return fmt.Errorf("invalid URL: %v", url)
+ }
+
+ r, err := b.newGet(url)
+ if err != nil {
+ b.error.Printf("Cannot create HTTP GET instance on %v: %v", url, err)
+ return err
+ }
+
+ r.Header.Set("Referer",
+ fmt.Sprintf("https://live.bilibili.com/blanc/%d?liteVersion=true", roomId))
+
+ resp, err := b.http.Do(r)
+ if err != nil {
+ b.error.Printf("Cannot make HTTP GET request on %v: %v\n", url, err)
+ return
+ }
+
+ // 404 when not streaming
+ if resp.StatusCode == http.StatusNotFound {
+ return ErrRoomIsClosed
+ }
+
+ err = validateHttpStatus(resp)
+ if err != nil {
+ return
+ }
+
+ defer func() { _ = resp.Body.Close() }()
+
+ // guard the following copy loop
+ // if the context is cancelled, stop it by closing the reader
+ guardianCtx, cancelGuardian := context.WithCancel(ctx)
+ go func() {
+ <-guardianCtx.Done()
+ _ = resp.Body.Close()
+ }()
+ defer cancelGuardian()
+
+ // blocking copy
+ n, err := io.Copy(out, resp.Body)
+
+ b.info.Printf("Bytes copied: %v", n)
+ return
+}