updated with session

This commit is contained in:
Linrador 2025-12-08 17:48:55 +01:00
parent e9126e34f0
commit fcc853de47
2 changed files with 127 additions and 22 deletions

149
main.go
View File

@ -23,23 +23,67 @@ var roomDossierRegexp = regexp.MustCompile(`window\.initialRoomDossier = "(.*?)"
// --- main --- // --- main ---
func main() { func main() {
if len(os.Args) < 2 { if len(os.Args) < 2 {
fmt.Println("Verwendung: recorder.exe <Chaturbate-URL oder Benutzername> [-o <Pfad/zieldatei.ts>]") fmt.Println("Verwendung: recorder.exe [--http-cookie \"<Cookie>\"] <Chaturbate-URL oder Benutzername> [best] [-o <Pfad/zieldatei.ts>] [-f]")
os.Exit(1) os.Exit(1)
} }
arg := os.Args[1] var (
username := extractUsername(arg) httpCookie string
outputPath string
urlArg string
)
outputPath := fmt.Sprintf("%s_%s.ts", username, time.Now().Format("20060102_150405")) args := os.Args[1:]
for i := 2; i < len(os.Args); i++ {
if os.Args[i] == "-o" && i+1 < len(os.Args) { for i := 0; i < len(args); i++ {
outputPath = os.Args[i+1] a := args[i]
break
switch a {
case "--http-cookie":
if i+1 >= len(args) {
fmt.Println("Fehlender Wert nach --http-cookie")
os.Exit(1)
}
httpCookie = args[i+1]
i++
case "-o":
if i+1 >= len(args) {
fmt.Println("Fehlender Wert nach -o")
os.Exit(1)
}
outputPath = args[i+1]
i++
case "-f":
// nur für Kompatibilität, wird ignoriert
case "best":
// wird ignoriert, wir wählen sowieso beste Qualität
default:
// erste „normale“ Angabe ist die URL / der Benutzername
if urlArg == "" {
urlArg = a
}
} }
} }
if urlArg == "" {
fmt.Println("Keine URL / kein Benutzername angegeben.")
os.Exit(1)
}
username := extractUsername(urlArg)
// Default-Ausgabedatei, falls kein -o
if outputPath == "" {
outputPath = fmt.Sprintf("%s_%s.ts", username, time.Now().Format("20060102_150405"))
}
ctx := context.Background() ctx := context.Background()
body, err := fetchPage("https://chaturbate.com/" + username)
// Seite laden (inkl. Cookie, falls gesetzt)
body, err := fetchPage("https://chaturbate.com/"+username, httpCookie)
if err != nil { if err != nil {
fmt.Println("❌ Fehler beim Laden der Seite:", err) fmt.Println("❌ Fehler beim Laden der Seite:", err)
os.Exit(1) os.Exit(1)
@ -51,7 +95,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
playlist, err := FetchPlaylist(ctx, hlsURL) playlist, err := FetchPlaylist(ctx, hlsURL, httpCookie)
if err != nil { if err != nil {
fmt.Println("❌ Fehler beim Abrufen der Playlist:", err) fmt.Println("❌ Fehler beim Abrufen der Playlist:", err)
os.Exit(1) os.Exit(1)
@ -66,7 +110,7 @@ func main() {
fmt.Println("📡 Aufnahme gestartet:", outputPath) fmt.Println("📡 Aufnahme gestartet:", outputPath)
err = playlist.WatchSegments(ctx, func(b []byte, _ float64) error { err = playlist.WatchSegments(ctx, httpCookie, func(b []byte, _ float64) error {
_, err := file.Write(b) _, err := file.Write(b)
if err != nil { if err != nil {
fmt.Println("❌ Fehler beim Schreiben in Datei:", err) fmt.Println("❌ Fehler beim Schreiben in Datei:", err)
@ -78,10 +122,9 @@ func main() {
fmt.Println("❌ Aufnahmefehler:", err) fmt.Println("❌ Aufnahmefehler:", err)
} }
// TS -> MP4 remuxen (wie gehabt)
mp4Out := outputPath mp4Out := outputPath
ext := filepath.Ext(outputPath) ext := filepath.Ext(outputPath)
// Falls nicht bereits .mp4 als Endung existiert
if ext != ".mp4" { if ext != ".mp4" {
mp4Out = strings.TrimSuffix(outputPath, ext) + ".mp4" mp4Out = strings.TrimSuffix(outputPath, ext) + ".mp4"
} }
@ -100,7 +143,6 @@ func main() {
} else { } else {
fmt.Println("✅ Umwandlung abgeschlossen (web-optimiert):", mp4Out) fmt.Println("✅ Umwandlung abgeschlossen (web-optimiert):", mp4Out)
} }
} }
// --- helper --- // --- helper ---
@ -114,13 +156,40 @@ func extractUsername(input string) string {
return strings.TrimSpace(input) return strings.TrimSpace(input)
} }
func fetchPage(url string) (string, error) { func addCookiesFromString(req *http.Request, cookieStr string) {
if cookieStr == "" {
return
}
pairs := strings.Split(cookieStr, ";")
for _, pair := range pairs {
parts := strings.SplitN(strings.TrimSpace(pair), "=", 2)
if len(parts) != 2 {
continue
}
name := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
if name == "" {
continue
}
req.AddCookie(&http.Cookie{
Name: name,
Value: value,
})
}
}
func fetchPage(url, httpCookie string) (string, error) {
client := http.Client{Timeout: 10 * time.Second} client := http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)
if err != nil { if err != nil {
return "", err return "", err
} }
req.Header.Set("User-Agent", "Mozilla/5.0") req.Header.Set("User-Agent", "Mozilla/5.0")
if httpCookie != "" {
// kompletter Inhalt von --http-cookie, z.B. "session=XYZ; foo=bar"
addCookiesFromString(req, httpCookie)
}
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
@ -140,6 +209,7 @@ func fetchPage(url string) (string, error) {
return string(data), nil return string(data), nil
} }
func ParseStream(html string) (string, error) { func ParseStream(html string) (string, error) {
matches := roomDossierRegexp.FindStringSubmatch(html) matches := roomDossierRegexp.FindStringSubmatch(html)
if len(matches) < 2 { if len(matches) < 2 {
@ -176,12 +246,22 @@ type Resolution struct {
Width int Width int
} }
func FetchPlaylist(ctx context.Context, hlsSource string) (*Playlist, error) { func FetchPlaylist(ctx context.Context, hlsSource, httpCookie string) (*Playlist, error) {
if hlsSource == "" { if hlsSource == "" {
return nil, errors.New("HLS-URL leer") return nil, errors.New("HLS-URL leer")
} }
resp, err := http.Get(hlsSource) client := http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequestWithContext(ctx, "GET", hlsSource, nil)
if err != nil {
return nil, fmt.Errorf("Fehler beim Erstellen der Playlist-Request: %w", err)
}
req.Header.Set("User-Agent", "Mozilla/5.0")
if httpCookie != "" {
req.Header.Set("Cookie", httpCookie)
}
resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("Fehler beim Laden der Playlist: %w", err) return nil, fmt.Errorf("Fehler beim Laden der Playlist: %w", err)
} }
@ -217,7 +297,6 @@ func FetchPlaylist(ctx context.Context, hlsSource string) (*Playlist, error) {
fr = 60 fr = 60
} }
// Besser, wenn größere Auflösung oder gleiche Auflösung mit höherem Framerate
if width > bestWidth || (width == bestWidth && fr > bestFramerate) { if width > bestWidth || (width == bestWidth && fr > bestFramerate) {
bestWidth = width bestWidth = width
bestFramerate = fr bestFramerate = fr
@ -238,12 +317,16 @@ func FetchPlaylist(ctx context.Context, hlsSource string) (*Playlist, error) {
}, nil }, nil
} }
func (p *Playlist) WatchSegments(ctx context.Context, handler func([]byte, float64) error) error { func (p *Playlist) WatchSegments(
ctx context.Context,
httpCookie string,
handler func([]byte, float64) error,
) error {
var lastSeq int64 = -1 var lastSeq int64 = -1
client := http.Client{Timeout: 10 * time.Second} client := http.Client{Timeout: 10 * time.Second}
emptyRounds := 0 emptyRounds := 0
const maxEmptyRounds = 5 // z.B. nach 5 leeren Runden abbrechen const maxEmptyRounds = 5
for { for {
select { select {
@ -252,7 +335,17 @@ func (p *Playlist) WatchSegments(ctx context.Context, handler func([]byte, float
default: default:
} }
resp, err := client.Get(p.PlaylistURL) // Playlist holen
req, err := http.NewRequestWithContext(ctx, "GET", p.PlaylistURL, nil)
if err != nil {
return fmt.Errorf("Fehler beim Erstellen der Playlist-Request: %w", err)
}
req.Header.Set("User-Agent", "Mozilla/5.0")
if httpCookie != "" {
req.Header.Set("Cookie", httpCookie)
}
resp, err := client.Do(req)
if err != nil { if err != nil {
emptyRounds++ emptyRounds++
if emptyRounds >= maxEmptyRounds { if emptyRounds >= maxEmptyRounds {
@ -261,6 +354,7 @@ func (p *Playlist) WatchSegments(ctx context.Context, handler func([]byte, float
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
continue continue
} }
playlist, listType, err := m3u8.DecodeFrom(resp.Body, true) playlist, listType, err := m3u8.DecodeFrom(resp.Body, true)
resp.Body.Close() resp.Body.Close()
@ -288,7 +382,17 @@ func (p *Playlist) WatchSegments(ctx context.Context, handler func([]byte, float
newSegment = true newSegment = true
segmentURL := p.RootURL + segment.URI segmentURL := p.RootURL + segment.URI
segResp, err := client.Get(segmentURL)
segReq, err := http.NewRequestWithContext(ctx, "GET", segmentURL, nil)
if err != nil {
continue
}
segReq.Header.Set("User-Agent", "Mozilla/5.0")
if httpCookie != "" {
segReq.Header.Set("Cookie", httpCookie)
}
segResp, err := client.Do(segReq)
if err != nil { if err != nil {
continue continue
} }
@ -316,3 +420,4 @@ func (p *Playlist) WatchSegments(ctx context.Context, handler func([]byte, float
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }
} }

Binary file not shown.