220 lines
4.7 KiB
Go
220 lines
4.7 KiB
Go
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 {
|
||
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)
|
||
}
|
||
}
|