Duplicated level sequence contains old references

I have duplicated a level sequence and did a rebind by name within my second map. But I noticed it still have references to the first map. I would like to get rid of these references as I want to use migrate and it would still suggest to copy this map.

My idea was to just copy the tracks and everything into a new fresh level sequence. By hand it is just copy and paste. Of course a in place cleanup would also work.

A C++ or Python solution would be nice here. :slight_smile:

Thanks!

Steps to Reproduce

  • Create two maps with actors with the same name
  • Create a Level Sequence and add some actors from the first map
  • Duplicate the level sequence and rebind by name the actors with the actors from the second map
  • we still have references to the first map when checking references with the ref viewer

I found something that could work. I need to chck that out:

https://dev.epicgames.com/documentation/en-us/unreal-engine/python-scripting-in-sequencer-in-unreal-engine#copyandpastecommands

Hi [mention removed]​,

I think that with this code you should be able to do the job. What this does is get the opened level and find all the tracks and override it with the current actors of the level. It is working in my case, but just want to make sure it works also on yours. I just did a quick test in a BlueprintFunctionLibrary and run it from an EditorUtilityWidget. Let me know if it worked for you :slight_smile:

.h

class TPP_554_NEW_API USeqBindingToolsBPLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
 
	UFUNCTION(BlueprintCallable, CallInEditor, Category="Sequencer|Rebinding",
			  meta=(WorldContext="WorldContextObject", DevelopmentOnly))
	static int32 RebindLevelSequenceByName(
		UObject* WorldContextObject,
		ULevelSequence* Sequence,
		bool bMatchByActorLabel = true,
		bool bClearUnmatched = true,
		bool bSaveSequence = false);
};

.cpp

#include "SeqBindingToolsBPLibrary.h"
#include "EditorAssetLibrary.h"
#include "Engine/World.h"
#include "MovieScene.h"
#include "EngineUtils.h"
#include "LevelSequenceEditorBlueprintLibrary.h"
#include "LevelSequenceEditorSubsystem.h"
 
#include "GameFramework/Actor.h"
#include "Runtime/LevelSequence/Public/LevelSequence.h"
 
#if WITH_EDITOR
	#include "Editor.h"
	#include "MovieSceneBindingProxy.h"
#endif
 
static UWorld* ResolveEditorWorld(UObject* WorldContextObject)
{
    if (WorldContextObject)
    {
        if (UWorld* WCW = WorldContextObject->GetWorld())
        {
            return WCW;
        }
    }
 
#if WITH_EDITOR
    if (GEditor)
    {
        return GEditor->GetEditorWorldContext().World();
    }
#endif
 
    return nullptr;
}
 
int32 USeqBindingToolsBPLibrary::RebindLevelSequenceByName(
    UObject* WorldContextObject,
    ULevelSequence* Sequence,
    bool bMatchByActorLabel,
    bool bClearUnmatched,
    bool bSaveSequence)
{
 
    if (!Sequence)
    {
        UE_LOG(LogTemp, Warning, TEXT("RebindLevelSequenceByName: Sequence is null."));
        return 0;
    }
 
    UWorld* World = ResolveEditorWorld(WorldContextObject);
    if (!World)
    {
        UE_LOG(LogTemp, Warning, TEXT("RebindLevelSequenceByName: Open the target map and try again."));
        return 0;
    }
 
    UMovieScene* MovieScene = Sequence->GetMovieScene();
    if (!MovieScene)
    {
        UE_LOG(LogTemp, Warning, TEXT("RebindLevelSequenceByName: Sequence has no MovieScene."));
        return 0;
    }
 
 
    ULevelSequenceEditorSubsystem* SequencerSubsys =
        GEditor ? GEditor->GetEditorSubsystem<ULevelSequenceEditorSubsystem>() : nullptr;
 
    if (!SequencerSubsys)
    {
        UE_LOG(LogTemp, Warning, TEXT("RebindLevelSequenceByName: LevelSequenceEditorSubsystem not available."));
        return 0;
    }
 
    // Build lookups from CURRENT world
    TMultiMap<FString, AActor*> ByLabel;
    TMultiMap<FString, AActor*> ByName;
 
    for (TActorIterator<AActor> It(World); It; ++It)
    {
        AActor* A = *It;
        if (!IsValid(A)) continue;
 
        ByName.Add(A->GetName(), A);
    #if WITH_EDITORONLY_DATA
        ByLabel.Add(A->GetActorLabel(), A);
    #else
        ByLabel.Add(A->GetName(), A);
    #endif
    }
 
    const TArray<FMovieSceneBinding>& Bindings = MovieScene->GetBindings();
    int32 NumRebound = 0;
 
    for (const FMovieSceneBinding& B : Bindings)
    {
        const FGuid& Guid = B.GetObjectGuid();
        const FText BindingDisplayName = FText::FromString(B.GetName());
 
 
        FMovieSceneBindingProxy BindingProxy(Guid, Sequence);
 
        // Choose candidate key(s) to match. In a “rebind by name” workflow the binding name
        TArray<FString> CandidateKeys;
        CandidateKeys.Add(B.GetName());
 
        // Clear existing binding first to purge stale references to the old map
        SequencerSubsys->RemoveAllBindings(BindingProxy);
 
        // Find matches in the current world
        TArray<AActor*> NewActors;
        for (const FString& Key : CandidateKeys)
        {
            TArray<AActor*> Matches;
            if (bMatchByActorLabel)
            {
                ByLabel.MultiFind(Key, Matches);
            }
            else
            {
                ByName.MultiFind(Key, Matches);
            }
 
            for (AActor* Match : Matches)
            {
                if (IsValid(Match))
                {
                    NewActors.AddUnique(Match);
                }
            }
        }
 
        if (NewActors.Num() > 0)
        {
            // Replace binding with the actors we found (single or multiple)
            SequencerSubsys->ReplaceBindingWithActors(NewActors, BindingProxy);
            ++NumRebound;
 
            UE_LOG(LogTemp, Log, TEXT("[Rebound] %s -> %d actor(s)"),
                *BindingDisplayName.ToString(), NewActors.Num());
        }
        else
        {
            if (bClearUnmatched)
            {
                // Leave cleared (already done)
                UE_LOG(LogTemp, Warning, TEXT("[Cleared] %s (no match found)"),
                    *BindingDisplayName.ToString());
            }
            else
            {
                UE_LOG(LogTemp, Warning, TEXT("[Unchanged] %s (no match found; keeping cleared binding)"),
                    *BindingDisplayName.ToString());
            }
        }
    }
 
    if (bSaveSequence)
    {
        if (!UEditorAssetLibrary::SaveLoadedAsset(Sequence))
        {
            UE_LOG(LogTemp, Warning, TEXT("RebindLevelSequenceByName: Save failed (asset may be read-only)."));
        }
    }
 
    UE_LOG(LogTemp, Log, TEXT("RebindLevelSequenceByName: %d binding(s) rebound."), NumRebound);
    return NumRebound;
}

Thanks for the answer. I was also able to solve it withing my python script by simply using:

https://dev.epicgames.com/documentation/en\-us/unreal\-engine/python\-api/class/LevelSequenceEditorSubsystem?application\_version\=5\.5\#unreal.LevelSequenceEditorSubsystem.remove\_invalid\_bindings

Thanks for the efforts!