nsfwapp/backend/chaturbate_autostart.go
2026-01-13 14:00:05 +01:00

226 lines
4.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"fmt"
"net/url"
"sort"
"strings"
"time"
)
type autoStartItem struct {
userKey string
url string
}
func normUser(s string) string {
return strings.ToLower(strings.TrimSpace(s))
}
func chaturbateUserFromURL(raw string) string {
raw = strings.TrimSpace(raw)
if raw == "" {
return ""
}
u, err := url.Parse(raw)
if err != nil || u.Hostname() == "" {
return ""
}
host := strings.ToLower(u.Hostname())
if !strings.Contains(host, "chaturbate.com") {
return ""
}
parts := strings.Split(u.Path, "/")
for _, p := range parts {
p = strings.TrimSpace(p)
if p != "" {
return normUser(p)
}
}
return ""
}
func cookieHeaderFromSettings(s RecorderSettings) string {
m, err := decryptCookieMap(s.EncryptedCookies)
if err != nil || len(m) == 0 {
return ""
}
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
var b strings.Builder
for i, k := range keys {
v := strings.TrimSpace(m[k])
if k == "" || v == "" {
continue
}
if i > 0 {
b.WriteString("; ")
}
b.WriteString(k)
b.WriteString("=")
b.WriteString(v)
}
return b.String()
}
func resolveChaturbateURL(m WatchedModelLite) string {
in := strings.TrimSpace(m.Input)
if strings.HasPrefix(strings.ToLower(in), "http://") || strings.HasPrefix(strings.ToLower(in), "https://") {
return in
}
key := strings.Trim(strings.TrimSpace(m.ModelKey), "/")
if key == "" {
return ""
}
return fmt.Sprintf("https://chaturbate.com/%s/", key)
}
// Startet watched+online(public) automatisch unabhängig vom Frontend
func startChaturbateAutoStartWorker(store *ModelStore) {
if store == nil {
fmt.Println("⚠️ [autostart] model store is nil")
return
}
const pollInterval = 5 * time.Second
const startGap = 5 * time.Second
const retryCooldown = 25 * time.Second
queue := make([]autoStartItem, 0, 64)
queued := map[string]bool{}
lastTry := map[string]time.Time{}
var lastStart time.Time
for {
if isAutostartPaused() {
// optional: Queue behalten oder leeren ich würde sie behalten.
time.Sleep(2 * time.Second)
continue
}
s := getSettings()
// ✅ Autostart nur wenn Feature aktiviert ist
// (optional zusätzlich AutoAddToDownloadList wie im Frontend logisch gekoppelt)
if !s.UseChaturbateAPI || !s.AutoStartAddedDownloads || !s.AutoAddToDownloadList {
queue = queue[:0]
queued = map[string]bool{}
time.Sleep(2 * time.Second)
continue
}
cookieHdr := cookieHeaderFromSettings(s)
// ohne cf_clearance + session_* keine Autostarts (gleiches Kriterium wie runJob)
if !hasChaturbateCookies(cookieHdr) {
time.Sleep(5 * time.Second)
continue
}
// online snapshot aus cache
cbMu.RLock()
rooms := append([]ChaturbateRoom(nil), cb.Rooms...)
cbMu.RUnlock()
showByUser := map[string]string{}
for _, r := range rooms {
showByUser[normUser(r.Username)] = strings.ToLower(strings.TrimSpace(r.CurrentShow))
}
// running users (damit wir nicht doppelt starten)
running := map[string]bool{}
jobsMu.Lock()
for _, j := range jobs {
if j == nil || j.Status != JobRunning {
continue
}
u := chaturbateUserFromURL(j.SourceURL)
if u != "" {
running[u] = true
}
}
jobsMu.Unlock()
// watched list aus DB
watched := store.ListWatchedLite("chaturbate.com")
watchedByUser := map[string]WatchedModelLite{}
for _, m := range watched {
key := normUser(m.ModelKey)
if key != "" && m.Watching {
watchedByUser[key] = m
}
}
// queue prune
nextQueue := queue[:0]
nextQueued := map[string]bool{}
for _, it := range queue {
m, ok := watchedByUser[it.userKey]
if !ok {
continue
}
if showByUser[it.userKey] == "" {
continue
}
if running[it.userKey] {
continue
}
it.url = resolveChaturbateURL(m)
if it.url == "" {
continue
}
nextQueue = append(nextQueue, it)
nextQueued[it.userKey] = true
}
queue = nextQueue
queued = nextQueued
// enqueue new public watched
now := time.Now()
for user, m := range watchedByUser {
if showByUser[user] != "public" {
continue
}
if running[user] {
continue
}
if queued[user] {
continue
}
if t, ok := lastTry[user]; ok && now.Sub(t) < retryCooldown {
continue
}
u := resolveChaturbateURL(m)
if u == "" {
continue
}
queue = append(queue, autoStartItem{userKey: user, url: u})
queued[user] = true
}
// starte max. einen Job pro Loop (mit Abstand)
if len(queue) > 0 && (lastStart.IsZero() || time.Since(lastStart) >= startGap) {
it := queue[0]
queue = queue[1:]
delete(queued, it.userKey)
lastTry[it.userKey] = time.Now()
_, err := startRecordingInternal(RecordRequest{
URL: it.url,
Cookie: cookieHdr,
})
if err != nil {
fmt.Println("❌ [autostart] start failed:", it.url, err)
} else {
fmt.Println("▶️ [autostart] started:", it.url)
lastStart = time.Now()
}
}
time.Sleep(pollInterval)
}
}