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) } }