From fa40821319a803a08958e7216ec91ed08c9a06a6 Mon Sep 17 00:00:00 2001 From: Melbar Date: Mon, 18 May 2026 08:48:26 +0200 Subject: [PATCH] Update cutter report --- CUTTER_REPORT.html | 2 +- CUTTER_REPORT.md | 2 +- cli.py | 165 +++++++++++++++++++++++++++++++++++++++++++++ docs/ALGORITHM.md | 5 ++ 4 files changed, 172 insertions(+), 2 deletions(-) diff --git a/CUTTER_REPORT.html b/CUTTER_REPORT.html index 1d8392d..8ac44cd 100644 --- a/CUTTER_REPORT.html +++ b/CUTTER_REPORT.html @@ -133,4 +133,4 @@ table.ov tr:hover { background: rgba(255, 255, 255, 0.05); }
-

Cutter & Match Report

Generiert: 2026-05-09 19:06:01Trailer: BehindTheRedDoor_Trailer_REFERENCE.mp4 @ 25.000 fpsSource: BehindTheRedDoor_FTR_1080P_2398_Fixed.mp4 @ 23.976 fps
25 Beats — 24 automatisch (6 bestätigt)1 manuell.
Recent Changes:
Mark trailer title cards as graphics

Legende

OKBestätigt — direkt in Schnitt-Timeline übernehmen
?Vorläufig — Phase und Aktion im NLE visuell prüfen
GFXTitel-/Grafikkarte — als Trailer-Grafik übernehmen, nicht im Spielfilm suchen
MAN.Kein Treffer — manuell suchen oder Schwarzbild einfügen

Übersicht

BeatTrailer TC In–OutDauerSource TC InSceneScoreStatus
0000:00:00:00–00:00:03:003.00s00:00:03:1910.597?
0100:00:03:00–00:00:08:105.40s00:00:04:0910.380?
0200:00:08:10–00:00:16:248.56s00:00:35:0530.761OK
0300:00:16:24–00:00:19:032.16s01:02:19:034360.572?
0400:00:19:03–00:00:20:161.52s01:02:21:024370.777OK
0500:00:20:16–00:00:26:095.72s00:01:33:03100.499?
0600:00:26:09–00:00:29:062.88s00:01:03:0750.396?
0700:00:29:06–00:00:31:172.44s01:20:10:105530.497?
0800:00:31:17–00:00:33:161.96s00:00:51:0750.620?
0900:00:33:16–00:00:36:193.12s01:20:29:035570.674OK
1000:00:36:19–00:00:40:023.32s01:20:35:16558+559+5560.660?
1100:00:40:02–00:00:42:032.04s01:20:40:185590.636?
1200:00:42:03–00:00:50:068.12s01:14:26:06519+130.701OK
1300:00:50:06–00:00:53:213.60s00:43:19:133080.636?
1400:00:53:21–00:00:57:023.24s00:43:24:163090.626?
1500:00:57:02–00:01:01:124.40s00:02:10:0817+3090.708?
1600:01:01:12–00:01:04:123.00s01:05:12:124510.626?
1700:01:04:12–00:01:09:034.64s01:31:18:04623+720.399?
1800:01:09:03–00:01:10:191.64s00:09:13:13750.542?
1900:01:10:19–00:01:12:131.76s00:16:48:131260.403?
2000:01:12:13–00:01:15:143.04s01:27:51:126130.711OK
2100:01:15:14–00:01:17:131.96s01:36:22:086370.661OK
2200:01:17:13–00:01:19:232.40s01:03:05:114420.544?
2300:01:19:23–00:01:25:145.64s01:04:35:214460.534?
2400:01:25:14–00:01:32:076.72sGFX

Beat-Details

Beat 00

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:00:00–00:00:03:00  (3.00s)
Phase logo animation assembling from distorted shapes with motion blur
Bild centered, symmetrical, abstract black void
Source
00:00:03:19–00:00:05:09
Scene 1 · Score 0.597
⚠ Score 0.597 unter 0.65 — visuell prüfen
python cli.py rematch --beat 0

Beat 01

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:03:00–00:00:08:10  (5.40s)
Phase Dynamic motion blur and shifting optical distortions across the text
Bild Centered, symmetrical layout with overlapping circular glass-like distortions, Abstract black void
Source
00:00:04:09–00:00:06:03
Scene 1 · Score 0.380
⚠ Score 0.380 unter 0.65 — visuell prüfen
python cli.py rematch --beat 1

Beat 02

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:08:10–00:00:16:24  (8.56s)
Phase drawing a heart shape on a foggy surface
Bild extreme close-up, shifting from profile of a face to a hand interacting with a surface, indoor, obscured glass or foggy surface
Source
00:00:35:05–00:00:43:06
Scene 3 · Score 0.761
python cli.py rematch --beat 2

