[Python] Unreal Python script for adding pre-roll

Discussão em 'Python' iniciado por Stack, Setembro 3, 2025.

  1. Stack

    Stack Membro Participativo

    Im trying to create a python script that adds pre-roll to my tracks in Unreal 5.4.

    Currently it correctly sets the CameraCuts, but fails to correctly update Subsequences and CameraComponents > Spawned + Transform to Pre Roll Frames 0100, instead setting it to 0000* which is odd. I have tried saving and re-opening to see if its a visual bug but it doesn't seem to update and I really need this to reflect the change.

    here is my script

    Any help is greatly appreciated!

    import unreal

    # ----------------------------- CONFIG ---------------------------------
    SUBSEQ_PREROLL_FRAMES = 100
    CAMERA_PREROLL_FRAMES = 100
    CAMERA_CUT_HEAD_EXTEND_FRAMES = SUBSEQ_PREROLL_FRAMES - 12 # 88 frames
    # ----------------------------------------------------------------------

    # ----------------------------- Helpers ---------------------------------
    def _get_or_open_target_sequence():
    seq = unreal.LevelSequenceEditorBlueprintLibrary.get_current_level_sequence()
    if seq:
    return seq
    # Fallback: exactly one selected LevelSequence asset
    try:
    selected = unreal.EditorUtilityLibrary.get_selected_assets()
    except Exception:
    selected = []
    level_sequences = [a for a in selected if isinstance(a, unreal.LevelSequence)]
    if len(level_sequences) == 1:
    seq = level_sequences[0]
    try:
    unreal.LevelSequenceEditorBlueprintLibrary.open_level_sequence(seq)
    except Exception:
    pass
    return seq
    return None

    def _get_sections(track):
    try:
    return list(track.get_sections())
    except Exception:
    return []

    def _get_section_start_end(section):
    try:
    start = int(section.get_start_frame())
    end = int(section.get_end_frame())
    return start, end
    except Exception:
    pass
    try:
    rng = section.get_range()
    start = int(rng.lower_bound.value)
    end = int(rng.upper_bound.value)
    return start, end
    except Exception:
    return None, None

    def _set_section_start_end(section, new_start, end):
    try:
    section.set_start_frame(int(new_start))
    section.set_end_frame(int(end))
    return True
    except Exception:
    pass
    try:
    section.set_range(int(new_start), int(end))
    return True
    except Exception:
    return False

    # ----------------------------- Debugging ---------------------------------
    def debug_tracks(seq):
    unreal.log(f"=== Debug Tracks for sequence '{seq.get_name()}' ===")
    try:
    tracks = unreal.MovieSceneSequenceExtensions.get_tracks(seq)
    except Exception:
    tracks = []
    unreal.log(f"Master tracks found: {len(tracks)}")
    for t in tracks:
    try:
    sections = t.get_sections()
    unreal.log(f"Track: {t.get_display_name()} ({t.get_class().get_name()}), sections: {len(sections)}")
    for sec in sections:
    start, end = _get_section_start_end(sec)
    pre_roll = sec.get_pre_roll_frames() if hasattr(sec, "get_pre_roll_frames") else "N/A"
    unreal.log(f" Section: {sec.get_name()}, start={start}, end={end}, pre_roll={pre_roll}")
    except Exception as e:
    unreal.log_warning(f"Error reading track: {str(e)}")

    # ----------------------------- Main logic ---------------------------------
    def _process_subsequence_tracks(seq):
    modified = 0
    try:
    tracks = unreal.MovieSceneSequenceExtensions.get_tracks(seq)
    except Exception:
    tracks = []
    for t in tracks:
    if isinstance(t, unreal.MovieSceneSubTrack):
    for sec in _get_sections(t):
    start, end = _get_section_start_end(sec)
    if start is not None and end is not None:
    try:
    sec.set_pre_roll_frames(SUBSEQ_PREROLL_FRAMES)
    modified += 1
    unreal.log(f"[PreRoll] Subsequence section '{sec.get_name()}' set to {SUBSEQ_PREROLL_FRAMES}")
    except Exception as e:
    unreal.log_warning(f"[PreRoll] Failed to set subsequence section '{sec.get_name()}': {e}")
    else:
    unreal.log_warning(f"[PreRoll] Skipping subsequence section '{sec.get_name()}' (invalid start/end)")
    return modified

    def _process_camera_tracks(seq):
    modified = 0
    try:
    bindings = unreal.MovieSceneSequenceExtensions.get_bindings(seq)
    except Exception:
    bindings = []
    for b in bindings:
    try:
    tracks = unreal.MovieSceneBindingExtensions.get_tracks(b)
    except Exception:
    tracks = []
    for tr in tracks:
    sections = _get_sections(tr)
    track_name = tr.get_display_name() if hasattr(tr, "get_display_name") else "UnnamedTrack"
    track_class = tr.get_class().get_name() if hasattr(tr, "get_class") else "UnknownClass"
    for sec in sections:
    try:
    sec_name = sec.get_name()
    start, end = _get_section_start_end(sec)
    pre_roll_before = sec.get_pre_roll_frames() if hasattr(sec, "get_pre_roll_frames") else "N/A"
    unreal.log(f"[DEBUG] Track '{track_name}' ({track_class}) Section '{sec_name}', start={start}, end={end}, pre_roll={pre_roll_before}")

    # Determine if we should set pre-roll
    valid_types_for_preroll = (unreal.MovieSceneSpawnTrack, unreal.MovieScene3DTransformTrack)
    if isinstance(tr, valid_types_for_preroll):
    sec.set_pre_roll_frames(CAMERA_PREROLL_FRAMES)
    modified += 1
    unreal.log(f"[PreRoll] Camera section '{sec_name}' set to {CAMERA_PREROLL_FRAMES}")
    else:
    # Only skip tracks that are not supported (e.g., Material, Visibility, Float)
    if start is None or end is None:
    unreal.log_warning(f"[PreRoll] Skipping unsupported section '{sec_name}'")
    except Exception as e:
    unreal.log_warning(f"[PreRoll] Failed to process camera section '{sec_name}': {e}")
    return modified

    def _process_camera_cuts(seq):
    modified = 0
    try:
    tracks = unreal.MovieSceneSequenceExtensions.get_tracks(seq)
    except Exception:
    tracks = []
    for t in tracks:
    if not isinstance(t, unreal.MovieSceneCameraCutTrack):
    continue
    sections = _get_sections(t)
    if not sections:
    continue
    sec = sections[0] # only first CameraCut
    try:
    start, end = _get_section_start_end(sec)
    if start is None or end is None:
    continue
    current_marker = sec.get_pre_roll_frames() or 0
    if current_marker >= CAMERA_CUT_HEAD_EXTEND_FRAMES:
    continue
    delta = CAMERA_CUT_HEAD_EXTEND_FRAMES - current_marker
    new_start = start - int(delta)
    if _set_section_start_end(sec, new_start, end):
    sec.set_pre_roll_frames(CAMERA_CUT_HEAD_EXTEND_FRAMES)
    modified += 1
    unreal.log(f"[PreRoll] CameraCut '{sec.get_name()}' start moved from {start} to {new_start}")
    except Exception as e:
    unreal.log_warning(f"[PreRoll] Failed to process CameraCut '{sec.get_name()}': {e}")
    return modified

    def add_preroll_to_sequence_safely():
    seq = _get_or_open_target_sequence()
    if not seq:
    unreal.log_error("[PreRoll] No Level Sequence active or selected. Open one or select exactly one LevelSequence asset.")
    return False

    unreal.log("=== DEBUG BEFORE MODIFICATION ===")
    debug_tracks(seq)

    total_modified = 0
    with unreal.ScopedEditorTransaction("Add Sequencer Pre-roll (All Sections)"):
    total_modified += _process_subsequence_tracks(seq)
    total_modified += _process_camera_tracks(seq)
    total_modified += _process_camera_cuts(seq)

    try:
    unreal.EditorAssetLibrary.save_asset(seq.get_path_name())
    except Exception:
    pass

    if total_modified > 0:
    unreal.log(f"[PreRoll] Applied pre-roll updates to '{seq.get_name()}'. Sections modified: {total_modified}")
    else:
    unreal.log(f"[PreRoll] '{seq.get_name()}' already compliant. No changes applied.")

    unreal.log("=== DEBUG AFTER MODIFICATION ===")
    debug_tracks(seq)
    return True

    # ----------------------------- RUN ------------------------------------
    add_preroll_to_sequence_safely()

    Continue reading...

Compartilhe esta Página