mirror of
https://github.com/weyne85/chaturbate-dvr.git
synced 2025-10-29 16:58:56 +00:00
2.0.0 refactor
This commit is contained in:
56
internal/internal.go
Normal file
56
internal/internal.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// FormatDuration converts a float64 duration (in seconds) to h:m:s format.
|
||||
func FormatDuration(duration float64) string {
|
||||
if duration == 0 {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
hours = int(duration) / 3600
|
||||
minutes = (int(duration) % 3600) / 60
|
||||
seconds = int(duration) % 60
|
||||
)
|
||||
return fmt.Sprintf("%d:%02d:%02d", hours, minutes, seconds)
|
||||
}
|
||||
|
||||
// FormatFilesize converts an int filesize in bytes to a human-readable string (KB, MB, GB).
|
||||
func FormatFilesize(filesize int) string {
|
||||
if filesize == 0 {
|
||||
return ""
|
||||
}
|
||||
const (
|
||||
KB = 1024
|
||||
MB = KB * 1024
|
||||
GB = MB * 1024
|
||||
)
|
||||
switch {
|
||||
case filesize >= GB:
|
||||
return fmt.Sprintf("%.2f GB", float64(filesize)/float64(GB))
|
||||
case filesize >= MB:
|
||||
return fmt.Sprintf("%.2f MB", float64(filesize)/float64(MB))
|
||||
case filesize >= KB:
|
||||
return fmt.Sprintf("%.2f KB", float64(filesize)/float64(KB))
|
||||
default:
|
||||
return fmt.Sprintf("%d bytes", filesize)
|
||||
}
|
||||
}
|
||||
|
||||
// SegmentSeq extracts the segment sequence number from a filename.
|
||||
func SegmentSeq(filename string) int {
|
||||
re := regexp.MustCompile(`_(\d+)\.ts$`)
|
||||
match := re.FindStringSubmatch(filename)
|
||||
|
||||
if len(match) > 1 {
|
||||
number, err := strconv.Atoi(match[1])
|
||||
if err == nil {
|
||||
return number
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
13
internal/internal_err.go
Normal file
13
internal/internal_err.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package internal
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrChannelExists = errors.New("channel exists")
|
||||
ErrChannelNotFound = errors.New("channel not found")
|
||||
ErrCloudflareBlocked = errors.New("blocked by Cloudflare; try with `-cookies` and `-user-agent`")
|
||||
ErrChannelOffline = errors.New("channel offline")
|
||||
ErrPrivateStream = errors.New("channel went offline or private")
|
||||
ErrPaused = errors.New("channel paused")
|
||||
ErrStopped = errors.New("channel stopped")
|
||||
)
|
||||
117
internal/internal_req.go
Normal file
117
internal/internal_req.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/teacat/chaturbate-dvr/server"
|
||||
)
|
||||
|
||||
// Req represents an HTTP client with customized settings.
|
||||
type Req struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewReq creates a new HTTP client with specific transport configurations.
|
||||
func NewReq() *Req {
|
||||
return &Req{
|
||||
client: &http.Client{
|
||||
Transport: CreateTransport(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTransport initializes a custom HTTP transport.
|
||||
func CreateTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
}
|
||||
|
||||
// Get sends an HTTP GET request and returns the response as a string.
|
||||
func (h *Req) Get(ctx context.Context, url string) (string, error) {
|
||||
resp, err := h.GetBytes(ctx, url)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get bytes: %w", err)
|
||||
}
|
||||
return string(resp), nil
|
||||
}
|
||||
|
||||
// GetBytes sends an HTTP GET request and returns the response as a byte slice.
|
||||
func (h *Req) GetBytes(ctx context.Context, url string) ([]byte, error) {
|
||||
req, cancel, err := CreateRequest(ctx, url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new request: %w", err)
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
resp, err := h.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("client do: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusForbidden {
|
||||
return nil, fmt.Errorf("forbidden: %w", ErrPrivateStream)
|
||||
}
|
||||
|
||||
return ReadResponseBody(resp)
|
||||
}
|
||||
|
||||
// CreateRequest constructs an HTTP GET request with necessary headers.
|
||||
func CreateRequest(ctx context.Context, url string) (*http.Request, context.CancelFunc, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 10*time.Second) // timed out after 10 seconds
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, cancel, err
|
||||
}
|
||||
SetRequestHeaders(req)
|
||||
return req, cancel, nil
|
||||
}
|
||||
|
||||
// SetRequestHeaders applies necessary headers to the request.
|
||||
func SetRequestHeaders(req *http.Request) {
|
||||
if server.Config.UserAgent != "" {
|
||||
req.Header.Set("User-Agent", server.Config.UserAgent)
|
||||
}
|
||||
if server.Config.Cookies != "" {
|
||||
cookies := ParseCookies(server.Config.Cookies)
|
||||
for name, value := range cookies {
|
||||
req.AddCookie(&http.Cookie{Name: name, Value: value})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
pairs := strings.Split(cookieStr, ";")
|
||||
|
||||
// Iterate over each cookie pair and extract key-value pairs
|
||||
for _, pair := range pairs {
|
||||
parts := strings.SplitN(strings.TrimSpace(pair), "=", 2)
|
||||
if len(parts) == 2 {
|
||||
// Trim spaces around key and value
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
// Store cookie name and value in the map
|
||||
cookies[key] = value
|
||||
}
|
||||
}
|
||||
return cookies
|
||||
}
|
||||
Reference in New Issue
Block a user