Beat 03

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:16:24–00:00:19:03  (2.16s)
Phase speaking and smiling slightly
Bild close-up shot, centered face, outdoor rocky environment
Source
01:02:19:03–01:02:20:20
Scene 436 · Score 0.572
⚠ Score 0.572 unter 0.65 — visuell prüfen
python cli.py rematch --beat 3

Beat 04

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:19:03–00:00:20:16  (1.52s)
Phase lifting a camera to eye level to take a photo
Bild medium shot, subject centered, shallow depth of field with blurred treeline background, outdoor forest landscape, overcast sky
Source
01:02:21:02–01:02:22:14
Scene 437 · Score 0.777
python cli.py rematch --beat 4

Beat 05

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:20:16–00:00:26:09  (5.72s)
Phase a metallic cylindrical object is brought toward and touches the skin of the forearm
Bild extreme close-up, shallow depth of field, indistinct dark interior
Source
00:01:33:03–00:01:37:09
Scene 10 · Score 0.499
⚠ Score 0.499 unter 0.65 — visuell prüfen
python cli.py rematch --beat 5

Beat 06

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:26:09–00:00:29:06  (2.88s)
Phase hand reaching toward and touching an ornate decorative box
Bild close-up, shallow depth of field, hand entering from left frame, dark interior
Source
00:01:03:07–00:01:05:03
Scene 5 · Score 0.396
⚠ Score 0.396 unter 0.65 — visuell prüfen
python cli.py rematch --beat 6

Beat 07

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:29:06–00:00:31:17  (2.44s)
Phase man appears to be engaged in conversation
Bild man in a light gray sweater and scarf, seated on a white couch, with a window in the background, indoor with a view of the ocean
Source
01:20:10:10–01:20:12:14
Scene 553 · Score 0.497
⚠ Score 0.497 unter 0.65 — visuell prüfen
python cli.py rematch --beat 7

Beat 08

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:31:17–00:00:33:16  (1.96s)
Phase static or slow drifting
Bild close-up, diagonal curve from top-left to bottom-center, dark, indistinct void
Source
00:00:51:07–00:00:53:01
Scene 5 · Score 0.620
⚠ Score 0.620 unter 0.65 — visuell prüfen
python cli.py rematch --beat 8

Beat 09

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:33:16–00:00:36:19  (3.12s)
Phase speaking, transitioning from closed eyes to open mouth and focused gaze
Bild medium close-up, subject positioned right of center, profile/three-quarter view, indoor room next to a large window overlooking a blue horizon/sea
Source
01:20:29:03–01:20:32:06
Scene 557 · Score 0.674
python cli.py rematch --beat 9

Beat 10

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:36:19–00:00:40:02  (3.32s)
Phase conversation
Bild alternating close-ups and a medium two-shot, indoor living room with large windows showing a blue exterior landscape
Source
01:20:35:16  (multi-shot)
Scene Scenes 558, 559, 556 · 3 Segmente
  • Seg 1: 01:20:35:16  dur 0.88s  @ off 0.00s  sc 558  score 0.674
  • Seg 2: 01:20:36:14  dur 1.76s  @ off 0.88s  sc 559  score 0.649
  • Seg 3: 01:20:22:14  dur 0.68s  @ off 2.64s  sc 556  score 0.672
python cli.py rematch --beat 10

Beat 11

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:40:02–00:00:42:03  (2.04s)
Phase static talking head with slight facial expression changes
Bild medium close-up, subject positioned right of center, profile/three-quarter view facing left, indoor room with a large window showing a blue sea/horizon background
Source
01:20:40:18–01:20:42:18
Scene 559 · Score 0.636
⚠ Score 0.636 unter 0.65 — visuell prüfen
python cli.py rematch --beat 11

Beat 12

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:42:03–00:00:50:06  (8.12s)
Phase static profile shot transitioning to black/darkness
Bild medium close-up, profile view, subject positioned on the right side of the frame, dark outdoor environment, blurred trees in background
Source
01:14:26:06  (multi-shot)
Scene Scenes 519, 13 · 2 Segmente
  • Seg 1: 01:14:26:06  dur 3.52s  @ off 0.16s  sc 519  score 0.721
  • Seg 2: 00:01:47:14  dur 2.88s  @ off 4.88s  sc 13  score 0.676
python cli.py rematch --beat 12

Beat 13

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:50:06–00:00:53:21  (3.60s)
Phase static conversation; woman on right is standing and holding a cup
Bild wide shot, two figures positioned on opposite sides of a round dining table, modern glass-walled sunroom or conservatory overlooking a snowy landscape
Source
00:43:19:13–00:43:23:04
Scene 308 · Score 0.636
⚠ Score 0.636 unter 0.65 — visuell prüfen
python cli.py rematch --beat 13

