186 lines
4.1 KiB
Go
186 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Startet watched MyFreeCams Models (ohne API) "best-effort".
|
|
// Wenn nach kurzer Zeit keine Output-Datei existiert (oder 0 Bytes), wird abgebrochen und der Job wieder entfernt.
|
|
func startMyFreeCamsAutoStartWorker(store *ModelStore) {
|
|
if store == nil {
|
|
return
|
|
}
|
|
|
|
// pro Model: Retry-Cooldown, damit du nicht permanent die gleichen Models spamst
|
|
const cooldown = 2 * time.Minute
|
|
|
|
// wie lange wir nach Start warten, ob eine Datei entsteht
|
|
const outputProbeMax = 12 * time.Second
|
|
|
|
lastAttempt := map[string]time.Time{}
|
|
|
|
tick := time.NewTicker(6 * time.Second)
|
|
defer tick.Stop()
|
|
|
|
for range tick.C {
|
|
s := getSettings()
|
|
if !s.UseMyFreeCamsWatcher {
|
|
continue
|
|
}
|
|
|
|
// watched Models aus DB
|
|
watched := store.ListWatchedLite("myfreecams.com")
|
|
if len(watched) == 0 {
|
|
continue
|
|
}
|
|
|
|
// langsam nacheinander starten (keine API -> einzelnes "Anprobieren")
|
|
for _, m := range watched {
|
|
|
|
// ✅ Wenn User den Switch während eines Ticks deaktiviert, sofort stoppen
|
|
if !getSettings().UseMyFreeCamsWatcher {
|
|
break
|
|
}
|
|
|
|
// ✅ Wenn im UI "Alle Stoppen" -> Autostart pausiert, sofort aufhören
|
|
if isAutostartPaused() {
|
|
break
|
|
}
|
|
|
|
u := strings.TrimSpace(m.Input)
|
|
if u == "" {
|
|
continue
|
|
}
|
|
|
|
modelID := strings.TrimSpace(m.ID)
|
|
if modelID == "" {
|
|
// Fallback
|
|
modelID = strings.TrimSpace(m.Host) + ":" + strings.TrimSpace(m.ModelKey)
|
|
}
|
|
|
|
// Cooldown
|
|
if t, ok := lastAttempt[modelID]; ok && time.Since(t) < cooldown {
|
|
continue
|
|
}
|
|
|
|
// bereits als Job aktiv?
|
|
if isJobRunningForURL(u) {
|
|
continue
|
|
}
|
|
|
|
lastAttempt[modelID] = time.Now()
|
|
|
|
job, err := startRecordingInternal(RecordRequest{URL: u, Hidden: true})
|
|
if err != nil || job == nil {
|
|
continue
|
|
}
|
|
|
|
// Output prüfen: wenn nichts entsteht -> abbrechen + aus jobs entfernen
|
|
go mfcAbortIfNoOutput(job.ID, outputProbeMax)
|
|
|
|
// kleine Pause, damit du nicht 20 Models in einem Tick startest
|
|
time.Sleep(1200 * time.Millisecond)
|
|
}
|
|
}
|
|
}
|
|
|
|
func isJobRunningForURL(u string) bool {
|
|
u = strings.TrimSpace(u)
|
|
if u == "" {
|
|
return false
|
|
}
|
|
|
|
jobsMu.Lock()
|
|
defer jobsMu.Unlock()
|
|
|
|
for _, j := range jobs {
|
|
if j == nil {
|
|
continue
|
|
}
|
|
if j.Status == JobRunning && strings.TrimSpace(j.SourceURL) == u {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Wenn nach maxWait keine Output-Datei (>0 Bytes) existiert, stoppen + Job entfernen.
|
|
// Hintergrund: bei MFC kann "offline/away/private" sein => keine Ausgabe entsteht.
|
|
func mfcAbortIfNoOutput(jobID string, maxWait time.Duration) {
|
|
deadline := time.Now().Add(maxWait)
|
|
|
|
for time.Now().Before(deadline) {
|
|
jobsMu.Lock()
|
|
job := jobs[jobID]
|
|
status := JobStatus("")
|
|
out := ""
|
|
if job != nil {
|
|
status = job.Status
|
|
out = strings.TrimSpace(job.Output)
|
|
}
|
|
jobsMu.Unlock()
|
|
|
|
// Job schon weg oder nicht mehr running -> nix tun
|
|
if job == nil || status != JobRunning {
|
|
return
|
|
}
|
|
|
|
// Output schon da?
|
|
if out != "" {
|
|
if fi, err := os.Stat(out); err == nil && !fi.IsDir() && fi.Size() > 0 {
|
|
// ✅ jetzt ist es ein "echter" Download -> im UI sichtbar machen
|
|
publishJob(jobID)
|
|
return
|
|
}
|
|
}
|
|
|
|
time.Sleep(900 * time.Millisecond)
|
|
}
|
|
|
|
// nach Wartezeit immer noch keine Datei => stoppen + löschen
|
|
jobsMu.Lock()
|
|
job := jobs[jobID]
|
|
if job == nil || job.Status != JobRunning {
|
|
jobsMu.Unlock()
|
|
return
|
|
}
|
|
|
|
// Snapshot: was wir ohne Lock beenden können
|
|
pc := job.previewCmd
|
|
job.previewCmd = nil
|
|
cancel := job.cancel
|
|
out := strings.TrimSpace(job.Output)
|
|
jobsMu.Unlock()
|
|
|
|
// preview kill
|
|
if pc != nil && pc.Process != nil {
|
|
_ = pc.Process.Kill()
|
|
}
|
|
// recording cancel
|
|
if cancel != nil {
|
|
cancel()
|
|
}
|
|
|
|
// 0-Byte Datei ggf. wegwerfen (damit "leere Starts" nicht im recordDir liegen bleiben)
|
|
if out != "" {
|
|
if fi, err := os.Stat(out); err == nil && !fi.IsDir() && fi.Size() == 0 {
|
|
_ = os.Remove(out)
|
|
}
|
|
}
|
|
|
|
// Job aus der Liste entfernen (UI bleibt sauber)
|
|
jobsMu.Lock()
|
|
j := jobs[jobID]
|
|
wasVisible := (j != nil && !j.Hidden)
|
|
delete(jobs, jobID)
|
|
jobsMu.Unlock()
|
|
|
|
// ✅ wenn der Job nie sichtbar war, nicht unnötig UI refreshen
|
|
if wasVisible {
|
|
notifyJobsChanged()
|
|
}
|
|
|
|
}
|