// backend\tasks_release_file.go package main import ( "encoding/json" "net/http" "path/filepath" "strings" "time" ) type releaseFileTasksRequest struct { File string `json:"file"` } type releaseFileTasksResponse struct { OK bool `json:"ok"` File string `json:"file"` ReleasedAny bool `json:"releasedAny"` AssetsTaskCancelled bool `json:"assetsTaskCancelled"` RegenerateCancelled bool `json:"regenerateCancelled"` PostworkCancelled bool `json:"postworkCancelled"` EnrichCancelled bool `json:"enrichCancelled"` } func assetIDFromAnyFileName(file string) string { base := strings.TrimSpace(filepath.Base(strings.TrimSpace(file))) if base == "" { return "" } stem := strings.TrimSuffix(base, filepath.Ext(base)) return strings.TrimSpace(stripHotPrefix(stem)) } func sameBaseFile(a, b string) bool { aa := strings.TrimSpace(filepath.Base(strings.TrimSpace(a))) bb := strings.TrimSpace(filepath.Base(strings.TrimSpace(b))) if aa == "" || bb == "" { return false } return strings.EqualFold(aa, bb) } func cancelAssetsTaskForFile(file string) bool { assetsTaskMu.Lock() cancel := assetsTaskCancel running := assetsTaskState.Running currentFile := assetsTaskState.CurrentFile assetsTaskMu.Unlock() if !running || cancel == nil { return false } if !sameBaseFile(currentFile, file) { return false } cancel() return true } func cancelRegenerateAssetsTaskForFile(file string) bool { regenerateAssetsMu.Lock() var matched []*regenerateAssetsJob for _, job := range regenerateAssetsJobs { if job == nil { continue } if !sameBaseFile(job.File, file) { continue } matched = append(matched, job) } regenerateAssetsMu.Unlock() cancelled := false for _, job := range matched { if job != nil && job.Cancel != nil { job.Cancel() cancelled = true } } return cancelled } func cancelQueuesForFile(file string) (postworkCancelled bool, enrichCancelled bool) { file = strings.TrimSpace(file) // 1) Direkt aus Dateiname ableiten: // Für laufende Sprites ist das meistens der relevante Queue-Key. if assetID := assetIDFromAnyFileName(file); assetID != "" { enrichKey := "enrich:" + assetID if enrichQ.Cancel(enrichKey) { enrichCancelled = true } if enrichQ.RemoveQueued(enrichKey) { enrichCancelled = true } clearEnrichStatusSnapshot(enrichKey) } // 2) Zusätzlich wie bisher über jobs suchen type queueKeys struct { postworkKey string enrichKey string } matches := make([]queueKeys, 0, 8) jobsMu.RLock() for _, job := range jobs { if job == nil { continue } if !sameBaseFile(job.Output, file) { continue } postworkKey := strings.TrimSpace(job.PostWorkKey) enrichKey := "" if id := strings.TrimSpace(assetIDForJob(job)); id != "" { enrichKey = "enrich:" + id } matches = append(matches, queueKeys{ postworkKey: postworkKey, enrichKey: enrichKey, }) } jobsMu.RUnlock() for _, item := range matches { if item.postworkKey != "" { if postWorkQ.Cancel(item.postworkKey) { postworkCancelled = true } if postWorkQ.RemoveQueued(item.postworkKey) { postworkCancelled = true } } if item.enrichKey != "" { if enrichQ.Cancel(item.enrichKey) { enrichCancelled = true } if enrichQ.RemoveQueued(item.enrichKey) { enrichCancelled = true } clearEnrichStatusSnapshot(item.enrichKey) } } return postworkCancelled, enrichCancelled } func releaseFileTasks(file string) releaseFileTasksResponse { file = strings.TrimSpace(file) resp := releaseFileTasksResponse{ OK: true, File: file, } if file == "" { return resp } resp.AssetsTaskCancelled = cancelAssetsTaskForFile(file) resp.RegenerateCancelled = cancelRegenerateAssetsTaskForFile(file) resp.PostworkCancelled, resp.EnrichCancelled = cancelQueuesForFile(file) resp.ReleasedAny = resp.AssetsTaskCancelled || resp.RegenerateCancelled || resp.PostworkCancelled || resp.EnrichCancelled // ffmpeg / Analyse / Sprite-Generierung einen Moment zum Beenden geben if resp.ReleasedAny { time.Sleep(900 * time.Millisecond) } return resp } func handleReleaseFileTasks(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Nur POST erlaubt", http.StatusMethodNotAllowed) return } var req releaseFileTasksRequest if r.Body != nil { _ = json.NewDecoder(r.Body).Decode(&req) } file := strings.TrimSpace(req.File) if file == "" { file = strings.TrimSpace(r.URL.Query().Get("file")) } if file == "" { http.Error(w, "file fehlt", http.StatusBadRequest) return } writeJSON(w, http.StatusOK, releaseFileTasks(file)) }