79 changed files with 1496 additions and 1016 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,178 +0,0 @@ |
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
|||
|
|||
|
|||
#include "Tools/OpenAiModerator.h" |
|||
|
|||
#include "HttpModule.h" |
|||
#include "Interfaces/IHttpResponse.h" |
|||
#include "Dom/JsonObject.h" |
|||
#include "Serialization/JsonReader.h" |
|||
#include "Serialization/JsonSerializer.h" |
|||
|
|||
FString UOpenAiModerator::StoredApiKey; |
|||
FString UOpenAiModerator::StoredModel = TEXT("omni-moderation-latest"); |
|||
TArray<TWeakObjectPtr<UOpenAiModerator>> UOpenAiModerator::ActiveRequests; |
|||
|
|||
void UOpenAiModerator::InitModeration(const FString& ApiKey, const FString& Model) |
|||
{ |
|||
StoredApiKey = ApiKey; |
|||
StoredModel = Model; |
|||
} |
|||
|
|||
UOpenAiModerator* UOpenAiModerator::UseModeration(UObject* WorldContextObject, const FString& Text) |
|||
{ |
|||
UOpenAiModerator* Action = NewObject<UOpenAiModerator>(); |
|||
Action->InputText = Text; |
|||
Action->RegisterWithGameInstance(WorldContextObject); |
|||
return Action; |
|||
} |
|||
|
|||
void UOpenAiModerator::Activate() |
|||
{ |
|||
if (StoredApiKey.IsEmpty()) |
|||
{ |
|||
UE_LOG(LogTemp, Error, TEXT("OpenAiModerator: API key not set. Call InitModeration first.")); |
|||
FModerationResult EmptyResult; |
|||
Passed.Broadcast(EmptyResult); |
|||
return; |
|||
} |
|||
|
|||
RequestStartTime = FPlatformTime::Seconds(); |
|||
ActiveRequests.Add(this); |
|||
|
|||
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> HttpRequest = FHttpModule::Get().CreateRequest(); |
|||
HttpRequest->SetURL(TEXT("https://api.openai.com/v1/moderations")); |
|||
HttpRequest->SetVerb(TEXT("POST")); |
|||
HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json")); |
|||
HttpRequest->SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Bearer %s"), *StoredApiKey)); |
|||
|
|||
const FString Body = FString::Printf(TEXT("{\"model\":\"%s\",\"input\":\"%s\"}"), |
|||
*StoredModel, |
|||
*InputText.ReplaceCharWithEscapedChar()); |
|||
|
|||
HttpRequest->SetContentAsString(Body); |
|||
|
|||
HttpRequest->OnProcessRequestComplete().BindUObject(this, &UOpenAiModerator::HandleHttpResponse); |
|||
|
|||
if (!HttpRequest->ProcessRequest()) |
|||
{ |
|||
UE_LOG(LogTemp, Error, TEXT("OpenAiModerator: Failed to start HTTP request.")); |
|||
FModerationResult EmptyResult; |
|||
Passed.Broadcast(EmptyResult); |
|||
} |
|||
} |
|||
|
|||
void UOpenAiModerator::ClearModeration() |
|||
{ |
|||
TArray<TWeakObjectPtr<UOpenAiModerator>> RequestsCopy = ActiveRequests; |
|||
ActiveRequests.Empty(); |
|||
|
|||
for (const TWeakObjectPtr<UOpenAiModerator>& Weak : RequestsCopy) |
|||
{ |
|||
if (UOpenAiModerator* Action = Weak.Get()) |
|||
{ |
|||
Action->SetReadyToDestroy(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void UOpenAiModerator::HandleHttpResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) |
|||
{ |
|||
ActiveRequests.RemoveAll([this](const TWeakObjectPtr<UOpenAiModerator>& Weak) { return !Weak.IsValid() || Weak.Get() == this; }); |
|||
|
|||
const float ElapsedTime = static_cast<float>(FPlatformTime::Seconds() - RequestStartTime); |
|||
|
|||
FModerationResult Result; |
|||
Result.ExecutionTime = ElapsedTime; |
|||
|
|||
if (!bWasSuccessful || !Response.IsValid()) |
|||
{ |
|||
UE_LOG(LogTemp, Error, TEXT("OpenAiModerator: HTTP request failed.")); |
|||
Passed.Broadcast(Result); |
|||
return; |
|||
} |
|||
|
|||
const int32 Code = Response->GetResponseCode(); |
|||
const FString Body = Response->GetContentAsString(); |
|||
|
|||
if (Code < 200 || Code >= 300) |
|||
{ |
|||
UE_LOG(LogTemp, Error, TEXT("OpenAiModerator: HTTP %d — %s"), Code, *Body); |
|||
Passed.Broadcast(Result); |
|||
return; |
|||
} |
|||
|
|||
TSharedPtr<FJsonObject> RootObject; |
|||
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Body); |
|||
if (!FJsonSerializer::Deserialize(Reader, RootObject) || !RootObject.IsValid()) |
|||
{ |
|||
UE_LOG(LogTemp, Error, TEXT("OpenAiModerator: Failed to parse response JSON.")); |
|||
Passed.Broadcast(Result); |
|||
return; |
|||
} |
|||
|
|||
const TArray<TSharedPtr<FJsonValue>>* ResultsArray; |
|||
if (!RootObject->TryGetArrayField(TEXT("results"), ResultsArray) || ResultsArray->Num() == 0) |
|||
{ |
|||
UE_LOG(LogTemp, Error, TEXT("OpenAiModerator: No results in response.")); |
|||
Passed.Broadcast(Result); |
|||
return; |
|||
} |
|||
|
|||
const TSharedPtr<FJsonObject>& FirstResult = (*ResultsArray)[0]->AsObject(); |
|||
if (!FirstResult.IsValid()) |
|||
{ |
|||
UE_LOG(LogTemp, Error, TEXT("OpenAiModerator: Invalid result object.")); |
|||
Passed.Broadcast(Result); |
|||
return; |
|||
} |
|||
|
|||
Result.bFlagged = FirstResult->GetBoolField(TEXT("flagged")); |
|||
|
|||
const TSharedPtr<FJsonObject>* CategoriesObject; |
|||
if (FirstResult->TryGetObjectField(TEXT("categories"), CategoriesObject)) |
|||
{ |
|||
FModerationCategories& C = Result.Categories; |
|||
C.bSexual = (*CategoriesObject)->GetBoolField(TEXT("sexual")); |
|||
C.bSexualMinors = (*CategoriesObject)->GetBoolField(TEXT("sexual/minors")); |
|||
C.bHarassment = (*CategoriesObject)->GetBoolField(TEXT("harassment")); |
|||
C.bHarassmentThreatening = (*CategoriesObject)->GetBoolField(TEXT("harassment/threatening")); |
|||
C.bHate = (*CategoriesObject)->GetBoolField(TEXT("hate")); |
|||
C.bHateThreatening = (*CategoriesObject)->GetBoolField(TEXT("hate/threatening")); |
|||
C.bIllicit = (*CategoriesObject)->GetBoolField(TEXT("illicit")); |
|||
C.bIllicitViolent = (*CategoriesObject)->GetBoolField(TEXT("illicit/violent")); |
|||
C.bSelfHarm = (*CategoriesObject)->GetBoolField(TEXT("self-harm")); |
|||
C.bSelfHarmIntent = (*CategoriesObject)->GetBoolField(TEXT("self-harm/intent")); |
|||
C.bSelfHarmInstructions = (*CategoriesObject)->GetBoolField(TEXT("self-harm/instructions")); |
|||
C.bViolence = (*CategoriesObject)->GetBoolField(TEXT("violence")); |
|||
C.bViolenceGraphic = (*CategoriesObject)->GetBoolField(TEXT("violence/graphic")); |
|||
} |
|||
|
|||
const TSharedPtr<FJsonObject>* ScoresObject; |
|||
if (FirstResult->TryGetObjectField(TEXT("category_scores"), ScoresObject)) |
|||
{ |
|||
FModerationCategoryScores& S = Result.CategoryScores; |
|||
S.Sexual = (*ScoresObject)->GetNumberField(TEXT("sexual")); |
|||
S.SexualMinors = (*ScoresObject)->GetNumberField(TEXT("sexual/minors")); |
|||
S.Harassment = (*ScoresObject)->GetNumberField(TEXT("harassment")); |
|||
S.HarassmentThreatening = (*ScoresObject)->GetNumberField(TEXT("harassment/threatening")); |
|||
S.Hate = (*ScoresObject)->GetNumberField(TEXT("hate")); |
|||
S.HateThreatening = (*ScoresObject)->GetNumberField(TEXT("hate/threatening")); |
|||
S.Illicit = (*ScoresObject)->GetNumberField(TEXT("illicit")); |
|||
S.IllicitViolent = (*ScoresObject)->GetNumberField(TEXT("illicit/violent")); |
|||
S.SelfHarm = (*ScoresObject)->GetNumberField(TEXT("self-harm")); |
|||
S.SelfHarmIntent = (*ScoresObject)->GetNumberField(TEXT("self-harm/intent")); |
|||
S.SelfHarmInstructions = (*ScoresObject)->GetNumberField(TEXT("self-harm/instructions")); |
|||
S.Violence = (*ScoresObject)->GetNumberField(TEXT("violence")); |
|||
S.ViolenceGraphic = (*ScoresObject)->GetNumberField(TEXT("violence/graphic")); |
|||
} |
|||
|
|||
if (Result.bFlagged) |
|||
{ |
|||
Flagged.Broadcast(Result); |
|||
} |
|||
else |
|||
{ |
|||
Passed.Broadcast(Result); |
|||
} |
|||
} |
|||
@ -1,159 +0,0 @@ |
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
|||
|
|||
#pragma once |
|||
|
|||
#include "CoreMinimal.h" |
|||
#include "Kismet/BlueprintAsyncActionBase.h" |
|||
#include "Http.h" |
|||
#include "OpenAiModerator.generated.h" |
|||
|
|||
USTRUCT(BlueprintType) |
|||
struct FModerationCategories |
|||
{ |
|||
GENERATED_BODY() |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bSexual = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bSexualMinors = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bHarassment = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bHarassmentThreatening = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bHate = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bHateThreatening = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bIllicit = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bIllicitViolent = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bSelfHarm = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bSelfHarmIntent = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bSelfHarmInstructions = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bViolence = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bViolenceGraphic = false; |
|||
}; |
|||
|
|||
USTRUCT(BlueprintType) |
|||
struct FModerationCategoryScores |
|||
{ |
|||
GENERATED_BODY() |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float Sexual = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float SexualMinors = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float Harassment = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float HarassmentThreatening = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float Hate = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float HateThreatening = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float Illicit = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float IllicitViolent = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float SelfHarm = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float SelfHarmIntent = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float SelfHarmInstructions = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float Violence = 0.f; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float ViolenceGraphic = 0.f; |
|||
}; |
|||
|
|||
USTRUCT(BlueprintType) |
|||
struct FModerationResult |
|||
{ |
|||
GENERATED_BODY() |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
bool bFlagged = false; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
FModerationCategories Categories; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
FModerationCategoryScores CategoryScores; |
|||
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AvatarCoreAI|Moderation") |
|||
float ExecutionTime = 0.f; |
|||
}; |
|||
|
|||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnModerationResult, const FModerationResult&, Result); |
|||
|
|||
/**
|
|||
* Async Blueprint node for OpenAI Moderation API. |
|||
* Call InitModeration once to set API key and model, then use UseModeration to classify text. |
|||
*/ |
|||
UCLASS() |
|||
class AVATARCORE_AI_API UOpenAiModerator : public UBlueprintAsyncActionBase |
|||
{ |
|||
GENERATED_BODY() |
|||
|
|||
public: |
|||
/** Set API key and model. Call once before using UseModeration. */ |
|||
UFUNCTION(BlueprintCallable, Category = "AvatarCoreAI|Moderation", meta = (DisplayName = "Init Moderation")) |
|||
static void InitModeration(const FString& ApiKey, const FString& Model = TEXT("omni-moderation-latest")); |
|||
|
|||
/** Classify text using the OpenAI Moderation API. Returns via Flagged or Passed exec pins. */ |
|||
UFUNCTION(BlueprintCallable, Category = "AvatarCoreAI|Moderation", meta = (BlueprintInternalUseOnly = "true", DisplayName = "Use Moderation", WorldContext = "WorldContextObject")) |
|||
static UOpenAiModerator* UseModeration(UObject* WorldContextObject, const FString& Text); |
|||
|
|||
/** Cancel all active moderation requests. */ |
|||
UFUNCTION(BlueprintCallable, Category = "AvatarCoreAI|Moderation", meta = (DisplayName = "Clear Moderation")) |
|||
static void ClearModeration(); |
|||
|
|||
UPROPERTY(BlueprintAssignable) |
|||
FOnModerationResult Flagged; |
|||
|
|||
UPROPERTY(BlueprintAssignable) |
|||
FOnModerationResult Passed; |
|||
|
|||
virtual void Activate() override; |
|||
|
|||
private: |
|||
void HandleHttpResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); |
|||
|
|||
FString InputText; |
|||
double RequestStartTime = 0.0; |
|||
|
|||
static FString StoredApiKey; |
|||
static FString StoredModel; |
|||
static TArray<TWeakObjectPtr<UOpenAiModerator>> ActiveRequests; |
|||
}; |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,24 @@ |
|||
{ |
|||
"FileVersion": 3, |
|||
"Version": 1, |
|||
"VersionName": "1.0", |
|||
"FriendlyName": "AvatarCore_Shared", |
|||
"Description": "Shared assets, structs and enums. In theory we want to be AI, TTS and STT to be separate.", |
|||
"Category": "Other", |
|||
"CreatedBy": "b.ReX GmbH", |
|||
"CreatedByURL": "", |
|||
"DocsURL": "", |
|||
"MarketplaceURL": "", |
|||
"SupportURL": "", |
|||
"CanContainContent": true, |
|||
"IsBetaVersion": false, |
|||
"IsExperimentalVersion": false, |
|||
"Installed": false, |
|||
"Modules": [ |
|||
{ |
|||
"Name": "AvatarCore_Shared", |
|||
"Type": "Runtime", |
|||
"LoadingPhase": "Default" |
|||
} |
|||
] |
|||
} |
|||
Binary file not shown.
@ -0,0 +1,53 @@ |
|||
// Copyright Epic Games, Inc. All Rights Reserved.
|
|||
|
|||
using UnrealBuildTool; |
|||
|
|||
public class AvatarCore_Shared : ModuleRules |
|||
{ |
|||
public AvatarCore_Shared(ReadOnlyTargetRules Target) : base(Target) |
|||
{ |
|||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; |
|||
|
|||
PublicIncludePaths.AddRange( |
|||
new string[] { |
|||
// ... add public include paths required here ...
|
|||
} |
|||
); |
|||
|
|||
|
|||
PrivateIncludePaths.AddRange( |
|||
new string[] { |
|||
// ... add other private include paths required here ...
|
|||
} |
|||
); |
|||
|
|||
|
|||
PublicDependencyModuleNames.AddRange( |
|||
new string[] |
|||
{ |
|||
"Core", |
|||
// ... add other public dependencies that you statically link with here ...
|
|||
} |
|||
); |
|||
|
|||
|
|||
PrivateDependencyModuleNames.AddRange( |
|||
new string[] |
|||
{ |
|||
"CoreUObject", |
|||
"Engine", |
|||
"Slate", |
|||
"SlateCore", |
|||
// ... add private dependencies that you statically link with here ...
|
|||
} |
|||
); |
|||
|
|||
|
|||
DynamicallyLoadedModuleNames.AddRange( |
|||
new string[] |
|||
{ |
|||
// ... add any modules that your module loads dynamically here ...
|
|||
} |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
// Copyright Epic Games, Inc. All Rights Reserved.
|
|||
|
|||
#include "AvatarCore_Shared.h" |
|||
|
|||
#define LOCTEXT_NAMESPACE "FAvatarCore_SharedModule" |
|||
|
|||
void FAvatarCore_SharedModule::StartupModule() |
|||
{ |
|||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
|||
} |
|||
|
|||
void FAvatarCore_SharedModule::ShutdownModule() |
|||
{ |
|||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
|||
// we call this function before unloading the module.
|
|||
} |
|||
|
|||
#undef LOCTEXT_NAMESPACE |
|||
|
|||
IMPLEMENT_MODULE(FAvatarCore_SharedModule, AvatarCore_Shared) |
|||
@ -0,0 +1,27 @@ |
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
|||
|
|||
|
|||
#include "FL_AvatarCoreShared.h" |
|||
|
|||
ELanguage UFL_AvatarCoreShared::StringToLanguage(const FString& InString) |
|||
{ |
|||
const UEnum* EnumPtr = StaticEnum<ELanguage>(); |
|||
|
|||
if (!EnumPtr) |
|||
{ |
|||
return ELanguage::NONE; |
|||
} |
|||
|
|||
FString Normalized = InString.ToLower(); |
|||
|
|||
int64 Value = EnumPtr->GetValueByNameString(Normalized); |
|||
|
|||
return Value != INDEX_NONE |
|||
? static_cast<ELanguage>(Value) |
|||
: ELanguage::NONE; |
|||
} |
|||
|
|||
FString UFL_AvatarCoreShared::LanguageToString(ELanguage Language) |
|||
{ |
|||
return StaticEnum<ELanguage>()->GetNameStringByValue((int64)Language); |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
|||
|
|||
#pragma once |
|||
|
|||
#include "CoreMinimal.h" |
|||
#include "UObject/NoExportTypes.h" |
|||
#include "AvatarCoreSharedEnums.generated.h" |
|||
|
|||
UENUM(BlueprintType) |
|||
enum class ELanguage : uint8 { |
|||
NONE UMETA(DisplayName = "Unset"), |
|||
en UMETA(DisplayName = "English"), |
|||
fr UMETA(DisplayName = "French"), |
|||
de UMETA(DisplayName = "German"), |
|||
es UMETA(DisplayName = "Spanish"), |
|||
pt UMETA(DisplayName = "Portuguese"), |
|||
zh UMETA(DisplayName = "Chinese"), |
|||
ja UMETA(DisplayName = "Japanese"), |
|||
hi UMETA(DisplayName = "Hindi"), |
|||
it UMETA(DisplayName = "Italian"), |
|||
ko UMETA(DisplayName = "Korean"), |
|||
nl UMETA(DisplayName = "Dutch"), |
|||
pl UMETA(DisplayName = "Polish"), |
|||
ru UMETA(DisplayName = "Russian"), |
|||
sv UMETA(DisplayName = "Swedish"), |
|||
tr UMETA(DisplayName = "Turkish"), |
|||
tl UMETA(DisplayName = "Filipino"), |
|||
bg UMETA(DisplayName = "Bulgarian"), |
|||
ro UMETA(DisplayName = "Romanian"), |
|||
ar UMETA(DisplayName = "Arabic"), |
|||
cs UMETA(DisplayName = "Czech"), |
|||
el UMETA(DisplayName = "Greek"), |
|||
fi UMETA(DisplayName = "Finnish"), |
|||
hr UMETA(DisplayName = "Croatian"), |
|||
ms UMETA(DisplayName = "Malay"), |
|||
sk UMETA(DisplayName = "Slovak"), |
|||
da UMETA(DisplayName = "Danish"), |
|||
ta UMETA(DisplayName = "Tamil"), |
|||
uk UMETA(DisplayName = "Ukrainian"), |
|||
hu UMETA(DisplayName = "Hungarian"), |
|||
no UMETA(DisplayName = "Norwegian"), |
|||
vi UMETA(DisplayName = "Vietnamese"), |
|||
bn UMETA(DisplayName = "Bengali"), |
|||
th UMETA(DisplayName = "Thai"), |
|||
he UMETA(DisplayName = "Hebrew"), |
|||
ka UMETA(DisplayName = "Georgian"), |
|||
id UMETA(DisplayName = "Indonesian"), |
|||
te UMETA(DisplayName = "Telugu"), |
|||
gu UMETA(DisplayName = "Gujarati"), |
|||
kn UMETA(DisplayName = "Kannada"), |
|||
ml UMETA(DisplayName = "Malayalam"), |
|||
mr UMETA(DisplayName = "Marathi"), |
|||
pa UMETA(DisplayName = "Punjabi"), |
|||
}; |
|||
|
|||
UENUM(BlueprintType) |
|||
enum class EKeepAliveRule : uint8 { |
|||
EditorOnly UMETA(DisplayName = "Only keep Module open in Editor Mode"), |
|||
Never UMETA(DisplayName = "Never keep Module alive."), |
|||
Always UMETA(DisplayName = "Keep Module alive in Editor and Shipping mode.") |
|||
}; |
|||
|
|||
@ -0,0 +1,14 @@ |
|||
// Copyright Epic Games, Inc. All Rights Reserved.
|
|||
|
|||
#pragma once |
|||
|
|||
#include "Modules/ModuleManager.h" |
|||
|
|||
class FAvatarCore_SharedModule : public IModuleInterface |
|||
{ |
|||
public: |
|||
|
|||
/** IModuleInterface implementation */ |
|||
virtual void StartupModule() override; |
|||
virtual void ShutdownModule() override; |
|||
}; |
|||
@ -0,0 +1,27 @@ |
|||
// Fill out your copyright notice in the Description page of Project Settings.
|
|||
|
|||
#pragma once |
|||
|
|||
#include "CoreMinimal.h" |
|||
#include "Kismet/BlueprintFunctionLibrary.h" |
|||
#include "AvatarCoreSharedEnums.h" |
|||
#include "FL_AvatarCoreShared.generated.h" |
|||
|
|||
/**
|
|||
* |
|||
*/ |
|||
UCLASS() |
|||
class AVATARCORE_SHARED_API UFL_AvatarCoreShared : public UBlueprintFunctionLibrary |
|||
{ |
|||
GENERATED_BODY() |
|||
|
|||
public: |
|||
// Returns the ISO 639-1 code string for the given language enum (e.g. ELanguage::de "de").
|
|||
// Derived from the enum value name via UE reflection, so it stays in sync automatically.
|
|||
UFUNCTION(BlueprintCallable, Category = "AvatarCoreShared") |
|||
static FString LanguageToString(ELanguage Language); |
|||
|
|||
// Returns the ELanguage for the given ISO 639-1 code string
|
|||
UFUNCTION(BlueprintCallable, Category = "AvatarCoreShared") |
|||
static ELanguage StringToLanguage(const FString& InString); |
|||
}; |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"permissions": { |
|||
"allow": [ |
|||
"Bash(Get-ChildItem -Path \"c:\\\\Gitprojects\\\\SchaniConcierge\\\\Unreal\\\\Plugins\\\\AvatarCore_TTS\\\\Source\" -Recurse -Filter \"CartesiaTTSManager.h\")", |
|||
"Bash(Select-Object -ExpandProperty FullName)", |
|||
"Bash(find c:\\\\\\\\Gitprojects\\\\\\\\SchaniConcierge\\\\\\\\Unreal\\\\\\\\Plugins\\\\\\\\AvatarCore_TTS\\\\\\\\Source -name \"CartesiaTTSManager.h\")" |
|||
] |
|||
} |
|||
} |
|||
Binary file not shown.
@ -0,0 +1,109 @@ |
|||
@echo off |
|||
setlocal EnableExtensions EnableDelayedExpansion |
|||
|
|||
rem ============================================================ |
|||
rem Mirror specific folders from A (AvatarBase) to B (this repo) |
|||
rem - Works relative to this .bat location |
|||
rem - Wipes extras in destination (/MIR) |
|||
rem - Logs removed extras (shown as *EXTRA File / *EXTRA Dir) |
|||
rem - Pauses before exit so console stays open |
|||
rem ============================================================ |
|||
|
|||
rem Destination root is the folder where this script lives |
|||
set "SRC_ROOT=%~dp0" |
|||
if "%SRC_ROOT:~-1%"=="\" set "SRC_ROOT=%SRC_ROOT:~0,-1%" |
|||
|
|||
rem Source root relative to this script |
|||
set "DST_ROOT=%SRC_ROOT%\..\..\AvatarBase\Unreal" |
|||
|
|||
rem Canonicalize paths |
|||
for %%I in ("%SRC_ROOT%") do set "SRC_ROOT=%%~fI" |
|||
for %%I in ("%DST_ROOT%") do set "DST_ROOT=%%~fI" |
|||
|
|||
echo ============================================================ |
|||
echo Robocopy Sync Script |
|||
echo ============================================================ |
|||
echo Source: %SRC_ROOT% |
|||
echo Destination: %DST_ROOT% |
|||
echo ============================================================ |
|||
echo. |
|||
|
|||
rem Guard: only intended for subprojects |
|||
if /I "%SRC_ROOT%"=="%DST_ROOT%" ( |
|||
echo ERROR: Source and destination are identical: |
|||
echo %SRC_ROOT% |
|||
echo This sync script is intended to be run from subprojects only. |
|||
echo. |
|||
pause |
|||
exit /b 1 |
|||
) |
|||
|
|||
if not exist "%SRC_ROOT%\" ( |
|||
echo ERROR: Source root does not exist: |
|||
echo %SRC_ROOT% |
|||
echo. |
|||
pause |
|||
exit /b 2 |
|||
) |
|||
|
|||
echo ============================================================ |
|||
echo Push back data from a project to avatar base? Are you sure? y/n |
|||
echo ============================================================ |
|||
|
|||
:PROMPT |
|||
SET /P AREYOUSURE=Are you sure (Y/[N])? |
|||
IF /I "%AREYOUSURE%" NEQ "Y" GOTO END |
|||
|
|||
rem List of folders to mirror |
|||
set "FOLDERS=Plugins\AvatarCore_AI Plugins\AvatarCore_Shared Plugins\AvatarCore_Manager Plugins\AvatarCore_MetaHuman Plugins\AvatarCore_STT Plugins\AvatarCore_TTS Plugins\BLogger Plugins\BSettings Plugins\BTools Plugins\RuntimeMetaHumanLipSync_5.6 Content\Project" |
|||
|
|||
set "ANY_FAIL=0" |
|||
|
|||
for %%F in (%FOLDERS%) do ( |
|||
set "SRC=%SRC_ROOT%\%%F" |
|||
set "DST=%DST_ROOT%\%%F" |
|||
|
|||
echo ------------------------------------------------------------ |
|||
echo Mirroring: %%F |
|||
echo FROM: !SRC! |
|||
echo TO: !DST! |
|||
echo ------------------------------------------------------------ |
|||
|
|||
if not exist "!SRC!\" ( |
|||
echo WARNING: Source folder missing, skipping: !SRC! |
|||
) else ( |
|||
if not exist "!DST!\" ( |
|||
mkdir "!DST!" 2>nul |
|||
) |
|||
|
|||
robocopy "!SRC!" "!DST!" /MIR /DCOPY:DAT /COPY:DAT /R:2 /W:1 /FFT /Z /NP /FP /TS /TEE |
|||
set "RC=!ERRORLEVEL!" |
|||
|
|||
echo Robocopy exit code: !RC! |
|||
|
|||
if !RC! GEQ 8 ( |
|||
echo ERROR: Robocopy reported failure for %%F ^(exit code !RC!^) |
|||
set "ANY_FAIL=1" |
|||
) |
|||
) |
|||
|
|||
echo. |
|||
) |
|||
|
|||
if "!ANY_FAIL!"=="1" ( |
|||
echo ============================================================ |
|||
echo Sync failed! |
|||
echo ============================================================ |
|||
pause |
|||
exit /b 8 |
|||
) |
|||
|
|||
echo ============================================================ |
|||
echo Sync finished. |
|||
echo ============================================================ |
|||
|
|||
:END |
|||
endlocal |
|||
|
|||
pause |
|||
exit /b 0 |
|||
Loading…
Reference in new issue