Beat 14

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:53:21–00:00:57:02  (3.24s)
Phase static conversation, subject holding a white cup
Bild medium shot, subject positioned on the left, vertical window frame dividing the right third of the frame, interior room with a large window overlooking a snowy pine forest
Source
00:43:24:16–00:43:27:19
Scene 309 · Score 0.626
⚠ Score 0.626 unter 0.65 — visuell prüfen
python cli.py rematch --beat 14

Beat 15

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:57:02–00:01:01:12  (4.40s)
Phase static conversation
Bild medium shot, profile view of two characters facing each other, indoor room with a large window overlooking a snowy forest
Source
00:02:10:08  (multi-shot)
Scene Scenes 17, 309 · 2 Segmente
  • Seg 1: 00:02:10:08  dur 2.80s  @ off 0.24s  sc 17  score 0.720
  • Seg 2: 00:43:27:13  dur 1.28s  @ off 3.04s  sc 309  score 0.682
python cli.py rematch --beat 15

Beat 16

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:01:12–00:01:04:12  (3.00s)
Phase man reaches out and touches the red door with a small object
Bild medium side profile shot, subject on left, door on right, indoor dim environment, adjacent to a red wooden door
Source
01:05:12:12–01:05:15:07
Scene 451 · Score 0.626
⚠ Score 0.626 unter 0.65 — visuell prüfen
python cli.py rematch --beat 16

Beat 17

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:04:12–00:01:09:03  (4.64s)
Phase Static intimacy transitioning to a spatial arrangement of figures
Bild Medium shot, eye-level. First two frames: static shot of couple in bed. Third frame: wide shot of women among white blocks, Bedroom with bedside table and lamp; transition to a white minimalist interior with pedestals
Source
01:31:18:04  (multi-shot)
Scene Scenes 623, 72 · 3 Segmente
  • Seg 1: 01:31:18:04  dur 1.92s  @ off 0.24s  sc 623  score 0.384
  • Seg 2: 00:09:06:13  dur 1.04s  @ off 2.80s  sc 72  score 0.434
  • Seg 3: 00:09:07:18  dur 0.50s  @ off 4.00s  sc 72  score 0.384
⚠ Score 0.399 unter 0.65 — visuell prüfen
python cli.py rematch --beat 17

Beat 18

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:09:03–00:01:10:19  (1.64s)
Phase Woman in foreground turns her head from profile to face the camera while speaking
Bild Medium shot, three-quarter profile of woman in foreground left, two women positioned behind her to the right, Indoors, minimalist dark background
Source
00:09:13:13–00:09:15:05
Scene 75 · Score 0.542
⚠ Score 0.542 unter 0.65 — visuell prüfen
python cli.py rematch --beat 18

Beat 19

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:10:19–00:01:12:13  (1.76s)
Phase static conversation, subtle facial expression change
Bild medium close-up, over-the-shoulder shot with a blurred figure in the foreground right, outdoor dark forest or wooded area
Source
00:16:48:13–00:16:49:10
Scene 126 · Score 0.403
⚠ Score 0.403 unter 0.65 — visuell prüfen
python cli.py rematch --beat 19

Beat 20

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:12:13–00:01:15:14  (3.04s)
Phase man kisses woman's forehead, then they pull back slightly to face each other
Bild extreme close-up, profile view, faces facing each other, indoor, blurred background
Source
01:27:51:12–01:27:54:01
Scene 613 · Score 0.711
python cli.py rematch --beat 20

Beat 21

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:15:14–00:01:17:13  (1.96s)
Phase hand raised to mouth, slight facial movement
Bild extreme close-up, face partially obscured by shadow, dark interior
Source
01:36:22:08–01:36:23:14
Scene 637 · Score 0.661
python cli.py rematch --beat 21

Beat 22

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:17:13–00:01:19:23  (2.40s)
Phase man looks up and speaks, transitioning from downward gaze to forward gaze
Bild close-up, profile to three-quarter view, outdoor rocky environment, blurred background
Source
01:03:05:11–01:03:07:07
Scene 442 · Score 0.544
⚠ Score 0.544 unter 0.65 — visuell prüfen
python cli.py rematch --beat 22

Beat 23

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:19:23–00:01:25:14  (5.64s)
Phase static posture, head tilted upwards looking at the sky
Bild medium shot, subject positioned on the left third, facing away from camera towards the water, rocky shoreline next to a body of water under an overcast sky
Source
01:04:35:21–01:04:41:02
Scene 446 · Score 0.534
⚠ Score 0.534 unter 0.65 — visuell prüfen
python cli.py rematch --beat 23

Beat 24

GFXTitel/Grafik
Trailer 24
— manuell setzen —
Trailer
00:01:25:14–00:01:32:07  (6.72s)
— kein automatischer Treffer —
python cli.py rematch --beat 24
+

Cutter & Match Report

Generiert: 2026-05-18 08:46:49Trailer: BehindTheRedDoor_Trailer_REFERENCE.mp4 @ 25.000 fpsSource: BehindTheRedDoor_FTR_1080P_2398_Fixed.mp4 @ 23.976 fps
25 Beats — 24 automatisch (6 bestätigt)1 manuell.
Recent Changes:
Mark trailer title cards as graphics

