added sprites check

This commit is contained in:
Linrador 2026-05-22 14:11:00 +02:00
parent cc2befe093
commit c9f2086b79
5 changed files with 143 additions and 6 deletions

View File

@ -166,6 +166,12 @@ func ensureAnalyzeAllGoalsForVideoCtxForce(
return false, appErrorf("videolänge konnte nicht bestimmt werden")
}
if skip, reason, err := checkPreviewSpriteBeforeAnalysis(actx, videoPath); err != nil {
return false, err
} else if skip {
return false, skipAnalysisBecauseBadSpriteError(reason)
}
hits, err := analyzeVideoFromFrames(actx, videoPath)
if err != nil {
return false, err

View File

@ -1619,10 +1619,18 @@ func runDeferredEnrichPipeline(ctx context.Context, outPath string, sourceURL st
// Ohne Sprite kann keine Sprite-basierte Analyse laufen.
if !truth.SpritesReady {
if firstErr == nil {
firstErr = appErrorf("Analysen übersprungen: preview-sprite.jpg fehlt")
}
return firstErr
err := skipAnalysisBecauseBadSpriteError("preview-sprite.jpg fehlt")
publishDeferredPhase(
fileName,
assetID,
"enrich",
"analyze",
"error",
"Analyse übersprungen",
err,
)
return nil
}
// -------------------------------------------------
@ -1643,6 +1651,22 @@ func runDeferredEnrichPipeline(ctx context.Context, outPath string, sourceURL st
return cerr
}
if analysisSkippedByBadSprite(err) {
publishDeferredPhase(
fileName,
assetID,
"enrich",
"analyze",
"error",
"Analyse übersprungen",
err,
)
// Wichtig: nicht als Queue-Fehler zurückgeben,
// sonst wird die enrichQ unnötig weiter belastet/retryt.
return nil
}
analyzeReady = hasAIResultsForAllOutputGoals(outPath, requiredGoals)
if err != nil || !analyzeReady {

View File

@ -916,6 +916,19 @@ func runGenerateMissingAssetsJob(jobID string, ctx context.Context) {
publishAssetsTaskPhase(it.name, "enrich", "analyze", "running", "Analyse")
_, aerr := ensureAnalyzeForVideoCtx(fileCtx, it.path, sourceURL, "highlights")
if analysisSkippedByBadSprite(aerr) {
publishFinishedPostworkPhase(
it.name,
id,
"enrich",
"analyze",
"error",
"Analyse übersprungen",
aerr.Error(),
)
continue
}
if skipFile, stopAll := handleFileAbort(aerr); stopAll {
return
} else if skipFile {

View File

@ -703,3 +703,81 @@ func runTailBlackoutCheck(ctx context.Context, videoPath string) (*tailBlackoutR
return res, nil
}
var errAnalysisSkippedByBadSprite = errors.New("analysis skipped because preview-sprite.jpg is invalid")
func analysisSkippedByBadSprite(err error) bool {
return errors.Is(err, errAnalysisSkippedByBadSprite)
}
func skipAnalysisBecauseBadSpriteError(reason string) error {
reason = strings.TrimSpace(reason)
if reason == "" {
reason = "preview-sprite.jpg ist fehlerhaft"
}
return fmt.Errorf("%w: %s", errAnalysisSkippedByBadSprite, reason)
}
func persistBadPreviewSpriteReason(videoPath string, reason string) {
reason = strings.TrimSpace(reason)
if reason == "" {
reason = "preview-sprite.jpg ist fehlerhaft"
}
err := persistTailBlackoutResult(videoPath, &tailBlackoutResult{
CheckedAt: time.Now().Format(time.RFC3339Nano),
IsCorrupt: true,
Reason: reason,
})
if err != nil {
appLogln("⚠️ preview-sprite validation persist failed:", videoPath, err)
}
}
func checkPreviewSpriteBeforeAnalysis(
ctx context.Context,
videoPath string,
) (skip bool, reason string, err error) {
videoPath = strings.TrimSpace(videoPath)
if videoPath == "" {
return true, "videoPath ist leer", nil
}
if err := ctx.Err(); err != nil {
return false, "", err
}
assetID := assetIDFromVideoPath(videoPath)
if assetID == "" {
return true, "asset id fehlt", nil
}
_, _, _, spritePath, _, perr := assetPathsForID(assetID)
if perr != nil {
return true, perr.Error(), nil
}
if !fileExistsNonEmpty(spritePath) {
reason := "preview-sprite.jpg fehlt oder ist leer"
persistBadPreviewSpriteReason(videoPath, reason)
return true, reason, nil
}
res, checkErr := runTailBlackoutCheck(ctx, videoPath)
if checkErr != nil {
reason := "preview-sprite.jpg konnte nicht geprüft werden: " + checkErr.Error()
persistBadPreviewSpriteReason(videoPath, reason)
return true, reason, nil
}
if res != nil && res.IsCorrupt {
reason := strings.TrimSpace(res.Reason)
if reason == "" {
reason = "preview-sprite.jpg deutet auf beschädigtes Videoende hin"
}
return true, reason, nil
}
return false, "", nil
}

View File

@ -466,8 +466,6 @@ func runRegenerateAssetsJob(job *regenerateAssetsJob) {
publishFinishedPostworkPhase(file, id, "postwork", "sprites", "done", "Sprites", "")
}
publishFinishedPostworkPhase(file, id, "postwork", "sprites", "done", "Sprites", "")
// 5) Analyse
publishFinishedPostworkPhase(file, id, "enrich", "analyze", "running", "Analyse", "")
@ -476,6 +474,24 @@ func runRegenerateAssetsJob(job *regenerateAssetsJob) {
cancelJob()
return
}
if analysisSkippedByBadSprite(err) {
publishFinishedPostworkPhase(
file,
id,
"enrich",
"analyze",
"error",
"Analyse übersprungen",
err.Error(),
)
finishRegenerateAssetsJob(file, "done", "")
setRegenerateAssetsTaskDone(file, "Fertig, Analyse übersprungen")
removeRegenerateAssetsJobLater(file, 3*time.Second)
return
}
failJob("enrich", "analyze", err.Error())
return
}