mirror of
https://github.com/teacat/chaturbate-dvr.git
synced 2025-10-29 16:59:59 +00:00
Merge pull request #90 from HeapOfChaos/master
Added ability to specify cookie for CloudFlare anti-bot check and to specify User-Agent string
This commit is contained in:
commit
251f0f1fbe
@ -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)
|
--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")
|
--log-level value log level, availables: 'DEBUG', 'INFO', 'WARN', 'ERROR' (default: "INFO")
|
||||||
--port value port to expose the web interface and API (default: "8080")
|
--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
|
--help, -h show help
|
||||||
--version, -v print the version
|
--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
|
## 💬 Verbose Log
|
||||||
|
|
||||||
Change `-log-level` to `DEBUG` to see more details in terminal, like Duration and Size.
|
Change `-log-level` to `DEBUG` to see more details in terminal, like Duration and Size.
|
||||||
|
|||||||
@ -27,6 +27,8 @@ type Channel struct {
|
|||||||
filenamePattern string
|
filenamePattern string
|
||||||
LastStreamedAt string
|
LastStreamedAt string
|
||||||
Interval int
|
Interval int
|
||||||
|
CFCookie string
|
||||||
|
UserAgent string
|
||||||
Framerate int
|
Framerate int
|
||||||
Resolution int
|
Resolution int
|
||||||
ResolutionFallback string
|
ResolutionFallback string
|
||||||
@ -60,6 +62,7 @@ type Channel struct {
|
|||||||
|
|
||||||
// Run
|
// Run
|
||||||
func (w *Channel) Run() {
|
func (w *Channel) Run() {
|
||||||
|
|
||||||
if w.Username == "" {
|
if w.Username == "" {
|
||||||
w.log(LogTypeError, "username is empty, use `-u USERNAME` to specify")
|
w.log(LogTypeError, "username is empty, use `-u USERNAME` to specify")
|
||||||
return
|
return
|
||||||
@ -98,8 +101,11 @@ func (w *Channel) Run() {
|
|||||||
w.log(LogTypeError, "release file: %v", err)
|
w.log(LogTypeError, "release file: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if strings.Contains(body, "<title>Just a moment...</title>") {
|
||||||
w.log(LogTypeInfo, "channel is offline, check again %d min(s) later", w.Interval)
|
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
|
<-time.After(time.Duration(w.Interval) * time.Minute) // minutes cooldown to check online status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,14 +18,30 @@ import (
|
|||||||
|
|
||||||
// requestChannelBody requests the channel page and returns the body.
|
// requestChannelBody requests the channel page and returns the body.
|
||||||
func (w *Channel) requestChannelBody() (string, error) {
|
func (w *Channel) requestChannelBody() (string, error) {
|
||||||
|
|
||||||
transport := &http.Transport{
|
transport := &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
}
|
}
|
||||||
client := &http.Client{Transport: transport}
|
client := &http.Client{Transport: transport}
|
||||||
|
|
||||||
resp, err := client.Get(w.ChannelURL)
|
req, err := http.NewRequest("GET", w.ChannelURL, nil)
|
||||||
if err != 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()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
@ -33,7 +49,7 @@ func (w *Channel) requestChannelBody() (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("read body: %w", err)
|
return "", fmt.Errorf("read body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(body), nil
|
return string(body), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,9 +116,25 @@ func (w *Channel) resolveSource(body string) (string, string, error) {
|
|||||||
}
|
}
|
||||||
client := &http.Client{Transport: transport}
|
client := &http.Client{Transport: transport}
|
||||||
|
|
||||||
resp, err := client.Get(roomData.HLSSource)
|
req, err := http.NewRequest("GET", roomData.HLSSource, nil)
|
||||||
if err != 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 {
|
if resp.StatusCode != http.StatusOK {
|
||||||
switch resp.StatusCode {
|
switch resp.StatusCode {
|
||||||
@ -196,7 +228,8 @@ func (w *Channel) resolveSource(body string) (string, string, error) {
|
|||||||
return rootURL, sourceURL, nil
|
return rootURL, sourceURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeSegments runs in the background and merges segments from the buffer to the file.
|
// 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() {
|
func (w *Channel) mergeSegments() {
|
||||||
var segmentRetries int
|
var segmentRetries int
|
||||||
startTime := time.Now() // Track the start time of the current segment.
|
startTime := time.Now() // Track the start time of the current segment.
|
||||||
@ -339,9 +372,25 @@ func (w *Channel) requestChunks() ([]*m3u8.MediaSegment, float64, error) {
|
|||||||
return nil, 0, fmt.Errorf("channel seems to be paused?")
|
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 {
|
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 {
|
if resp.StatusCode != http.StatusOK {
|
||||||
switch resp.StatusCode {
|
switch resp.StatusCode {
|
||||||
@ -372,6 +421,7 @@ func (w *Channel) requestChunks() ([]*m3u8.MediaSegment, float64, error) {
|
|||||||
return chunks, playlist.TargetDuration, nil
|
return chunks, playlist.TargetDuration, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// requestSegment requests the specific single segment and put it into the buffer.
|
// 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.
|
// the mergeSegments function will merge the segment from buffer to the file in the backgrond.
|
||||||
func (w *Channel) requestSegment(url string, index int) error {
|
func (w *Channel) requestSegment(url string, index int) error {
|
||||||
@ -384,9 +434,25 @@ func (w *Channel) requestSegment(url string, index int) error {
|
|||||||
return fmt.Errorf("channel seems to be paused?")
|
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 {
|
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 {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return fmt.Errorf("received status code %d", resp.StatusCode)
|
return fmt.Errorf("received status code %d", resp.StatusCode)
|
||||||
@ -407,3 +473,4 @@ func (w *Channel) requestSegment(url string, index int) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
@ -32,6 +31,8 @@ type Config struct {
|
|||||||
SplitDuration int
|
SplitDuration int
|
||||||
SplitFilesize int
|
SplitFilesize int
|
||||||
Interval int
|
Interval int
|
||||||
|
CFCookie string
|
||||||
|
UserAgent string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager
|
// Manager
|
||||||
@ -39,10 +40,12 @@ type Manager struct {
|
|||||||
cli *cli.Context
|
cli *cli.Context
|
||||||
Channels map[string]*Channel
|
Channels map[string]*Channel
|
||||||
Updates map[string]chan *Update
|
Updates map[string]chan *Update
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager
|
// NewManager
|
||||||
func NewManager(c *cli.Context) *Manager {
|
func NewManager(c *cli.Context) *Manager {
|
||||||
|
|
||||||
return &Manager{
|
return &Manager{
|
||||||
cli: c,
|
cli: c,
|
||||||
Channels: map[string]*Channel{},
|
Channels: map[string]*Channel{},
|
||||||
@ -101,6 +104,8 @@ func (m *Manager) CreateChannel(conf *Config) error {
|
|||||||
Resolution: conf.Resolution,
|
Resolution: conf.Resolution,
|
||||||
ResolutionFallback: conf.ResolutionFallback,
|
ResolutionFallback: conf.ResolutionFallback,
|
||||||
Interval: conf.Interval,
|
Interval: conf.Interval,
|
||||||
|
CFCookie: m.cli.String("cf-cookie"),
|
||||||
|
UserAgent: m.cli.String("user-agent"),
|
||||||
LastStreamedAt: "-",
|
LastStreamedAt: "-",
|
||||||
SegmentDuration: 0,
|
SegmentDuration: 0,
|
||||||
SplitDuration: conf.SplitDuration,
|
SplitDuration: conf.SplitDuration,
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
|||||||
module github.com/teacat/chaturbate-dvr
|
module github.com/teacat/chaturbate-dvr
|
||||||
|
|
||||||
go 1.22.0
|
go 1.22
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
|
|||||||
16
main.go
16
main.go
@ -104,6 +104,16 @@ func main() {
|
|||||||
Usage: "minutes to check if the channel is online",
|
Usage: "minutes to check if the channel is online",
|
||||||
Value: 1,
|
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{
|
//&cli.StringFlag{
|
||||||
// Name: "gui",
|
// Name: "gui",
|
||||||
// Usage: "enabling GUI, availables: 'no', 'web'",
|
// Usage: "enabling GUI, availables: 'no', 'web'",
|
||||||
@ -119,6 +129,9 @@ func main() {
|
|||||||
|
|
||||||
func start(c *cli.Context) error {
|
func start(c *cli.Context) error {
|
||||||
fmt.Println(logo)
|
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("gui") == "web" {
|
||||||
if c.String("username") == "" {
|
if c.String("username") == "" {
|
||||||
@ -135,6 +148,8 @@ func start(c *cli.Context) error {
|
|||||||
SplitDuration: c.Int("split-duration"),
|
SplitDuration: c.Int("split-duration"),
|
||||||
SplitFilesize: c.Int("split-filesize"),
|
SplitFilesize: c.Int("split-filesize"),
|
||||||
Interval: c.Int("interval"),
|
Interval: c.Int("interval"),
|
||||||
|
CFCookie: c.String("cf-cookie"),
|
||||||
|
UserAgent: c.String("user-agent"),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -159,7 +174,6 @@ func startWeb(c *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
guiUsername := c.String("gui-username")
|
guiUsername := c.String("gui-username")
|
||||||
guiPassword := c.String("gui-password")
|
guiPassword := c.String("gui-password")
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user