287 lines
9.1 KiB
C++
287 lines
9.1 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "QwenTTSPresetDetailsCustomization.h"
|
|
|
|
#include "DetailCategoryBuilder.h"
|
|
#include "DetailLayoutBuilder.h"
|
|
#include "DetailWidgetRow.h"
|
|
#include "HttpModule.h"
|
|
#include "Interfaces/IHttpResponse.h"
|
|
#include "Json.h"
|
|
#include "PropertyHandle.h"
|
|
#include "QwenTTSPresetDataAsset.h"
|
|
#include "QwenTTSStreamingLog.h"
|
|
#include "QwenTTSStreamingSettingsDataAsset.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/Input/SComboBox.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "FQwenTTSPresetDetailsCustomization"
|
|
|
|
FText FQwenTTSPresetChoice::GetDisplayText() const
|
|
{
|
|
if (!Label.IsEmpty())
|
|
{
|
|
return FText::FromString(FString::Printf(TEXT("%s (%s)"), *Label, *Id));
|
|
}
|
|
return FText::FromString(Id);
|
|
}
|
|
|
|
TSharedRef<IDetailCustomization> FQwenTTSPresetDetailsCustomization::MakeInstance()
|
|
{
|
|
return MakeShared<FQwenTTSPresetDetailsCustomization>();
|
|
}
|
|
|
|
void FQwenTTSPresetDetailsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
TArray<TWeakObjectPtr<UObject>> Objects;
|
|
DetailBuilder.GetObjectsBeingCustomized(Objects);
|
|
if (Objects.Num() > 0)
|
|
{
|
|
PresetAsset = Cast<UQwenTTSPresetDataAsset>(Objects[0].Get());
|
|
}
|
|
|
|
SettingsHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UQwenTTSPresetDataAsset, Settings));
|
|
PresetIdHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UQwenTTSPresetDataAsset, PresetId));
|
|
DetailBuilder.HideProperty(SettingsHandle);
|
|
DetailBuilder.HideProperty(PresetIdHandle);
|
|
|
|
IDetailCategoryBuilder& PresetCategory = DetailBuilder.EditCategory(TEXT("Qwen TTS|Preset"));
|
|
PresetCategory.AddProperty(SettingsHandle.ToSharedRef());
|
|
PresetCategory.AddCustomRow(LOCTEXT("PresetPickerFilter", "Preset Picker"))
|
|
.NameContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(LOCTEXT("PresetPickerLabel", "Preset Picker"))
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
.ValueContent()
|
|
.MinDesiredWidth(520.0f)
|
|
[
|
|
SNew(SVerticalBox)
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
[
|
|
SAssignNew(PresetComboBox, SComboBox<TSharedPtr<FQwenTTSPresetChoice>>)
|
|
.OptionsSource(&PresetChoices)
|
|
.OnGenerateWidget(this, &FQwenTTSPresetDetailsCustomization::MakePresetComboWidget)
|
|
.OnSelectionChanged(this, &FQwenTTSPresetDetailsCustomization::HandleSelectionChanged)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &FQwenTTSPresetDetailsCustomization::GetSelectedPresetText)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
]
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.Padding(6.0f, 0.0f, 0.0f, 0.0f)
|
|
[
|
|
SNew(SButton)
|
|
.Text(LOCTEXT("RefreshPresets", "Refresh"))
|
|
.OnClicked(this, &FQwenTTSPresetDetailsCustomization::RefreshPresets)
|
|
]
|
|
]
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(0.0f, 4.0f, 0.0f, 0.0f)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &FQwenTTSPresetDetailsCustomization::GetStatusText)
|
|
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
.AutoWrapText(true)
|
|
]
|
|
];
|
|
PresetCategory.AddProperty(PresetIdHandle.ToSharedRef());
|
|
|
|
RefreshPresets();
|
|
}
|
|
|
|
FReply FQwenTTSPresetDetailsCustomization::RefreshPresets()
|
|
{
|
|
const FString StatusUrl = GetStatusUrl();
|
|
if (StatusUrl.IsEmpty())
|
|
{
|
|
StatusText = LOCTEXT("MissingStatusUrl", "Set Settings or Gateway Status Url before refreshing presets.");
|
|
return FReply::Handled();
|
|
}
|
|
|
|
StatusText = FText::Format(LOCTEXT("LoadingPresets", "Loading presets from {0}"), FText::FromString(StatusUrl));
|
|
UE_LOG(LogQwenTTSStreaming, Log, TEXT("Refreshing Qwen TTS presets from %s"), *StatusUrl);
|
|
|
|
const TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
|
|
Request->SetURL(StatusUrl);
|
|
Request->SetVerb(TEXT("GET"));
|
|
Request->SetHeader(TEXT("Accept"), TEXT("application/json"));
|
|
Request->OnProcessRequestComplete().BindSP(StaticCastSharedRef<FQwenTTSPresetDetailsCustomization>(AsShared()), &FQwenTTSPresetDetailsCustomization::HandlePresetResponse);
|
|
Request->ProcessRequest();
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
void FQwenTTSPresetDetailsCustomization::HandlePresetResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
|
|
{
|
|
if (!bWasSuccessful || !Response.IsValid())
|
|
{
|
|
StatusText = LOCTEXT("PresetRequestFailed", "Preset request failed.");
|
|
UE_LOG(LogQwenTTSStreaming, Warning, TEXT("Qwen TTS preset request failed."));
|
|
return;
|
|
}
|
|
|
|
if (Response->GetResponseCode() < 200 || Response->GetResponseCode() >= 300)
|
|
{
|
|
StatusText = FText::Format(LOCTEXT("PresetHttpFailed", "Preset request failed with HTTP {0}."), Response->GetResponseCode());
|
|
UE_LOG(LogQwenTTSStreaming, Warning, TEXT("Qwen TTS preset request failed. HTTP=%d Body=%s"), Response->GetResponseCode(), *Response->GetContentAsString());
|
|
return;
|
|
}
|
|
|
|
SetPresetChoicesFromStatusJson(Response->GetContentAsString());
|
|
}
|
|
|
|
void FQwenTTSPresetDetailsCustomization::SetPresetChoicesFromStatusJson(const FString& ResponseText)
|
|
{
|
|
TSharedPtr<FJsonObject> JsonObject;
|
|
const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseText);
|
|
if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid())
|
|
{
|
|
StatusText = LOCTEXT("BadPresetJson", "Preset response is not valid JSON.");
|
|
UE_LOG(LogQwenTTSStreaming, Warning, TEXT("Qwen TTS preset response is not valid JSON."));
|
|
return;
|
|
}
|
|
|
|
const TArray<TSharedPtr<FJsonValue>>* PresetRefs = nullptr;
|
|
if (!JsonObject->TryGetArrayField(TEXT("preset_refs"), PresetRefs))
|
|
{
|
|
StatusText = LOCTEXT("NoPresetRefs", "Preset response has no preset_refs array.");
|
|
UE_LOG(LogQwenTTSStreaming, Warning, TEXT("Qwen TTS preset response has no preset_refs array."));
|
|
return;
|
|
}
|
|
|
|
PresetChoices.Reset();
|
|
for (const TSharedPtr<FJsonValue>& Value : *PresetRefs)
|
|
{
|
|
const TSharedPtr<FJsonObject> PresetObject = Value.IsValid() ? Value->AsObject() : nullptr;
|
|
if (!PresetObject.IsValid())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FString Id;
|
|
if (!PresetObject->TryGetStringField(TEXT("id"), Id) || Id.IsEmpty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TSharedRef<FQwenTTSPresetChoice> Choice = MakeShared<FQwenTTSPresetChoice>();
|
|
Choice->Id = Id;
|
|
PresetObject->TryGetStringField(TEXT("label"), Choice->Label);
|
|
PresetObject->TryGetStringField(TEXT("ref_text"), Choice->RefText);
|
|
PresetObject->TryGetStringField(TEXT("source"), Choice->Source);
|
|
PresetChoices.Add(Choice);
|
|
}
|
|
|
|
if (PresetComboBox.IsValid())
|
|
{
|
|
PresetComboBox->RefreshOptions();
|
|
}
|
|
|
|
StatusText = FText::Format(LOCTEXT("PresetLoadedCount", "Loaded {0} preset(s)."), PresetChoices.Num());
|
|
UE_LOG(LogQwenTTSStreaming, Log, TEXT("Loaded %d Qwen TTS preset(s)."), PresetChoices.Num());
|
|
}
|
|
|
|
FString FQwenTTSPresetDetailsCustomization::GetStatusUrl() const
|
|
{
|
|
UObject* SettingsObject = nullptr;
|
|
if (SettingsHandle.IsValid() && SettingsHandle->GetValue(SettingsObject) == FPropertyAccess::Success)
|
|
{
|
|
if (const UQwenTTSStreamingSettingsDataAsset* Settings = Cast<UQwenTTSStreamingSettingsDataAsset>(SettingsObject))
|
|
{
|
|
return Settings->GetResolvedGatewayStatusUrl();
|
|
}
|
|
}
|
|
|
|
if (const UQwenTTSPresetDataAsset* Preset = PresetAsset.Get())
|
|
{
|
|
if (Preset->Settings)
|
|
{
|
|
return Preset->Settings->GetResolvedGatewayStatusUrl();
|
|
}
|
|
}
|
|
|
|
return GetDefault<UQwenTTSStreamingSettingsDataAsset>()->GetResolvedGatewayStatusUrl();
|
|
}
|
|
|
|
TSharedPtr<FQwenTTSPresetChoice> FQwenTTSPresetDetailsCustomization::GetSelectedChoice() const
|
|
{
|
|
FString CurrentPresetId;
|
|
if (PresetIdHandle.IsValid())
|
|
{
|
|
PresetIdHandle->GetValue(CurrentPresetId);
|
|
}
|
|
|
|
for (const TSharedPtr<FQwenTTSPresetChoice>& Choice : PresetChoices)
|
|
{
|
|
if (Choice.IsValid() && Choice->Id == CurrentPresetId)
|
|
{
|
|
return Choice;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
FText FQwenTTSPresetDetailsCustomization::GetSelectedPresetText() const
|
|
{
|
|
if (const TSharedPtr<FQwenTTSPresetChoice> Choice = GetSelectedChoice())
|
|
{
|
|
return Choice->GetDisplayText();
|
|
}
|
|
|
|
FString CurrentPresetId;
|
|
if (PresetIdHandle.IsValid() && PresetIdHandle->GetValue(CurrentPresetId) == FPropertyAccess::Success && !CurrentPresetId.IsEmpty())
|
|
{
|
|
return FText::FromString(CurrentPresetId);
|
|
}
|
|
|
|
return LOCTEXT("NoPresetSelected", "Select preset");
|
|
}
|
|
|
|
FText FQwenTTSPresetDetailsCustomization::GetStatusText() const
|
|
{
|
|
if (const TSharedPtr<FQwenTTSPresetChoice> Choice = GetSelectedChoice())
|
|
{
|
|
if (!Choice->RefText.IsEmpty())
|
|
{
|
|
return FText::FromString(FString::Printf(TEXT("%s: %s"), *Choice->Source, *Choice->RefText));
|
|
}
|
|
}
|
|
return StatusText;
|
|
}
|
|
|
|
void FQwenTTSPresetDetailsCustomization::HandleSelectionChanged(TSharedPtr<FQwenTTSPresetChoice> Choice, ESelectInfo::Type SelectInfo)
|
|
{
|
|
if (!Choice.IsValid() || SelectInfo == ESelectInfo::Direct)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (PresetIdHandle.IsValid())
|
|
{
|
|
PresetIdHandle->SetValue(Choice->Id);
|
|
StatusText = FText::Format(LOCTEXT("PresetSelected", "Selected {0}."), Choice->GetDisplayText());
|
|
UE_LOG(LogQwenTTSStreaming, Log, TEXT("Selected Qwen TTS preset. Label=%s Id=%s"), *Choice->Label, *Choice->Id);
|
|
}
|
|
}
|
|
|
|
TSharedRef<SWidget> FQwenTTSPresetDetailsCustomization::MakePresetComboWidget(TSharedPtr<FQwenTTSPresetChoice> Choice) const
|
|
{
|
|
return SNew(STextBlock)
|
|
.Text(Choice.IsValid() ? Choice->GetDisplayText() : LOCTEXT("InvalidPresetChoice", "Invalid preset"))
|
|
.ToolTipText(Choice.IsValid() ? FText::FromString(Choice->RefText) : FText::GetEmpty());
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|