This commit is contained in:
Linrador 2026-05-02 11:02:45 +02:00
parent de4bfe8cef
commit 973a07fef8
4 changed files with 74 additions and 23 deletions

View File

@ -18,13 +18,22 @@ var autostartPaused int32 // 0=false, 1=true
var autostartSubsMu sync.Mutex var autostartSubsMu sync.Mutex
type autostartStatePayload struct { type autostartStatePayload struct {
Paused bool `json:"paused"` Enabled bool `json:"enabled"`
PausedByUser bool `json:"pausedByUser"` Active bool `json:"active"`
PausedByDisk bool `json:"pausedByDisk"` Paused bool `json:"paused"`
PausedByUser bool `json:"pausedByUser"`
PausedByDisk bool `json:"pausedByDisk"`
Reason string `json:"reason,omitempty"`
} }
var autostartSubs = map[chan autostartStatePayload]struct{}{} var autostartSubs = map[chan autostartStatePayload]struct{}{}
func isAutostartFeatureEnabled() bool {
s := getSettings()
return s.UseChaturbateAPI || s.UseMyFreeCamsWatcher
}
func broadcastAutostartPaused() { func broadcastAutostartPaused() {
state := getAutostartStatePayload() state := getAutostartStatePayload()
@ -50,13 +59,28 @@ func isAutostartPaused() bool {
} }
func getAutostartStatePayload() autostartStatePayload { func getAutostartStatePayload() autostartStatePayload {
enabled := isAutostartFeatureEnabled()
userPaused := atomic.LoadInt32(&autostartPaused) == 1 userPaused := atomic.LoadInt32(&autostartPaused) == 1
diskPaused := atomic.LoadInt32(&diskEmergency) == 1 diskPaused := atomic.LoadInt32(&diskEmergency) == 1
paused := userPaused || diskPaused
reason := ""
switch {
case !enabled:
reason = "disabled"
case diskPaused:
reason = "disk"
case userPaused:
reason = "user"
}
return autostartStatePayload{ return autostartStatePayload{
Paused: userPaused || diskPaused, Enabled: enabled,
Active: enabled && !paused,
Paused: paused,
PausedByUser: userPaused, PausedByUser: userPaused,
PausedByDisk: diskPaused, PausedByDisk: diskPaused,
Reason: reason,
} }
} }
@ -138,19 +162,22 @@ func autostartStateHandler(w http.ResponseWriter, r *http.Request) {
writeAutostartState(w) writeAutostartState(w)
} }
// GET/POST /api/autostart/pause (POST ohne Body pausiert) // GET/POST /api/autostart/pause
// POST muss explizit paused=true/false enthalten.
// Wichtig: Kein "POST ohne Body pausiert" mehr, weil das beim Frontend-Refresh
// unbemerkt den Autostart stoppen kann.
func autostartPauseQuickHandler(w http.ResponseWriter, r *http.Request) { func autostartPauseQuickHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method { autostartPauseHandler(w, r)
case http.MethodGet: }
writeAutostartState(w)
return func resetAutostartPauseOnStartup() {
case http.MethodPost: old := getAutostartStatePayload()
setAutostartPaused(true)
writeAutostartState(w) atomic.StoreInt32(&autostartPaused, 0)
return
default: newState := getAutostartStatePayload()
http.Error(w, "Nur GET/POST erlaubt", http.StatusMethodNotAllowed) if old != newState {
return broadcastAutostartPaused()
} }
} }

View File

