2.0.2, Related #104, X-Request

This commit is contained in:
Yami Odymel 2025-05-06 05:27:23 +08:00
parent eaf61e7f4a
commit 65397fb3a1
No known key found for this signature in database
GPG Key ID: 68E469836934DB36
4 changed files with 19 additions and 16 deletions

View File

@ -44,11 +44,6 @@ func FetchStream(ctx context.Context, client *internal.Req, username string) (*S
return nil, fmt.Errorf("failed to get page body: %w", err)
}
// Check for Cloudflare protection page
if strings.Contains(body, "<title>Just a moment...</title>") {
return nil, internal.ErrCloudflareBlocked
}
// Ensure that the playlist.m3u8 file is present in the response
if !strings.Contains(body, "playlist.m3u8") {
return nil, internal.ErrChannelOffline

View File

@ -6,6 +6,7 @@ var (
ErrChannelExists = errors.New("channel exists")
ErrChannelNotFound = errors.New("channel not found")
ErrCloudflareBlocked = errors.New("blocked by Cloudflare; try with `-cookies` and `-user-agent`")
ErrAgeVerification = errors.New("age verification required; try with `-cookies` and `-user-agent`")
ErrChannelOffline = errors.New("channel offline")
ErrPrivateStream = errors.New("channel went offline or private")
ErrPaused = errors.New("channel paused")

View File

@ -62,11 +62,25 @@ func (h *Req) GetBytes(ctx context.Context, url string) ([]byte, error) {
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read body: %w", err)
}
// Check for Cloudflare protection
if strings.Contains(string(b), "<title>Just a moment...</title>") {
return nil, ErrCloudflareBlocked
}
// Check for Age Verification
if strings.Contains(string(b), "Verify your age") {
return nil, ErrAgeVerification
}
if resp.StatusCode == http.StatusForbidden {
return nil, fmt.Errorf("forbidden: %w", ErrPrivateStream)
}
return ReadResponseBody(resp)
return b, err
}
// CreateRequest constructs an HTTP GET request with necessary headers.
@ -83,6 +97,8 @@ func CreateRequest(ctx context.Context, url string) (*http.Request, context.Canc
// SetRequestHeaders applies necessary headers to the request.
func SetRequestHeaders(req *http.Request) {
req.Header.Set("X-Requested-With", "XMLHttpRequest") // So Cloudflare would likely accept the request, and no Age Verification
if server.Config.UserAgent != "" {
req.Header.Set("User-Agent", server.Config.UserAgent)
}
@ -94,15 +110,6 @@ func SetRequestHeaders(req *http.Request) {
}
}
// ReadResponseBody reads and returns the response body.
func ReadResponseBody(resp *http.Response) ([]byte, error) {
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read body: %w", err)
}
return b, nil
}
// ParseCookies converts a cookie string into a map.
func ParseCookies(cookieStr string) map[string]string {
cookies := make(map[string]string)

View File

@ -30,7 +30,7 @@ const logo = `
func main() {
app := &cli.App{
Name: "chaturbate-dvr",
Version: "2.0.1",
Version: "2.0.2",
Usage: "Record your favorite Chaturbate streams automatically. 😎🫵",
Flags: []cli.Flag{
&cli.StringFlag{