Legende

OKBestätigt — direkt in Schnitt-Timeline übernehmen
?Vorläufig — Phase und Aktion im NLE visuell prüfen
GFXTitel-/Grafikkarte — als Trailer-Grafik übernehmen, nicht im Spielfilm suchen
MAN.Kein Treffer — manuell suchen oder Schwarzbild einfügen

Übersicht

BeatTrailer TC In–OutDauerSource TC InSceneScoreStatus
0000:00:00:00–00:00:03:003.00s00:00:03:1910.597?
0100:00:03:00–00:00:08:105.40s00:00:04:0910.380?
0200:00:08:10–00:00:16:248.56s00:00:35:0530.761OK
0300:00:16:24–00:00:19:032.16s01:02:19:034360.572?
0400:00:19:03–00:00:20:161.52s01:02:21:024370.777OK
0500:00:20:16–00:00:26:095.72s00:01:33:03100.499?
0600:00:26:09–00:00:29:062.88s00:01:03:0750.396?
0700:00:29:06–00:00:31:172.44s01:20:10:105530.497?
0800:00:31:17–00:00:33:161.96s00:00:51:0750.620?
0900:00:33:16–00:00:36:193.12s01:20:29:035570.674OK
1000:00:36:19–00:00:40:023.32s01:20:35:16558+559+5560.660?
1100:00:40:02–00:00:42:032.04s01:20:40:185590.636?
1200:00:42:03–00:00:50:068.12s01:14:26:06519+130.701OK
1300:00:50:06–00:00:53:213.60s00:43:19:133080.636?
1400:00:53:21–00:00:57:023.24s00:43:24:163090.626?
1500:00:57:02–00:01:01:124.40s00:02:10:0817+3090.708?
1600:01:01:12–00:01:04:123.00s01:05:12:124510.626?
1700:01:04:12–00:01:09:034.64s01:31:18:04623+720.399?
1800:01:09:03–00:01:10:191.64s00:09:13:13750.542?
1900:01:10:19–00:01:12:131.76s00:16:48:131260.403?
2000:01:12:13–00:01:15:143.04s01:27:51:126130.711OK
2100:01:15:14–00:01:17:131.96s01:36:22:086370.661OK
2200:01:17:13–00:01:19:232.40s01:03:05:114420.544?
2300:01:19:23–00:01:25:145.64s01:04:35:214460.534?
2400:01:25:14–00:01:32:076.72sGFX

Beat-Details

Beat 00

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:00:00–00:00:03:00  (3.00s)
Phase logo animation assembling from distorted shapes with motion blur
Bild centered, symmetrical, abstract black void
Source
00:00:03:19–00:00:05:09
Scene 1 · Score 0.597
⚠ Score 0.597 unter 0.65 — visuell prüfen
python cli.py rematch --beat 0

Beat 01

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:03:00–00:00:08:10  (5.40s)
Phase Dynamic motion blur and shifting optical distortions across the text
Bild Centered, symmetrical layout with overlapping circular glass-like distortions, Abstract black void
Source
00:00:04:09–00:00:06:03
Scene 1 · Score 0.380
⚠ Score 0.380 unter 0.65 — visuell prüfen
python cli.py rematch --beat 1

Beat 02

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:08:10–00:00:16:24  (8.56s)
Phase drawing a heart shape on a foggy surface
Bild extreme close-up, shifting from profile of a face to a hand interacting with a surface, indoor, obscured glass or foggy surface
Source
00:00:35:05–00:00:43:06
Scene 3 · Score 0.761
python cli.py rematch --beat 2

Beat 03

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:16:24–00:00:19:03  (2.16s)
Phase speaking and smiling slightly
Bild close-up shot, centered face, outdoor rocky environment
Source
01:02:19:03–01:02:20:20
Scene 436 · Score 0.572
⚠ Score 0.572 unter 0.65 — visuell prüfen
python cli.py rematch --beat 3

Beat 04

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:19:03–00:00:20:16  (1.52s)
Phase lifting a camera to eye level to take a photo
Bild medium shot, subject centered, shallow depth of field with blurred treeline background, outdoor forest landscape, overcast sky
Source
01:02:21:02–01:02:22:14
Scene 437 · Score 0.777
python cli.py rematch --beat 4

Beat 05

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:20:16–00:00:26:09  (5.72s)
Phase a metallic cylindrical object is brought toward and touches the skin of the forearm
Bild extreme close-up, shallow depth of field, indistinct dark interior
Source
00:01:33:03–00:01:37:09
Scene 10 · Score 0.499
⚠ Score 0.499 unter 0.65 — visuell prüfen
python cli.py rematch --beat 5

