|
|
|
@@ -43,6 +43,7 @@ use shared_contracts::assets::{
|
|
|
|
|
CharacterAssetJobStatusPayload, CharacterAssetJobStatusText, CharacterVisualDraftPayload,
|
|
|
|
|
CharacterWorkflowCacheGetResponse, CharacterWorkflowCachePayload,
|
|
|
|
|
CharacterWorkflowCacheSaveRequest, CharacterWorkflowCacheSaveResponse,
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode,
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalRequest,
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalResponse, EditorCharacterAnimationFramePayload,
|
|
|
|
|
EditorCharacterAnimationFrameSplitRequest, EditorCharacterAnimationFrameSplitResponse,
|
|
|
|
@@ -85,13 +86,13 @@ use crate::{
|
|
|
|
|
editor_generation_source_entity_id, enqueue_editor_generation_job_for_caller,
|
|
|
|
|
},
|
|
|
|
|
editor_green_screen::{
|
|
|
|
|
EditorScreenBackgroundColor, remove_editor_character_animation_green_screen_background,
|
|
|
|
|
EditorScreenBackgroundColor, remove_editor_character_animation_solid_color_background,
|
|
|
|
|
},
|
|
|
|
|
editor_project::{
|
|
|
|
|
EDITOR_ASSET_ID_PREFIX, EDITOR_BGFILTER_CROSS_CHECK_ENABLED,
|
|
|
|
|
EDITOR_BGFILTER_DEFAULT_SEG_MODEL, EDITOR_LOCAL_MATTING_PROVIDER,
|
|
|
|
|
EDITOR_RESOURCE_ID_PREFIX, EditorCanvasGeneratedLayerInput, EditorCanvasLayoutPlan,
|
|
|
|
|
EditorGenerationCaller, EditorGenerationOperationContext,
|
|
|
|
|
EDITOR_BGFILTER_DEFAULT_SEG_MODEL, EDITOR_BGFILTER_MATTING_PROVIDER,
|
|
|
|
|
EDITOR_LOCAL_MATTING_PROVIDER, EDITOR_RESOURCE_ID_PREFIX, EditorCanvasGeneratedLayerInput,
|
|
|
|
|
EditorCanvasLayoutPlan, EditorGenerationCaller, EditorGenerationOperationContext,
|
|
|
|
|
PreparedEditorGenerationResultItem, build_editor_canvas_generated_layer_item,
|
|
|
|
|
download_editor_persisted_image_object, editor_asset_payload_from_record,
|
|
|
|
|
editor_media_src_from_object_key, editor_project_payload_from_record,
|
|
|
|
@@ -100,6 +101,7 @@ use crate::{
|
|
|
|
|
persist_editor_generation_result_atomically, preflight_editor_billable_generation_target,
|
|
|
|
|
rebuild_editor_generation_input_references,
|
|
|
|
|
remove_editor_generated_screen_background_with_bgfilter,
|
|
|
|
|
request_editor_background_removal_image_with_bgfilter_worker,
|
|
|
|
|
resolve_editor_reference_object_key_for_owner, sanitize_editor_client_generation_inputs,
|
|
|
|
|
serialize_editor_generation_inputs, serialize_editor_image_sequence_frames,
|
|
|
|
|
validate_editor_generation_operation_id, with_editor_media_duration_generation_input,
|
|
|
|
@@ -1140,6 +1142,118 @@ fn editor_character_animation_sequence_is_video_conversion(
|
|
|
|
|
&& inputs.get("action").and_then(Value::as_str) == Some("character-animation.convert")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
|
struct NormalizedEditorCharacterAnimationBackgroundRemoval {
|
|
|
|
|
mode: EditorCharacterAnimationBackgroundRemovalMode,
|
|
|
|
|
screen_color: Option<String>,
|
|
|
|
|
key_rgb: Option<[u8; 3]>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn normalize_editor_character_animation_background_removal(
|
|
|
|
|
payload: &EditorCharacterAnimationBackgroundRemovalRequest,
|
|
|
|
|
) -> Result<NormalizedEditorCharacterAnimationBackgroundRemoval, AppError> {
|
|
|
|
|
match payload.mode {
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::SolidColor => {
|
|
|
|
|
let screen_color = payload
|
|
|
|
|
.screen_color
|
|
|
|
|
.as_deref()
|
|
|
|
|
.map(str::trim)
|
|
|
|
|
.filter(|value| !value.is_empty())
|
|
|
|
|
.ok_or_else(|| {
|
|
|
|
|
editor_character_animation_bad_request("纯色背景抠图必须提供 screenColor。")
|
|
|
|
|
})?;
|
|
|
|
|
let key_rgb = parse_editor_character_animation_screen_color(screen_color)?;
|
|
|
|
|
Ok(NormalizedEditorCharacterAnimationBackgroundRemoval {
|
|
|
|
|
mode: payload.mode,
|
|
|
|
|
screen_color: Some(screen_color.to_ascii_uppercase()),
|
|
|
|
|
key_rgb: Some(key_rgb),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::General => {
|
|
|
|
|
if payload.screen_color.is_some() {
|
|
|
|
|
return Err(editor_character_animation_bad_request(
|
|
|
|
|
"通用智能抠图不能携带 screenColor。",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
Ok(NormalizedEditorCharacterAnimationBackgroundRemoval {
|
|
|
|
|
mode: payload.mode,
|
|
|
|
|
screen_color: None,
|
|
|
|
|
key_rgb: None,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_editor_character_animation_screen_color(value: &str) -> Result<[u8; 3], AppError> {
|
|
|
|
|
let bytes = value.as_bytes();
|
|
|
|
|
if bytes.len() != 7 || bytes[0] != b'#' || !bytes[1..].iter().all(u8::is_ascii_hexdigit) {
|
|
|
|
|
return Err(editor_character_animation_bad_request(
|
|
|
|
|
"screenColor 必须是 #RRGGBB。",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
let parse_channel = |start: usize| {
|
|
|
|
|
u8::from_str_radix(&value[start..start + 2], 16)
|
|
|
|
|
.map_err(|_| editor_character_animation_bad_request("screenColor 必须是 #RRGGBB。"))
|
|
|
|
|
};
|
|
|
|
|
Ok([parse_channel(1)?, parse_channel(3)?, parse_channel(5)?])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn apply_editor_character_animation_background_removal_metadata(
|
|
|
|
|
generation_inputs: Option<Value>,
|
|
|
|
|
options: &NormalizedEditorCharacterAnimationBackgroundRemoval,
|
|
|
|
|
) -> Value {
|
|
|
|
|
let mut inputs = generation_inputs
|
|
|
|
|
.and_then(|value| value.as_object().cloned())
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
inputs.insert("version".to_string(), json!(2));
|
|
|
|
|
inputs.insert(
|
|
|
|
|
"action".to_string(),
|
|
|
|
|
json!("character-animation.remove-background"),
|
|
|
|
|
);
|
|
|
|
|
let mut fields = inputs
|
|
|
|
|
.remove("fields")
|
|
|
|
|
.and_then(|value| value.as_array().cloned())
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
fields.retain(|field| {
|
|
|
|
|
!matches!(
|
|
|
|
|
field.get("id").and_then(Value::as_str),
|
|
|
|
|
Some("backgroundRemovalMode" | "screenColor" | "mattingProvider" | "mattingModel")
|
|
|
|
|
)
|
|
|
|
|
});
|
|
|
|
|
fields.push(json!({
|
|
|
|
|
"id": "backgroundRemovalMode",
|
|
|
|
|
"title": "去背景方式",
|
|
|
|
|
"value": match options.mode {
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::SolidColor => "solid-color",
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::General => "general",
|
|
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
if let Some(screen_color) = options.screen_color.as_deref() {
|
|
|
|
|
fields.push(json!({
|
|
|
|
|
"id": "screenColor",
|
|
|
|
|
"title": "背景色",
|
|
|
|
|
"value": screen_color,
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
fields.push(json!({
|
|
|
|
|
"id": "mattingProvider",
|
|
|
|
|
"title": "处理服务",
|
|
|
|
|
"value": match options.mode {
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::SolidColor => EDITOR_LOCAL_MATTING_PROVIDER,
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::General => EDITOR_BGFILTER_MATTING_PROVIDER,
|
|
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
if options.mode == EditorCharacterAnimationBackgroundRemovalMode::General {
|
|
|
|
|
fields.push(json!({
|
|
|
|
|
"id": "mattingModel",
|
|
|
|
|
"title": "抠图模型",
|
|
|
|
|
"value": EDITOR_BGFILTER_DEFAULT_SEG_MODEL,
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
inputs.insert("fields".to_string(), Value::Array(fields));
|
|
|
|
|
Value::Object(inputs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn remove_editor_character_animation_background(
|
|
|
|
|
State(state): State<AppState>,
|
|
|
|
|
Extension(request_context): Extension<RequestContext>,
|
|
|
|
@@ -1186,6 +1300,9 @@ pub(crate) async fn enqueue_editor_character_animation_background_removal_for_ow
|
|
|
|
|
owner_user_id: &str,
|
|
|
|
|
mut payload: EditorCharacterAnimationBackgroundRemovalRequest,
|
|
|
|
|
) -> Result<ExternalGenerationJobRecord, Response> {
|
|
|
|
|
let options = normalize_editor_character_animation_background_removal(&payload)
|
|
|
|
|
.map_err(|error| character_animation_error_response(request_context, error))?;
|
|
|
|
|
payload.screen_color = options.screen_color;
|
|
|
|
|
let target = preflight_editor_billable_generation_target(
|
|
|
|
|
state,
|
|
|
|
|
owner_user_id,
|
|
|
|
@@ -1230,6 +1347,8 @@ pub(crate) async fn remove_editor_character_animation_background_for_owner(
|
|
|
|
|
editor_character_animation_bad_request(error.body_text()),
|
|
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
let options = normalize_editor_character_animation_background_removal(&payload)
|
|
|
|
|
.map_err(|error| character_animation_error_response(&request_context, error))?;
|
|
|
|
|
let owner_user_id = caller.owner_user_id.clone();
|
|
|
|
|
let project_id = payload.project_id.trim().to_string();
|
|
|
|
|
let source_layer_id = payload.source_layer_id.trim().to_string();
|
|
|
|
@@ -1366,6 +1485,15 @@ pub(crate) async fn remove_editor_character_animation_background_for_owner(
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|error| character_animation_error_response(&request_context, error))?;
|
|
|
|
|
}
|
|
|
|
|
let matting_audit = crate::external_api_audit::ExternalApiAuditContext {
|
|
|
|
|
user_id: caller.audit_subject_user_id.clone(),
|
|
|
|
|
profile_id: caller
|
|
|
|
|
.audit_project_id
|
|
|
|
|
.clone()
|
|
|
|
|
.or_else(|| Some(project_id.clone())),
|
|
|
|
|
request_id: Some(request_context.request_id().to_string()),
|
|
|
|
|
external_call_deadline: request_context.external_call_deadline(),
|
|
|
|
|
};
|
|
|
|
|
let persisted_frames = extract_and_prepare_background_removal_frames(
|
|
|
|
|
&state,
|
|
|
|
|
&caller,
|
|
|
|
@@ -1373,6 +1501,8 @@ pub(crate) async fn remove_editor_character_animation_background_for_owner(
|
|
|
|
|
&frames,
|
|
|
|
|
frame_width,
|
|
|
|
|
frame_height,
|
|
|
|
|
&options,
|
|
|
|
|
&matting_audit,
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|error| character_animation_error_response(&request_context, error))?;
|
|
|
|
@@ -1400,13 +1530,23 @@ pub(crate) async fn remove_editor_character_animation_background_for_owner(
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|error| character_animation_error_response(&request_context, error))?;
|
|
|
|
|
let generation_inputs = Some(
|
|
|
|
|
apply_editor_character_animation_background_removal_metadata(generation_inputs, &options),
|
|
|
|
|
);
|
|
|
|
|
let completed_at_micros = current_utc_micros();
|
|
|
|
|
let prepared = match prepare_editor_character_animation_sequence_result(
|
|
|
|
|
&caller,
|
|
|
|
|
Some(project_id.as_str()),
|
|
|
|
|
payload.asset_folder_id.as_deref(),
|
|
|
|
|
asset_label.as_str(),
|
|
|
|
|
EDITOR_LOCAL_MATTING_PROVIDER,
|
|
|
|
|
match options.mode {
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::SolidColor => {
|
|
|
|
|
EDITOR_LOCAL_MATTING_PROVIDER
|
|
|
|
|
}
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::General => {
|
|
|
|
|
EDITOR_BGFILTER_MATTING_PROVIDER
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
Some(source_resource_id.clone()),
|
|
|
|
|
generation_inputs,
|
|
|
|
|
plan,
|
|
|
|
@@ -5020,8 +5160,8 @@ async fn extract_and_prepare_direct_video_conversion_frames(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 对已有序列帧逐帧执行纯算法绿幕扣除,生成透明序列帧对象。
|
|
|
|
|
/// 输入是源序列帧资源的稳定 objectKey 列表;不调用 BgFilter、阿里云或其它模型。
|
|
|
|
|
/// 对已有序列帧按用户显式选择执行本地纯色键控或 BgFilter 通用智能分割,
|
|
|
|
|
/// 生成一套新的透明序列帧对象。输入只接受源资源的正式 objectKey 列表;
|
|
|
|
|
/// 任一帧失败即整批清理,不发布残缺结果。
|
|
|
|
|
async fn extract_and_prepare_background_removal_frames(
|
|
|
|
|
state: &AppState,
|
|
|
|
@@ -5030,6 +5170,8 @@ async fn extract_and_prepare_background_removal_frames(
|
|
|
|
|
source_frames: &[EditorCharacterAnimationFramePayload],
|
|
|
|
|
frame_width: u32,
|
|
|
|
|
frame_height: u32,
|
|
|
|
|
options: &NormalizedEditorCharacterAnimationBackgroundRemoval,
|
|
|
|
|
matting_audit: &crate::external_api_audit::ExternalApiAuditContext,
|
|
|
|
|
) -> Result<PersistedEditorCharacterAnimationFrameSet, AppError> {
|
|
|
|
|
use futures_util::StreamExt as _;
|
|
|
|
|
|
|
|
|
@@ -5047,10 +5189,28 @@ async fn extract_and_prepare_background_removal_frames(
|
|
|
|
|
"序列帧缺少稳定 objectKey,无法去背景。",
|
|
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
let source_image =
|
|
|
|
|
download_editor_persisted_image_object(state, source_object_key).await?;
|
|
|
|
|
let removed =
|
|
|
|
|
remove_editor_character_animation_green_screen_background(&source_image)?;
|
|
|
|
|
let removed = match options.mode {
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::SolidColor => {
|
|
|
|
|
let source_image =
|
|
|
|
|
download_editor_persisted_image_object(state, source_object_key)
|
|
|
|
|
.await?;
|
|
|
|
|
remove_editor_character_animation_solid_color_background(
|
|
|
|
|
&source_image,
|
|
|
|
|
options
|
|
|
|
|
.key_rgb
|
|
|
|
|
.expect("solid-color options require key RGB"),
|
|
|
|
|
)?
|
|
|
|
|
}
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::General => {
|
|
|
|
|
request_editor_background_removal_image_with_bgfilter_worker(
|
|
|
|
|
state,
|
|
|
|
|
source_object_key,
|
|
|
|
|
matting_audit,
|
|
|
|
|
)
|
|
|
|
|
.await?
|
|
|
|
|
.image
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let finalized = finalize_animation_frame_payload(
|
|
|
|
|
removed.bytes.as_slice(),
|
|
|
|
|
removed.mime_type.as_str(),
|
|
|
|
@@ -10309,9 +10469,115 @@ mod tests {
|
|
|
|
|
assert!(source.contains("content_type.starts_with(\"image/\")"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn editor_character_animation_background_removal_request(
|
|
|
|
|
mode: EditorCharacterAnimationBackgroundRemovalMode,
|
|
|
|
|
screen_color: Option<&str>,
|
|
|
|
|
) -> EditorCharacterAnimationBackgroundRemovalRequest {
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalRequest {
|
|
|
|
|
project_id: "project-1".to_string(),
|
|
|
|
|
source_layer_id: "layer-1".to_string(),
|
|
|
|
|
source_resource_id: "resource-1".to_string(),
|
|
|
|
|
mode,
|
|
|
|
|
screen_color: screen_color.map(str::to_string),
|
|
|
|
|
asset_folder_id: None,
|
|
|
|
|
asset_label: None,
|
|
|
|
|
canvas_completion: None,
|
|
|
|
|
generation_inputs: None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn editor_character_animation_background_removal_uses_local_green_screen_only() {
|
|
|
|
|
fn editor_character_animation_background_removal_requires_consistent_explicit_mode() {
|
|
|
|
|
let solid = normalize_editor_character_animation_background_removal(
|
|
|
|
|
&editor_character_animation_background_removal_request(
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::SolidColor,
|
|
|
|
|
Some("#33aaFF"),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.expect("solid color mode should normalize");
|
|
|
|
|
assert_eq!(solid.screen_color.as_deref(), Some("#33AAFF"));
|
|
|
|
|
assert_eq!(solid.key_rgb, Some([0x33, 0xAA, 0xFF]));
|
|
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
|
normalize_editor_character_animation_background_removal(
|
|
|
|
|
&editor_character_animation_background_removal_request(
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::SolidColor,
|
|
|
|
|
None,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.is_err()
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
normalize_editor_character_animation_background_removal(
|
|
|
|
|
&editor_character_animation_background_removal_request(
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::SolidColor,
|
|
|
|
|
Some("green"),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.is_err()
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
normalize_editor_character_animation_background_removal(
|
|
|
|
|
&editor_character_animation_background_removal_request(
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::General,
|
|
|
|
|
Some("#00FF00"),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.is_err()
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
normalize_editor_character_animation_background_removal(
|
|
|
|
|
&editor_character_animation_background_removal_request(
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::General,
|
|
|
|
|
None,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.is_ok()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn editor_character_animation_background_removal_persists_authoritative_mode_metadata() {
|
|
|
|
|
let options = normalize_editor_character_animation_background_removal(
|
|
|
|
|
&editor_character_animation_background_removal_request(
|
|
|
|
|
EditorCharacterAnimationBackgroundRemovalMode::General,
|
|
|
|
|
None,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.expect("general mode should normalize");
|
|
|
|
|
let inputs = apply_editor_character_animation_background_removal_metadata(None, &options);
|
|
|
|
|
assert_eq!(inputs["version"], json!(2));
|
|
|
|
|
assert_eq!(
|
|
|
|
|
inputs["action"],
|
|
|
|
|
json!("character-animation.remove-background")
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
inputs["fields"]
|
|
|
|
|
.as_array()
|
|
|
|
|
.is_some_and(|fields| fields.iter().any(|field| {
|
|
|
|
|
field["id"] == "backgroundRemovalMode" && field["value"] == "general"
|
|
|
|
|
}))
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
inputs["fields"]
|
|
|
|
|
.as_array()
|
|
|
|
|
.is_some_and(|fields| fields.iter().any(|field| {
|
|
|
|
|
field["id"] == "mattingProvider"
|
|
|
|
|
&& field["value"] == EDITOR_BGFILTER_MATTING_PROVIDER
|
|
|
|
|
}))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn editor_character_animation_background_removal_routes_both_modes_before_provider_work() {
|
|
|
|
|
let source = include_str!("character_animation_assets.rs");
|
|
|
|
|
let enqueue_body = source
|
|
|
|
|
.split_once("pub(crate) async fn enqueue_editor_character_animation_background_removal_for_owner")
|
|
|
|
|
.and_then(|(_, tail)| tail.split_once("pub(crate) async fn remove_editor_character_animation_background_for_owner"))
|
|
|
|
|
.map(|(body, _)| body)
|
|
|
|
|
.expect("background removal enqueue function should exist");
|
|
|
|
|
assert!(enqueue_body.contains("normalize_editor_character_animation_background_removal"));
|
|
|
|
|
|
|
|
|
|
let body = source
|
|
|
|
|
.split_once("async fn extract_and_prepare_background_removal_frames")
|
|
|
|
|
.and_then(|(_, tail)| {
|
|
|
|
@@ -10320,15 +10586,16 @@ mod tests {
|
|
|
|
|
.map(|(body, _)| body)
|
|
|
|
|
.expect("background removal frame function should exist");
|
|
|
|
|
|
|
|
|
|
assert!(body.contains("remove_editor_character_animation_green_screen_background"));
|
|
|
|
|
assert!(body.contains("EditorCharacterAnimationBackgroundRemovalMode::SolidColor"));
|
|
|
|
|
assert!(body.contains("remove_editor_character_animation_solid_color_background"));
|
|
|
|
|
assert!(body.contains("download_editor_persisted_image_object"));
|
|
|
|
|
assert!(body.contains("EditorCharacterAnimationBackgroundRemovalMode::General"));
|
|
|
|
|
assert!(body.contains("request_editor_background_removal_image_with_bgfilter_worker"));
|
|
|
|
|
assert!(body.contains(".buffer_unordered(frame_count.max(1))"));
|
|
|
|
|
assert!(body.contains(".collect::<Vec<_>>()"));
|
|
|
|
|
assert!(body.contains("processed.sort_by_key"));
|
|
|
|
|
assert!(body.contains("frame_errors.sort_by_key"));
|
|
|
|
|
assert!(!body.contains("request_editor_background_removal_image_with_bgfilter_worker"));
|
|
|
|
|
assert!(!body.contains("remove_editor_generated_screen_background_with_bgfilter"));
|
|
|
|
|
assert!(!body.contains("ExternalApiAuditContext"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|