From 866435d06127083e70c0abd53f8b3ad1f31dc825 Mon Sep 17 00:00:00 2001 From: HeapOfChaos Date: Sun, 23 Mar 2025 02:02:44 -0400 Subject: [PATCH 1/6] Added ability to specify CloudFlare cookie --- main.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 6ce22e4..be44d35 100644 --- a/main.go +++ b/main.go @@ -104,6 +104,16 @@ func main() { Usage: "minutes to check if the channel is online", Value: 1, }, + &cli.StringFlag{ + Name: "cf-cookie", + Usage: "Cloudflare cookie to bypass anti-bot page", + Value: "", + }, + &cli.StringFlag{ + Name: "user-agent", + Usage: "Custom user agent for when using cf-cookie", + Value: "", + }, //&cli.StringFlag{ // Name: "gui", // Usage: "enabling GUI, availables: 'no', 'web'", @@ -119,6 +129,9 @@ func main() { func start(c *cli.Context) error { fmt.Println(logo) + if c.String("cf-cookie") != "" && c.String("user-agent") == ""{ + return fmt.Errorf("When using the cf-cookie option a user-agent MUST be supplied") + } //if c.String("gui") == "web" { if c.String("username") == "" { @@ -135,6 +148,8 @@ func start(c *cli.Context) error { SplitDuration: c.Int("split-duration"), SplitFilesize: c.Int("split-filesize"), Interval: c.Int("interval"), + CFCookie: c.String("cf-cookie"), + UserAgent: c.String("user-agent"), }); err != nil { return err } @@ -159,7 +174,6 @@ func startWeb(c *cli.Context) error { if err != nil { log.Fatalln(err) } - guiUsername := c.String("gui-username") guiPassword := c.String("gui-password") From 0dd2f66a746fb439b79c649b0b20759f6598b102 Mon Sep 17 00:00:00 2001 From: HeapOfChaos Date: Sun, 23 Mar 2025 02:03:40 -0400 Subject: [PATCH 2/6] Added ability to add CloudFlare Cookie --- chaturbate/manager.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chaturbate/manager.go b/chaturbate/manager.go index 6c36f10..de16aaf 100644 --- a/chaturbate/manager.go +++ b/chaturbate/manager.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "os" - "github.com/google/uuid" "github.com/urfave/cli/v2" ) @@ -32,6 +31,8 @@ type Config struct { SplitDuration int SplitFilesize int Interval int + CFCookie string + UserAgent string } // Manager @@ -39,10 +40,12 @@ type Manager struct { cli *cli.Context Channels map[string]*Channel Updates map[string]chan *Update + } // NewManager func NewManager(c *cli.Context) *Manager { + return &Manager{ cli: c, Channels: map[string]*Channel{}, @@ -101,6 +104,8 @@ func (m *Manager) CreateChannel(conf *Config) error { Resolution: conf.Resolution, ResolutionFallback: conf.ResolutionFallback, Interval: conf.Interval, + CFCookie: m.cli.String("cf-cookie"), + UserAgent: m.cli.String("user-agent"), LastStreamedAt: "-", SegmentDuration: 0, SplitDuration: conf.SplitDuration, From 7541b977ff3c8ad0f3ff699c1ef3a880dc1a9360 Mon Sep 17 00:00:00 2001 From: HeapOfChaos Date: Sun, 23 Mar 2025 02:04:19 -0400 Subject: [PATCH 3/6] Added Ability to add CloudFlare Cookie --- chaturbate/channel.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/chaturbate/channel.go b/chaturbate/channel.go index 7ce4ea3..892e4f0 100644 --- a/chaturbate/channel.go +++ b/chaturbate/channel.go @@ -27,6 +27,8 @@ type Channel struct { filenamePattern string LastStreamedAt string Interval int + CFCookie string + UserAgent string Framerate int Resolution int ResolutionFallback string @@ -60,6 +62,7 @@ type Channel struct { // Run func (w *Channel) Run() { + if w.Username == "" { w.log(logTypeError, "username is empty, use `-u USERNAME` to specify") return @@ -98,7 +101,10 @@ func (w *Channel) Run() { w.log(logTypeError, "release file: %w", err) } } - + if strings.Contains(body, "Just a moment...") { + w.log(logTypeError, "Cloudflare anti-bot page detected, Try providing cf-cookie and user-agent (Check GitHub for instructions)... Exiting") + os.Exit(1) + } w.log(logTypeInfo, "channel is offline, check again %d min(s) later", w.Interval) <-time.After(time.Duration(w.Interval) * time.Minute) // minutes cooldown to check online status } From d3cbe90d09afefcb98411de77694b077d1ece283 Mon Sep 17 00:00:00 2001 From: HeapOfChaos Date: Sun, 23 Mar 2025 02:04:56 -0400 Subject: [PATCH 4/6] Added Ability to add CloudFlare Cookie --- chaturbate/channel_internal.go | 85 ++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/chaturbate/channel_internal.go b/chaturbate/channel_internal.go index 1fcd595..bef1243 100644 --- a/chaturbate/channel_internal.go +++ b/chaturbate/channel_internal.go @@ -18,14 +18,30 @@ import ( // requestChannelBody requests the channel page and returns the body. func (w *Channel) requestChannelBody() (string, error) { + transport := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := &http.Client{Transport: transport} - resp, err := client.Get(w.ChannelURL) + req, err := http.NewRequest("GET", w.ChannelURL, nil) if err != nil { - return "", fmt.Errorf("client get: %w", err) + return "", fmt.Errorf("new request: %w", err) + } + if w.CFCookie != "" { + cookie := &http.Cookie{ + Name: "cf_clearance", + Value: w.CFCookie, + } + + req.AddCookie(cookie) + } + if w.UserAgent != "" { + req.Header.Set("User-Agent", w.UserAgent) + } + resp, err := client.Do(req) + if err != nil { + return "", fmt.Errorf("client do: %w", err) } defer resp.Body.Close() @@ -33,7 +49,7 @@ func (w *Channel) requestChannelBody() (string, error) { if err != nil { return "", fmt.Errorf("read body: %w", err) } - + return string(body), nil } @@ -100,9 +116,25 @@ func (w *Channel) resolveSource(body string) (string, string, error) { } client := &http.Client{Transport: transport} - resp, err := client.Get(roomData.HLSSource) + req, err := http.NewRequest("GET", roomData.HLSSource, nil) if err != nil { - return "", "", fmt.Errorf("client get: %w", err) + return "", "", fmt.Errorf("new request: %w", err) + } + + if w.CFCookie != "" { + cookie := &http.Cookie{ + Name: "cf_clearance", + Value: w.CFCookie, + } + + req.AddCookie(cookie) + } + if w.UserAgent != "" { + req.Header.Set("User-Agent", w.UserAgent) + } + resp, err := client.Do(req) + if err != nil { + return "", "", fmt.Errorf("client do: %w", err) } if resp.StatusCode != http.StatusOK { switch resp.StatusCode { @@ -196,6 +228,7 @@ func (w *Channel) resolveSource(body string) (string, string, error) { return rootURL, sourceURL, nil } + // mergeSegments is a async function that runs in background for the channel, // and it merges the segments from buffer to the file. func (w *Channel) mergeSegments() { @@ -319,9 +352,25 @@ func (w *Channel) requestChunks() ([]*m3u8.MediaSegment, float64, error) { return nil, 0, fmt.Errorf("channel seems to be paused?") } - resp, err := client.Get(w.sourceURL) + req, err := http.NewRequest("GET", w.sourceURL, nil) if err != nil { - return nil, 3, fmt.Errorf("client get: %w", err) + return nil, 0, fmt.Errorf("new request: %w", err) + } + + if w.CFCookie != "" { + cookie := &http.Cookie{ + Name: "cf_clearance", + Value: w.CFCookie, + } + + req.AddCookie(cookie) + } + if w.UserAgent != "" { + req.Header.Set("User-Agent", w.UserAgent) + } + resp, err := client.Do(req) + if err != nil { + return nil, 3, fmt.Errorf("client do: %w", err) } if resp.StatusCode != http.StatusOK { switch resp.StatusCode { @@ -352,6 +401,7 @@ func (w *Channel) requestChunks() ([]*m3u8.MediaSegment, float64, error) { return chunks, playlist.TargetDuration, nil } + // requestSegment requests the specific single segment and put it into the buffer. // the mergeSegments function will merge the segment from buffer to the file in the backgrond. func (w *Channel) requestSegment(url string, index int) error { @@ -364,9 +414,25 @@ func (w *Channel) requestSegment(url string, index int) error { return fmt.Errorf("channel seems to be paused?") } - resp, err := client.Get(w.rootURL + url) + req, err := http.NewRequest("GET", w.rootURL+url, nil) if err != nil { - return fmt.Errorf("client get: %w", err) + return fmt.Errorf("new request: %w", err) + } + + if w.CFCookie != "" { + cookie := &http.Cookie{ + Name: "cf_clearance", + Value: w.CFCookie, + } + + req.AddCookie(cookie) + } + if w.UserAgent != "" { + req.Header.Set("User-Agent", w.UserAgent) + } + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("client do: %w", err) } if resp.StatusCode != http.StatusOK { return fmt.Errorf("received status code %d", resp.StatusCode) @@ -387,3 +453,4 @@ func (w *Channel) requestSegment(url string, index int) error { return nil } + From 2b2e267088a7d0089560ec4dbda9bb6d50324904 Mon Sep 17 00:00:00 2001 From: HeapOfChaos Date: Sun, 23 Mar 2025 02:07:52 -0400 Subject: [PATCH 5/6] Update go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index e4c5a13..b3ddd5c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/teacat/chaturbate-dvr -go 1.22.0 +go 1.22 require ( github.com/gin-gonic/gin v1.9.1 From 6b65dbcc4fc773542fec0e5ac5f530636e139439 Mon Sep 17 00:00:00 2001 From: HeapOfChaos Date: Sun, 23 Mar 2025 02:15:47 -0400 Subject: [PATCH 6/6] Update README.md -- Added info on CloudFlare cookie --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 76f731f..9cc18d6 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,8 @@ GLOBAL OPTIONS: --split-filesize value, --sf value size in MB to split each video into segments ('0' to disable) (default: 0) --log-level value log level, availables: 'DEBUG', 'INFO', 'WARN', 'ERROR' (default: "INFO") --port value port to expose the web interface and API (default: "8080") + --cf-cookie value Cloudflare cookie to bypass anti-bot page + --user-agent value Custom user agent for when using cf-cookie --help, -h show help --version, -v print the version ``` @@ -209,6 +211,12 @@ A: Your network is unstable or being blocked by Chaturbate, the program can't he   +**Q: `I'm receiving a message about CloudFlare anti-bot, what do I need to do?`** + +A: You need to successfully pass the CloudFlare anti-bot check and retrieve the cf_clearance Cookie that is set in the browser after successfully passing the check. This MUST be done from the same IP address and the same User-Agent string MUST be provided to chaturbate-dvr. Provide the cookie value and User-Agent string with the --cf-cookie and --user-agent command line options. The Cookie does expire, but it looks like it's Age is at ~1 year. + +  + ## 💬 Verbose Log Change `-log-level` to `DEBUG` to see more details in terminal, like Duration and Size.