Beat 06

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:26:09–00:00:29:06  (2.88s)
Phase hand reaching toward and touching an ornate decorative box
Bild close-up, shallow depth of field, hand entering from left frame, dark interior
Source
00:01:03:07–00:01:05:03
Scene 5 · Score 0.396
⚠ Score 0.396 unter 0.65 — visuell prüfen
python cli.py rematch --beat 6

Beat 07

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:29:06–00:00:31:17  (2.44s)
Phase man appears to be engaged in conversation
Bild man in a light gray sweater and scarf, seated on a white couch, with a window in the background, indoor with a view of the ocean
Source
01:20:10:10–01:20:12:14
Scene 553 · Score 0.497
⚠ Score 0.497 unter 0.65 — visuell prüfen
python cli.py rematch --beat 7

Beat 08

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:31:17–00:00:33:16  (1.96s)
Phase static or slow drifting
Bild close-up, diagonal curve from top-left to bottom-center, dark, indistinct void
Source
00:00:51:07–00:00:53:01
Scene 5 · Score 0.620
⚠ Score 0.620 unter 0.65 — visuell prüfen
python cli.py rematch --beat 8

Beat 09

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:33:16–00:00:36:19  (3.12s)
Phase speaking, transitioning from closed eyes to open mouth and focused gaze
Bild medium close-up, subject positioned right of center, profile/three-quarter view, indoor room next to a large window overlooking a blue horizon/sea
Source
01:20:29:03–01:20:32:06
Scene 557 · Score 0.674
python cli.py rematch --beat 9

Beat 10

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:36:19–00:00:40:02  (3.32s)
Phase conversation
Bild alternating close-ups and a medium two-shot, indoor living room with large windows showing a blue exterior landscape
Source
01:20:35:16  (multi-shot)
Scene Scenes 558, 559, 556 · 3 Segmente
  • Seg 1: 01:20:35:16  dur 0.88s  @ off 0.00s  sc 558  score 0.674
  • Seg 2: 01:20:36:14  dur 1.76s  @ off 0.88s  sc 559  score 0.649
  • Seg 3: 01:20:22:14  dur 0.68s  @ off 2.64s  sc 556  score 0.672
python cli.py rematch --beat 10

Beat 11

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:40:02–00:00:42:03  (2.04s)
Phase static talking head with slight facial expression changes
Bild medium close-up, subject positioned right of center, profile/three-quarter view facing left, indoor room with a large window showing a blue sea/horizon background
Source
01:20:40:18–01:20:42:18
Scene 559 · Score 0.636
⚠ Score 0.636 unter 0.65 — visuell prüfen
python cli.py rematch --beat 11

Beat 12

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:42:03–00:00:50:06  (8.12s)
Phase static profile shot transitioning to black/darkness
Bild medium close-up, profile view, subject positioned on the right side of the frame, dark outdoor environment, blurred trees in background
Source
01:14:26:06  (multi-shot)
Scene Scenes 519, 13 · 2 Segmente
  • Seg 1: 01:14:26:06  dur 3.52s  @ off 0.16s  sc 519  score 0.721
  • Seg 2: 00:01:47:14  dur 2.88s  @ off 4.88s  sc 13  score 0.676
python cli.py rematch --beat 12

Beat 13

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:50:06–00:00:53:21  (3.60s)
Phase static conversation; woman on right is standing and holding a cup
Bild wide shot, two figures positioned on opposite sides of a round dining table, modern glass-walled sunroom or conservatory overlooking a snowy landscape
Source
00:43:19:13–00:43:23:04
Scene 308 · Score 0.636
⚠ Score 0.636 unter 0.65 — visuell prüfen
python cli.py rematch --beat 13

Beat 14

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:53:21–00:00:57:02  (3.24s)
Phase static conversation, subject holding a white cup
Bild medium shot, subject positioned on the left, vertical window frame dividing the right third of the frame, interior room with a large window overlooking a snowy pine forest
Source
00:43:24:16–00:43:27:19
Scene 309 · Score 0.626
⚠ Score 0.626 unter 0.65 — visuell prüfen
python cli.py rematch --beat 14

Beat 15

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:00:57:02–00:01:01:12  (4.40s)
Phase static conversation
Bild medium shot, profile view of two characters facing each other, indoor room with a large window overlooking a snowy forest
Source
00:02:10:08  (multi-shot)
Scene Scenes 17, 309 · 2 Segmente
  • Seg 1: 00:02:10:08  dur 2.80s  @ off 0.24s  sc 17  score 0.720
  • Seg 2: 00:43:27:13  dur 1.28s  @ off 3.04s  sc 309  score 0.682
python cli.py rematch --beat 15

Beat 16

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:01:12–00:01:04:12  (3.00s)
Phase man reaches out and touches the red door with a small object
Bild medium side profile shot, subject on left, door on right, indoor dim environment, adjacent to a red wooden door
Source
01:05:12:12–01:05:15:07
Scene 451 · Score 0.626
⚠ Score 0.626 unter 0.65 — visuell prüfen
python cli.py rematch --beat 16