@ -98,6 +98,11 @@ func main() {
} }
defer appLn.Close() defer appLn.Close()
// ✅ Hier: alte manuelle Autostart-Pause beim echten App-Start zurücksetzen.
// Disk-Notbremse bleibt trotzdem aktiv, weil isAutostartPaused()
// zusätzlich diskEmergency prüft.
resetAutostartPauseOnStartup()
fixKeepRootFilesIntoModelSubdirs() fixKeepRootFilesIntoModelSubdirs()
postWorkQ.StartWorkers(1) postWorkQ.StartWorkers(1)
@ -137,10 +142,10 @@ func main() {
} }
// Hintergrund-Worker // Hintergrund-Worker
go startDiskSpaceGuard()
go startChaturbateOnlinePoller(store) go startChaturbateOnlinePoller(store)
go startChaturbateAutoStartWorker(store) go startChaturbateAutoStartWorker(store)
go startMyFreeCamsAutoStartWorker(store) go startMyFreeCamsAutoStartWorker(store)
go startDiskSpaceGuard()
// optional: Bootstrap nur im Hintergrund // optional: Bootstrap nur im Hintergrund
if getSettings().UseChaturbateAPI { if getSettings().UseChaturbateAPI {

View File

@ -642,7 +642,7 @@ function TrainingBeakerIcon(props: React.ComponentProps<typeof BeakerIcon>) {
} }
export default function App() { export default function App() {
const [trainingTabRunning, setTrainingTabRunning] = useState(false) const [trainingTabRunning, setTrainingTabRunning] = useState(false)
const [splitProgressByFile, setSplitProgressByFile] = useState<Record<string, BackgroundSplitProgress>>({}) const [splitProgressByFile, setSplitProgressByFile] = useState<Record<string, BackgroundSplitProgress>>({})
const splitProgressClearTimersRef = useRef<Record<string, number>>({}) const splitProgressClearTimersRef = useRef<Record<string, number>>({})
@ -2196,9 +2196,6 @@ export default function App() {
prev?.id === updated.id ? mergeStoredModel(prev, updated) : prev prev?.id === updated.id ? mergeStoredModel(prev, updated) : prev
) )
setPlayerModel((prev) =>
prev?.id === updated.id ? mergeStoredModel(prev, updated) : prev
)
scheduleOverviewLoad() scheduleOverviewLoad()
return return
} }
@ -3255,7 +3252,6 @@ export default function App() {
fillDonePageFromPrefetch, fillDonePageFromPrefetch,
donePage, donePage,
prefetchDonePage, prefetchDonePage,
doneCount,
loadDoneCount, loadDoneCount,
] ]
) )
@ -4009,7 +4005,11 @@ export default function App() {
doneTotal={doneCount} doneTotal={doneCount}
page={donePage} page={donePage}
pageSize={donePageSize} pageSize={donePageSize}
onPageSizeChange={setDonePageSize} onPageSizeChange={(size) => {
setDonePageSize(size)
setDonePage(1)
donePrefetchRef.current = null
}}
onPageChange={setDonePage} onPageChange={setDonePage}
onOpenPlayer={openPlayer} onOpenPlayer={openPlayer}
onDeleteJob={handleDeleteJobWithUndo} onDeleteJob={handleDeleteJobWithUndo}

View File

@ -18,6 +18,7 @@ type RecorderSettings = {
doneDir: string doneDir: string
ffmpegPath?: string ffmpegPath?: string
autoAddToDownloadList?: boolean autoAddToDownloadList?: boolean
autoStartAddedDownloads?: boolean
enableConcurrentDownloadsLimit?: boolean enableConcurrentDownloadsLimit?: boolean
maxConcurrentDownloads?: number maxConcurrentDownloads?: number
useChaturbateApi?: boolean useChaturbateApi?: boolean
@ -58,6 +59,7 @@ const DEFAULTS: RecorderSettings = {
doneDir: 'records/done', doneDir: 'records/done',
ffmpegPath: '', ffmpegPath: '',
autoAddToDownloadList: true, autoAddToDownloadList: true,
autoStartAddedDownloads: true,
enableConcurrentDownloadsLimit: false, enableConcurrentDownloadsLimit: false,
maxConcurrentDownloads: 20, maxConcurrentDownloads: 20,
useChaturbateApi: false, useChaturbateApi: false,
@ -396,6 +398,8 @@ export default function RecorderSettings({ onAssetsGenerated }: Props) {
// ✅ falls backend die Felder noch nicht hat -> defaults nutzen // ✅ falls backend die Felder noch nicht hat -> defaults nutzen
autoAddToDownloadList: data.autoAddToDownloadList ?? DEFAULTS.autoAddToDownloadList, autoAddToDownloadList: data.autoAddToDownloadList ?? DEFAULTS.autoAddToDownloadList,
autoStartAddedDownloads:
(data as any).autoStartAddedDownloads ?? DEFAULTS.autoStartAddedDownloads,
enableConcurrentDownloadsLimit: enableConcurrentDownloadsLimit:
(data as any).enableConcurrentDownloadsLimit ?? DEFAULTS.enableConcurrentDownloadsLimit, (data as any).enableConcurrentDownloadsLimit ?? DEFAULTS.enableConcurrentDownloadsLimit,
maxConcurrentDownloads: maxConcurrentDownloads:
@ -690,6 +694,7 @@ export default function RecorderSettings({ onAssetsGenerated }: Props) {
// ✅ Switch-Logik: Autostart nur sinnvoll, wenn Auto-Add aktiv ist // ✅ Switch-Logik: Autostart nur sinnvoll, wenn Auto-Add aktiv ist
const autoAddToDownloadList = !!value.autoAddToDownloadList const autoAddToDownloadList = !!value.autoAddToDownloadList
const autoStartAddedDownloads = !!value.autoStartAddedDownloads
const enableConcurrentDownloadsLimit = !!(value as any).enableConcurrentDownloadsLimit const enableConcurrentDownloadsLimit = !!(value as any).enableConcurrentDownloadsLimit
const maxConcurrentDownloads = Math.max( const maxConcurrentDownloads = Math.max(
1, 1,
@ -736,6 +741,7 @@ export default function RecorderSettings({ onAssetsGenerated }: Props) {
doneDir, doneDir,
ffmpegPath, ffmpegPath,
autoAddToDownloadList, autoAddToDownloadList,
autoStartAddedDownloads,
enableConcurrentDownloadsLimit, enableConcurrentDownloadsLimit,
maxConcurrentDownloads, maxConcurrentDownloads,
useChaturbateApi, useChaturbateApi,
@ -1284,6 +1290,19 @@ export default function RecorderSettings({ onAssetsGenerated }: Props) {
description="Neue Links/Modelle werden automatisch in die Downloadliste übernommen." description="Neue Links/Modelle werden automatisch in die Downloadliste übernommen."
/> />
<LabeledSwitch
checked={!!value.autoStartAddedDownloads}
onChange={(checked) =>
setValue((v) => ({
...v,
autoStartAddedDownloads: checked,
autoAddToDownloadList: checked ? true : v.autoAddToDownloadList,
}))
}
label="Automatisch starten"
description="Neue Links aus der Zwischenablage werden automatisch gestartet. Aktiviert automatisch auch das Hinzufügen zur Downloadliste."
/>
<LabeledSwitch <LabeledSwitch
checked={!!value.useChaturbateApi} checked={!!value.useChaturbateApi}
onChange={(checked) => setValue((v) => ({ ...v, useChaturbateApi: checked }))} onChange={(checked) => setValue((v) => ({ ...v, useChaturbateApi: checked }))}