Beat 17

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:04:12–00:01:09:03  (4.64s)
Phase Static intimacy transitioning to a spatial arrangement of figures
Bild Medium shot, eye-level. First two frames: static shot of couple in bed. Third frame: wide shot of women among white blocks, Bedroom with bedside table and lamp; transition to a white minimalist interior with pedestals
Source
01:31:18:04  (multi-shot)
Scene Scenes 623, 72 · 3 Segmente
  • Seg 1: 01:31:18:04  dur 1.92s  @ off 0.24s  sc 623  score 0.384
  • Seg 2: 00:09:06:13  dur 1.04s  @ off 2.80s  sc 72  score 0.434
  • Seg 3: 00:09:07:18  dur 0.50s  @ off 4.00s  sc 72  score 0.384
⚠ Score 0.399 unter 0.65 — visuell prüfen
python cli.py rematch --beat 17

Beat 18

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:09:03–00:01:10:19  (1.64s)
Phase Woman in foreground turns her head from profile to face the camera while speaking
Bild Medium shot, three-quarter profile of woman in foreground left, two women positioned behind her to the right, Indoors, minimalist dark background
Source
00:09:13:13–00:09:15:05
Scene 75 · Score 0.542
⚠ Score 0.542 unter 0.65 — visuell prüfen
python cli.py rematch --beat 18

Beat 19

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:10:19–00:01:12:13  (1.76s)
Phase static conversation, subtle facial expression change
Bild medium close-up, over-the-shoulder shot with a blurred figure in the foreground right, outdoor dark forest or wooded area
Source
00:16:48:13–00:16:49:10
Scene 126 · Score 0.403
⚠ Score 0.403 unter 0.65 — visuell prüfen
python cli.py rematch --beat 19

Beat 20

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:12:13–00:01:15:14  (3.04s)
Phase man kisses woman's forehead, then they pull back slightly to face each other
Bild extreme close-up, profile view, faces facing each other, indoor, blurred background
Source
01:27:51:12–01:27:54:01
Scene 613 · Score 0.711
python cli.py rematch --beat 20

Beat 21

OKBestätigt
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:15:14–00:01:17:13  (1.96s)
Phase hand raised to mouth, slight facial movement
Bild extreme close-up, face partially obscured by shadow, dark interior
Source
01:36:22:08–01:36:23:14
Scene 637 · Score 0.661
python cli.py rematch --beat 21

Beat 22

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:17:13–00:01:19:23  (2.40s)
Phase man looks up and speaks, transitioning from downward gaze to forward gaze
Bild close-up, profile to three-quarter view, outdoor rocky environment, blurred background
Source
01:03:05:11–01:03:07:07
Scene 442 · Score 0.544
⚠ Score 0.544 unter 0.65 — visuell prüfen
python cli.py rematch --beat 22

Beat 23

?Vorläufig
▶ Trailer  /  ▶ Source (Frame-Locked Compare)
Trailer
00:01:19:23–00:01:25:14  (5.64s)
Phase static posture, head tilted upwards looking at the sky
Bild medium shot, subject positioned on the left third, facing away from camera towards the water, rocky shoreline next to a body of water under an overcast sky
Source
01:04:35:21–01:04:41:02
Scene 446 · Score 0.534
⚠ Score 0.534 unter 0.65 — visuell prüfen
python cli.py rematch --beat 23

Beat 24

GFXTitel/Grafik
Trailer 24
— manuell setzen —
Trailer
00:01:25:14–00:01:32:07  (6.72s)
— kein automatischer Treffer —
python cli.py rematch --beat 24
diff --git a/CUTTER_REPORT.md b/CUTTER_REPORT.md index 2c8c8e2..5f91e96 100644 --- a/CUTTER_REPORT.md +++ b/CUTTER_REPORT.md @@ -1,6 +1,6 @@ # Cutter-Report — manuelles Nachschneiden -Generiert: **2026-05-09 19:06:01** +Generiert: **2026-05-18 08:46:49** - **Trailer**: `BehindTheRedDoor_Trailer_REFERENCE.mp4` @ 25.000 fps - **Source** : `BehindTheRedDoor_FTR_1080P_2398_Fixed.mp4` @ 23.976 fps diff --git a/cli.py b/cli.py index b6d5a3b..7df895b 100644 --- a/cli.py +++ b/cli.py @@ -384,6 +384,7 @@ def _normalize_cached_results(beats: list, results: list, cfg) -> list: fps = _scene_fps_light(scene, cfg) adjusted_in_s = result.in_point_s + phase_changed = False scene_changed = int(scene["scene_id"]) != result.scene_id starts_before_scene = result.in_point_s < float(scene["start_s"]) if scene_changed or starts_before_scene or result.duration_s <= 0.12: @@ -392,6 +393,25 @@ def _normalize_cached_results(beats: list, results: list, cfg) -> list: scene = _scene_for_time_light(scenes, adjusted_in_s, cfg) or scene fps = _scene_fps_light(scene, cfg) + should_phase_probe = ( + scene_changed + or starts_before_scene + or not result.is_confirmed + or result.match_score < cfg.cv.deep_scan.match_threshold + ) + phase_score = result.match_score + if should_phase_probe: + probe = _phase_probe_segment_in_scene(beat, scene, adjusted_in_s, cfg) + if probe is not None: + probed_in_s, probed_score = probe + max_shift_s = max(0.12, min(0.75, beat.duration_s * 0.35)) + if abs(probed_in_s - adjusted_in_s) <= max_shift_s: + adjusted_in_s = probed_in_s + phase_changed = True + phase_score = max(float(result.match_score), float(probed_score)) + scene = _scene_for_time_light(scenes, adjusted_in_s, cfg) or scene + fps = _scene_fps_light(scene, cfg) + matchable_duration_s = beat.duration_s try: from src.cv.global_scan import estimate_matchable_reference_duration @@ -414,6 +434,7 @@ def _normalize_cached_results(beats: list, results: list, cfg) -> list: if ( scene_changed or starts_before_scene + or phase_changed or result.duration_s <= 0.12 or result.out_point_s > adjusted_in_s + max_duration_s + (1.0 / fps) ): @@ -423,6 +444,8 @@ def _normalize_cached_results(beats: list, results: list, cfg) -> list: in_point_s=adjusted_in_s, out_point_s=adjusted_in_s + max_duration_s, in_point_frame=int(adjusted_in_s * fps), + match_score=phase_score, + is_confirmed=phase_score >= cfg.cv.deep_scan.match_threshold, ) coverage = ( @@ -2404,6 +2427,141 @@ def cmd_run(args: argparse.Namespace, cfg) -> None: cmd_export(args, cfg) +def cmd_preview(args: argparse.Namespace, cfg) -> None: + """Assemble a rough preview video from cached source matches, with original audio.""" + import subprocess + + log = logging.getLogger(__name__) + + results_path = _results_cache_path(cfg) + if not results_path.exists(): + log.error("No match_results.json — run 'match' first.") + return + + data = sorted( + json.loads(results_path.read_text(encoding="utf-8")), + key=lambda r: r["beat_id"], + ) + + beats_path = cfg.paths.cache_dir / "trailer_beats.json" + beats_by_id: dict = {} + if beats_path.exists(): + for b in json.loads(beats_path.read_text(encoding="utf-8")): + beats_by_id[int(b["beat_id"])] = b + + clip_width = 1280 + fps = 25 + out_dir = cfg.paths.output_dir / "preview_clips" + out_dir.mkdir(parents=True, exist_ok=True) + preview_out = cfg.paths.output_dir / "preview.mp4" + + def _run(cmd: list, timeout: int = 120) -> bool: + r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout) + if r.returncode != 0: + log.debug("ffmpeg stderr: %s", r.stderr[-600:]) + return r.returncode == 0 + + def extract_with_audio(src: Path, start_s: float, duration_s: float, out: Path) -> bool: + preroll = 2.0 if start_s >= 2.0 else 0.0 + input_seek = max(0.0, start_s - preroll) + accurate_seek = start_s - input_seek + return _run([ + "ffmpeg", "-y", "-loglevel", "error", + "-ss", f"{input_seek:.3f}", "-i", str(src), + "-ss", f"{accurate_seek:.3f}", "-t", f"{max(0.04, duration_s):.3f}", + "-map", "0:v:0", "-map", "0:a:0", + "-c:v", "libx264", "-preset", "veryfast", "-crf", "23", + "-vf", f"fps={fps},scale={clip_width}:-2,setsar=1,setpts=PTS-STARTPTS", + "-c:a", "aac", "-ar", "48000", "-ac", "2", + "-pix_fmt", "yuv420p", "-movflags", "+faststart", str(out), + ]) + + def black_silence(duration_s: float, out: Path) -> bool: + return _run([ + "ffmpeg", "-y", "-loglevel", "error", + "-f", "lavfi", "-i", f"color=black:s={clip_width}x720:r={fps}", + "-f", "lavfi", "-i", "anullsrc=r=48000:cl=stereo", + "-t", f"{max(0.5, duration_s):.3f}", + "-c:v", "libx264", "-preset", "veryfast", "-crf", "23", + "-c:a", "aac", "-pix_fmt", "yuv420p", "-movflags", "+faststart", str(out), + ]) + + def concat_clips(parts: list[Path], out: Path) -> bool: + lst = out.with_suffix(".txt") + lst.write_text( + "\n".join(f"file '{p.resolve().as_posix()}'" for p in parts), + encoding="utf-8", + ) + ok = _run([ + "ffmpeg", "-y", "-loglevel", "error", + "-f", "concat", "-safe", "0", "-i", str(lst), + "-c", "copy", str(out), + ], timeout=300) + lst.unlink(missing_ok=True) + return ok + + beat_clips: list[Path] = [] + + for rec in data: + bid = int(rec["beat_id"]) + segs = rec.get("segments", []) + src = Path(rec["source_path"]) if rec.get("source_path") else None + clip_out = out_dir / f"beat_{bid:02d}.mp4" + + if src is None or not src.exists(): + beat = beats_by_id.get(bid, {}) + dur = max(0.5, float(beat.get("end_s", 1)) - float(beat.get("start_s", 0))) + log.info("Beat %02d: NO MATCH — black/silence %.2fs", bid, dur) + if black_silence(dur, clip_out): + beat_clips.append(clip_out) + continue + + if len(segs) >= 2: + parts: list[Path] = [] + for idx, seg in enumerate(segs): + in_s = float(seg["in_point_s"]) + dur = max(0.04, float(seg["out_point_s"]) - in_s) + seg_src = Path(seg["source_path"]) if seg.get("source_path") else src + part = out_dir / f"beat_{bid:02d}_seg{idx:02d}.mp4" + log.info("Beat %02d seg%d: scene=%s %.2fs–%.2fs", bid, idx, seg.get("scene_id"), in_s, in_s + dur) + if extract_with_audio(seg_src, in_s, dur, part): + parts.append(part) + if not parts: + log.warning("Beat %02d: no segments extracted", bid) + continue + if len(parts) == 1: + parts[0].rename(clip_out) + beat_clips.append(clip_out) + else: + if concat_clips(parts, clip_out): + beat_clips.append(clip_out) + for p in parts: + p.unlink(missing_ok=True) + else: + in_s = float(rec["in_point_s"]) + beat = beats_by_id.get(bid, {}) + beat_dur = float(beat["end_s"]) - float(beat["start_s"]) if beat else 0.0 + source_dur = float(rec["out_point_s"]) - in_s + dur = max(0.04, beat_dur if beat_dur > 0.04 else source_dur) + log.info("Beat %02d: scene=%s %.2fs+%.2fs (trailer=%.2fs src=%.2fs)", bid, rec.get("scene_id"), in_s, dur, beat_dur, source_dur) + if extract_with_audio(src, in_s, dur, clip_out): + beat_clips.append(clip_out) + else: + log.warning("Beat %02d: extraction failed", bid) + + if not beat_clips: + log.error("No clips extracted — aborting.") + return + + log.info("Concatenating %d beat clips → %s", len(beat_clips), preview_out) + if concat_clips(beat_clips, preview_out): + size_mb = preview_out.stat().st_size / 1_048_576 + log.info("Preview ready: %s (%.1f MB)", preview_out, size_mb) + print(f"\n Preview → {preview_out} ({size_mb:.1f} MB)") + else: + log.error("Final concat failed — per-beat clips are in %s", out_dir) + + # --------------------------------------------------------------------------- # Argument parser # --------------------------------------------------------------------------- @@ -2474,6 +2632,12 @@ def _build_parser() -> argparse.ArgumentParser: p_run.add_argument("--beat", type=int, help="Run match/report/export for only one cached beat") + # preview + sub.add_parser( + "preview", + help="Build output/preview.mp4 from cached matches — source clips with audio in beat order", + ) + return parser @@ -2498,6 +2662,7 @@ def main() -> None: "report": cmd_report, "export": cmd_export, "run": cmd_run, + "preview": cmd_preview, } handler = dispatch[args.command] diff --git a/docs/ALGORITHM.md b/docs/ALGORITHM.md index 7eff0cb..b837890 100644 --- a/docs/ALGORITHM.md +++ b/docs/ALGORITHM.md @@ -230,6 +230,11 @@ sondern auch den schlechtesten Einzelvergleich, die ersten sichtbaren Frames und die Frame-zu-Frame-Bewegung. Dadurch gewinnt nicht mehr ein späteres Standbild derselben Einstellung, nur weil Fenster, Gesichter und Licht fast identisch aussehen. +Unsichere Einzeltreffer ohne Segmentliste laufen ebenfalls durch diesen lokalen +Phasen-Probe. Das repariert alte Cache-Einträge, deren Szene korrekt ist, deren +Inpoint aber einige Frames in der Bewegung daneben liegt. Der Probe bleibt auf +kleine lokale Shifts begrenzt und wird nicht für jeden bestätigten Treffer +erzwungen, damit Report-Refreshes nicht zum Vollscan werden. Report-Clips werden zusätzlich an den bekannten Source-Szenenstart plus eine sehr kurze Ein-Frame-Guard-Zone geklemmt, damit ein knapp vor oder direkt auf der Schnittkante liegender Inpoint nicht mit Frames der vorherigen Einstellung