diff --git a/README.md b/README.md index a524ae9d..6f7bcbf7 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,12 @@ commands: run run the program headless-run run the program in headless mode force-download force automate downloads and exit + job-list list jobs by status job-create create a drafted job job-submit submit a drafted job to become a queued job job-submit-all submit all drafted jobs to become a queued jobs job-delete delete a drafted, queued, failed or completed job job-delete-all delete all drafted, queued, failed and completed jobs - job-list list jobs by status job-add-step add a step to a drafted job job-remix-step remix a previous step from a drafted job job-insert-step insert a step to a drafted job diff --git a/facefusion.ini b/facefusion.ini index bba59217..8b23a0e3 100644 --- a/facefusion.ini +++ b/facefusion.ini @@ -6,8 +6,8 @@ output_path = [face_detector] face_detector_model = -face_detector_angles = face_detector_size = +face_detector_angles = face_detector_score = [face_landmarker] @@ -17,10 +17,10 @@ face_landmarker_score = [face_selector] face_selector_mode = face_selector_order = -face_selector_gender = -face_selector_race = face_selector_age_start = face_selector_age_end = +face_selector_gender = +face_selector_race = reference_face_position = reference_face_distance = reference_frame_number = @@ -75,8 +75,8 @@ face_enhancer_blend = face_swapper_model = face_swapper_pixel_boost = frame_colorizer_model = -frame_colorizer_blend = frame_colorizer_size = +frame_colorizer_blend = frame_enhancer_model = frame_enhancer_blend = lip_syncer_model = diff --git a/facefusion/args.py b/facefusion/args.py index 8ab29d34..416546b7 100644 --- a/facefusion/args.py +++ b/facefusion/args.py @@ -50,10 +50,10 @@ def apply_args(args : Args, apply_state_item : ApplyStateItem) -> None: # face selector state_manager.init_item('face_selector_mode', args.get('face_selector_mode')) state_manager.init_item('face_selector_order', args.get('face_selector_order')) - state_manager.init_item('face_selector_gender', args.get('face_selector_gender')) - state_manager.init_item('face_selector_race', args.get('face_selector_race')) state_manager.init_item('face_selector_age_start', args.get('face_selector_age_start')) state_manager.init_item('face_selector_age_end', args.get('face_selector_age_end')) + state_manager.init_item('face_selector_gender', args.get('face_selector_gender')) + state_manager.init_item('face_selector_race', args.get('face_selector_race')) state_manager.init_item('reference_face_position', args.get('reference_face_position')) state_manager.init_item('reference_face_distance', args.get('reference_face_distance')) state_manager.init_item('reference_frame_number', args.get('reference_frame_number')) @@ -97,10 +97,9 @@ def apply_args(args : Args, apply_state_item : ApplyStateItem) -> None: for processor_module in get_processors_modules(available_processors): processor_module.apply_args(args, apply_state_item) # uis - if args.get('command') == 'run': - apply_state_item('open_browser', args.get('open_browser')) - apply_state_item('ui_layouts', args.get('ui_layouts')) - apply_state_item('ui_workflow', args.get('ui_workflow')) + apply_state_item('open_browser', args.get('open_browser')) + apply_state_item('ui_layouts', args.get('ui_layouts')) + apply_state_item('ui_workflow', args.get('ui_workflow')) # execution apply_state_item('execution_device_id', args.get('execution_device_id')) apply_state_item('execution_providers', args.get('execution_providers')) diff --git a/facefusion/common_helper.py b/facefusion/common_helper.py index e8d9e4ba..4cedbcff 100644 --- a/facefusion/common_helper.py +++ b/facefusion/common_helper.py @@ -1,5 +1,5 @@ import platform -from typing import Any, Sequence +from typing import Any, Optional, Sequence def is_linux() -> bool: @@ -50,6 +50,20 @@ def calc_float_step(float_range : Sequence[float]) -> float: return round(float_range[1] - float_range[0], 2) +def cast_int(value : Any) -> Optional[Any]: + try: + return int(value) + except (ValueError, TypeError): + return None + + +def cast_float(value : Any) -> Optional[Any]: + try: + return float(value) + except (ValueError, TypeError): + return None + + def get_first(__list__ : Any) -> Any: return next(iter(__list__), None) diff --git a/facefusion/config.py b/facefusion/config.py index 928052b0..a1161fbb 100644 --- a/facefusion/config.py +++ b/facefusion/config.py @@ -2,6 +2,7 @@ from configparser import ConfigParser from typing import Any, List, Optional from facefusion import state_manager +from facefusion.common_helper import cast_float, cast_int CONFIG = None @@ -33,7 +34,7 @@ def get_int_value(key : str, fallback : Optional[str] = None) -> Optional[int]: value = get_value_by_notation(key) if value or fallback: - return int(value or fallback) + return cast_int(value or fallback) return None @@ -41,7 +42,7 @@ def get_float_value(key : str, fallback : Optional[str] = None) -> Optional[floa value = get_value_by_notation(key) if value or fallback: - return float(value or fallback) + return cast_float(value or fallback) return None @@ -67,7 +68,7 @@ def get_int_list(key : str, fallback : Optional[str] = None) -> Optional[List[in value = get_value_by_notation(key) if value or fallback: - return [ int(value) for value in (value or fallback).split(' ') ] + return [ cast_int(value) for value in (value or fallback).split(' ') ] return None @@ -75,7 +76,7 @@ def get_float_list(key : str, fallback : Optional[str] = None) -> Optional[List[ value = get_value_by_notation(key) if value or fallback: - return [ float(value) for value in (value or fallback).split(' ') ] + return [ cast_float(value) for value in (value or fallback).split(' ') ] return None diff --git a/facefusion/core.py b/facefusion/core.py index 3bf2033f..f0f7dfe7 100755 --- a/facefusion/core.py +++ b/facefusion/core.py @@ -50,7 +50,7 @@ def route(args : Args) -> None: if state_manager.get_item('command') == 'force-download': error_code = force_download() return conditional_exit(error_code) - if state_manager.get_item('command') in [ 'job-create', 'job-submit', 'job-submit-all', 'job-delete', 'job-delete-all', 'job-add-step', 'job-remix-step', 'job-insert-step', 'job-remove-step', 'job-list' ]: + if state_manager.get_item('command') in [ 'job-list', 'job-create', 'job-submit', 'job-submit-all', 'job-delete', 'job-delete-all', 'job-add-step', 'job-remix-step', 'job-insert-step', 'job-remove-step' ]: if not job_manager.init_jobs(state_manager.get_item('jobs_path')): hard_exit(1) error_code = route_job_manager(args) @@ -177,6 +177,13 @@ def force_download() -> ErrorCode: def route_job_manager(args : Args) -> ErrorCode: + if state_manager.get_item('command') == 'job-list': + job_headers, job_contents = compose_job_list(state_manager.get_item('job_status')) + + if job_contents: + logger.table(job_headers, job_contents) + return 0 + return 1 if state_manager.get_item('command') == 'job-create': if job_manager.create_job(state_manager.get_item('job_id')): logger.info(wording.get('job_created').format(job_id = state_manager.get_item('job_id')), __name__) @@ -207,13 +214,6 @@ def route_job_manager(args : Args) -> ErrorCode: return 0 logger.error(wording.get('job_all_not_deleted'), __name__) return 1 - if state_manager.get_item('command') == 'job-list': - job_headers, job_contents = compose_job_list(state_manager.get_item('job_status')) - - if job_contents: - logger.table(job_headers, job_contents) - return 0 - return 1 if state_manager.get_item('command') == 'job-add-step': step_args = reduce_step_args(args) @@ -403,15 +403,15 @@ def process_video(start_time : float) -> ErrorCode: logger.info(wording.get('skipping_audio'), __name__) move_temp_file(state_manager.get_item('target_path'), state_manager.get_item('output_path')) else: - if 'lip_syncer' in state_manager.get_item('processors'): - source_audio_path = get_first(filter_audio_paths(state_manager.get_item('source_paths'))) - if source_audio_path and replace_audio(state_manager.get_item('target_path'), source_audio_path, state_manager.get_item('output_path')): - logger.debug(wording.get('restoring_audio_succeed'), __name__) + source_audio_path = get_first(filter_audio_paths(state_manager.get_item('source_paths'))) + if source_audio_path: + if replace_audio(state_manager.get_item('target_path'), source_audio_path, state_manager.get_item('output_path')): + logger.debug(wording.get('replacing_audio_succeed'), __name__) else: if is_process_stopping(): process_manager.end() return 4 - logger.warn(wording.get('restoring_audio_skipped'), __name__) + logger.warn(wording.get('replacing_audio_skipped'), __name__) move_temp_file(state_manager.get_item('target_path'), state_manager.get_item('output_path')) else: if restore_audio(state_manager.get_item('target_path'), state_manager.get_item('output_path'), state_manager.get_item('output_video_fps')): diff --git a/facefusion/execution.py b/facefusion/execution.py index 771923e4..6ce3a697 100644 --- a/facefusion/execution.py +++ b/facefusion/execution.py @@ -6,7 +6,7 @@ from typing import Any, List from onnxruntime import get_available_providers, set_default_logger_severity from facefusion.choices import execution_provider_set -from facefusion.typing import ExecutionDevice, ExecutionProviderKey, ExecutionProviderSet, ExecutionProviderValue, ValueAndUnit +from facefusion.typing import ExecutionDevice, ExecutionProviderKey, ExecutionProviderSet, ValueAndUnit set_default_logger_severity(3) @@ -29,23 +29,18 @@ def get_available_execution_provider_set() -> ExecutionProviderSet: return available_execution_provider_set -def extract_execution_providers(execution_provider_keys : List[ExecutionProviderKey]) -> List[ExecutionProviderValue]: - return [ execution_provider_set[execution_provider_key] for execution_provider_key in execution_provider_keys if execution_provider_key in execution_provider_set ] - - def create_execution_providers(execution_device_id : str, execution_provider_keys : List[ExecutionProviderKey]) -> List[Any]: - execution_providers = extract_execution_providers(execution_provider_keys) - execution_providers_with_options : List[Any] = [] + execution_providers : List[Any] = [] - for execution_provider in execution_providers: - if execution_provider == 'CUDAExecutionProvider': - execution_providers_with_options.append((execution_provider, + for execution_provider_key in execution_provider_keys: + if execution_provider_key == 'cuda': + execution_providers.append((execution_provider_set.get(execution_provider_key), { 'device_id': execution_device_id, 'cudnn_conv_algo_search': 'EXHAUSTIVE' if use_exhaustive() else 'DEFAULT' })) - if execution_provider == 'TensorrtExecutionProvider': - execution_providers_with_options.append((execution_provider, + if execution_provider_key == 'tensorrt': + execution_providers.append((execution_provider_set.get(execution_provider_key), { 'device_id': execution_device_id, 'trt_engine_cache_enable': True, @@ -54,24 +49,24 @@ def create_execution_providers(execution_device_id : str, execution_provider_key 'trt_timing_cache_path': '.caches', 'trt_builder_optimization_level': 5 })) - if execution_provider == 'OpenVINOExecutionProvider': - execution_providers_with_options.append((execution_provider, + if execution_provider_key == 'openvino': + execution_providers.append((execution_provider_set.get(execution_provider_key), { 'device_type': 'GPU.' + execution_device_id, 'precision': 'FP32' })) - if execution_provider in [ 'DmlExecutionProvider', 'ROCMExecutionProvider' ]: - execution_providers_with_options.append((execution_provider, + if execution_provider_key in [ 'directml', 'rocm' ]: + execution_providers.append((execution_provider_set.get(execution_provider_key), { 'device_id': execution_device_id })) - if execution_provider == 'CoreMLExecutionProvider': - execution_providers_with_options.append(execution_provider) + if execution_provider_key == 'coreml': + execution_providers.append(execution_provider_set.get(execution_provider_key)) - if 'CPUExecutionProvider' in execution_providers: - execution_providers_with_options.append('CPUExecutionProvider') + if 'cpu' in execution_provider_keys: + execution_providers.append(execution_provider_set.get('cpu')) - return execution_providers_with_options + return execution_providers def use_exhaustive() -> bool: diff --git a/facefusion/logger.py b/facefusion/logger.py index 1ea484e0..c951c0b0 100644 --- a/facefusion/logger.py +++ b/facefusion/logger.py @@ -50,6 +50,7 @@ def table(headers : TableHeaders, contents : TableContents) -> None: package_logger.info(table_separator) for content in contents: + content = [ value if value else '' for value in content ] package_logger.info(table_column.format(*content)) package_logger.info(table_separator) diff --git a/facefusion/processors/modules/age_modifier.py b/facefusion/processors/modules/age_modifier.py index c1e39498..ec67f8cf 100755 --- a/facefusion/processors/modules/age_modifier.py +++ b/facefusion/processors/modules/age_modifier.py @@ -13,7 +13,7 @@ from facefusion import config, content_analyser, face_classifier, face_detector, from facefusion.common_helper import create_int_metavar from facefusion.download import conditional_download_hashes, conditional_download_sources from facefusion.face_analyser import get_many_faces, get_one_face -from facefusion.face_helper import merge_matrix, paste_back, warp_face_by_face_landmark_5 +from facefusion.face_helper import merge_matrix, paste_back, scale_face_landmark_5, warp_face_by_face_landmark_5 from facefusion.face_masker import create_occlusion_mask, create_static_box_mask from facefusion.face_selector import find_similar_faces, sort_and_filter_faces from facefusion.face_store import get_reference_faces @@ -119,7 +119,7 @@ def modify_age(target_face : Face, temp_vision_frame : VisionFrame) -> VisionFra model_size = get_model_options().get('size') crop_size = (model_size[0] // 2, model_size[1] // 2) face_landmark_5 = target_face.landmark_set.get('5/68').copy() - extend_face_landmark_5 = (face_landmark_5 - face_landmark_5[2]) * 2 + face_landmark_5[2] + extend_face_landmark_5 = scale_face_landmark_5(face_landmark_5, 2.0) crop_vision_frame, affine_matrix = warp_face_by_face_landmark_5(temp_vision_frame, face_landmark_5, model_template, crop_size) extend_vision_frame, extend_affine_matrix = warp_face_by_face_landmark_5(temp_vision_frame, extend_face_landmark_5, model_template, model_size) extend_vision_frame_raw = extend_vision_frame.copy() diff --git a/facefusion/processors/modules/face_editor.py b/facefusion/processors/modules/face_editor.py index 7e77d200..22682bec 100755 --- a/facefusion/processors/modules/face_editor.py +++ b/facefusion/processors/modules/face_editor.py @@ -132,8 +132,8 @@ def register_args(program : ArgumentParser) -> None: group_processors.add_argument('--face-editor-mouth-position-horizontal', help = wording.get('help.face_editor_mouth_position_horizontal'), type = float, default = config.get_float_value('processors.face_editor_mouth_position_horizontal', '0'), choices = processors_choices.face_editor_mouth_position_horizontal_range, metavar = create_float_metavar(processors_choices.face_editor_mouth_position_horizontal_range)) group_processors.add_argument('--face-editor-mouth-position-vertical', help = wording.get('help.face_editor_mouth_position_vertical'), type = float, default = config.get_float_value('processors.face_editor_mouth_position_vertical', '0'), choices = processors_choices.face_editor_mouth_position_vertical_range, metavar = create_float_metavar(processors_choices.face_editor_mouth_position_vertical_range)) group_processors.add_argument('--face-editor-head-pitch', help = wording.get('help.face_editor_head_pitch'), type = float, default = config.get_float_value('processors.face_editor_head_pitch', '0'), choices = processors_choices.face_editor_head_pitch_range, metavar = create_float_metavar(processors_choices.face_editor_head_pitch_range)) - group_processors.add_argument('--face-editor-head-yaw', help=wording.get('help.face_editor_head_yaw'), type = float, default = config.get_float_value('processors.face_editor_head_yaw', '0'), choices = processors_choices.face_editor_head_yaw_range, metavar = create_float_metavar(processors_choices.face_editor_head_yaw_range)) - group_processors.add_argument('--face-editor-head-roll', help=wording.get('help.face_editor_head_roll'), type = float, default = config.get_float_value('processors.face_editor_head_roll', '0'), choices = processors_choices.face_editor_head_roll_range, metavar = create_float_metavar(processors_choices.face_editor_head_roll_range)) + group_processors.add_argument('--face-editor-head-yaw', help = wording.get('help.face_editor_head_yaw'), type = float, default = config.get_float_value('processors.face_editor_head_yaw', '0'), choices = processors_choices.face_editor_head_yaw_range, metavar = create_float_metavar(processors_choices.face_editor_head_yaw_range)) + group_processors.add_argument('--face-editor-head-roll', help = wording.get('help.face_editor_head_roll'), type = float, default = config.get_float_value('processors.face_editor_head_roll', '0'), choices = processors_choices.face_editor_head_roll_range, metavar = create_float_metavar(processors_choices.face_editor_head_roll_range)) facefusion.jobs.job_store.register_step_keys([ 'face_editor_model', 'face_editor_eyebrow_direction', 'face_editor_eye_gaze_horizontal', 'face_editor_eye_gaze_vertical', 'face_editor_eye_open_ratio', 'face_editor_lip_open_ratio', 'face_editor_mouth_grim', 'face_editor_mouth_pout', 'face_editor_mouth_purse', 'face_editor_mouth_smile', 'face_editor_mouth_position_horizontal', 'face_editor_mouth_position_vertical', 'face_editor_head_pitch', 'face_editor_head_yaw', 'face_editor_head_roll' ]) diff --git a/facefusion/processors/modules/frame_colorizer.py b/facefusion/processors/modules/frame_colorizer.py index 43f6b3d7..256d8594 100644 --- a/facefusion/processors/modules/frame_colorizer.py +++ b/facefusion/processors/modules/frame_colorizer.py @@ -143,8 +143,8 @@ def register_args(program : ArgumentParser) -> None: group_processors = find_argument_group(program, 'processors') if group_processors: group_processors.add_argument('--frame-colorizer-model', help = wording.get('help.frame_colorizer_model'), default = config.get_str_value('processors.frame_colorizer_model', 'ddcolor'), choices = processors_choices.frame_colorizer_models) - group_processors.add_argument('--frame-colorizer-blend', help = wording.get('help.frame_colorizer_blend'), type = int, default = config.get_int_value('processors.frame_colorizer_blend', '100'), choices = processors_choices.frame_colorizer_blend_range, metavar = create_int_metavar(processors_choices.frame_colorizer_blend_range)) group_processors.add_argument('--frame-colorizer-size', help = wording.get('help.frame_colorizer_size'), type = str, default = config.get_str_value('processors.frame_colorizer_size', '256x256'), choices = processors_choices.frame_colorizer_sizes) + group_processors.add_argument('--frame-colorizer-blend', help = wording.get('help.frame_colorizer_blend'), type = int, default = config.get_int_value('processors.frame_colorizer_blend', '100'), choices = processors_choices.frame_colorizer_blend_range, metavar = create_int_metavar(processors_choices.frame_colorizer_blend_range)) facefusion.jobs.job_store.register_step_keys([ 'frame_colorizer_model', 'frame_colorizer_blend', 'frame_colorizer_size' ]) diff --git a/facefusion/processors/typing.py b/facefusion/processors/typing.py index ae57587e..b1ef4c84 100644 --- a/facefusion/processors/typing.py +++ b/facefusion/processors/typing.py @@ -90,24 +90,41 @@ ProcessorStateKey = Literal\ 'face_swapper_model', 'face_swapper_pixel_boost', 'frame_colorizer_model', - 'frame_colorizer_blend', 'frame_colorizer_size', + 'frame_colorizer_blend', 'frame_enhancer_model', 'frame_enhancer_blend', 'lip_syncer_model' ] ProcessorState = TypedDict('ProcessorState', { - 'age_modifier_model': AgeModifierModel, - 'age_modifier_direction': int, + 'age_modifier_model' : AgeModifierModel, + 'age_modifier_direction' : int, + 'expression_restorer_model' : ExpressionRestorerModel, + 'expression_restorer_factor' : int, 'face_debugger_items' : List[FaceDebuggerItem], + 'face_editor_model' : FaceEditorModel, + 'face_editor_eyebrow_direction' : float, + 'face_editor_eye_gaze_horizontal' : float, + 'face_editor_eye_gaze_vertical' : float, + 'face_editor_eye_open_ratio' : float, + 'face_editor_lip_open_ratio' : float, + 'face_editor_mouth_grim' : float, + 'face_editor_mouth_pout' : float, + 'face_editor_mouth_purse' : float, + 'face_editor_mouth_smile' : float, + 'face_editor_mouth_position_horizontal' : float, + 'face_editor_mouth_position_vertical' : float, + 'face_editor_head_pitch' : float, + 'face_editor_head_yaw' : float, + 'face_editor_head_roll' : float, 'face_enhancer_model' : FaceEnhancerModel, 'face_enhancer_blend' : int, 'face_swapper_model' : FaceSwapperModel, 'face_swapper_pixel_boost' : str, 'frame_colorizer_model' : FrameColorizerModel, - 'frame_colorizer_blend' : int, 'frame_colorizer_size' : str, + 'frame_colorizer_blend' : int, 'frame_enhancer_model' : FrameEnhancerModel, 'frame_enhancer_blend' : int, 'lip_syncer_model' : LipSyncerModel diff --git a/facefusion/program.py b/facefusion/program.py index 5e0a3761..314cb794 100755 --- a/facefusion/program.py +++ b/facefusion/program.py @@ -7,7 +7,7 @@ from facefusion.execution import get_execution_provider_choices from facefusion.filesystem import list_directory from facefusion.jobs import job_store from facefusion.processors.core import get_processors_modules -from facefusion.program_helper import suggest_face_detector_choices +from facefusion.program_helper import remove_args, suggest_face_detector_choices def create_help_formatter_small(prog : str) -> HelpFormatter: @@ -20,7 +20,8 @@ def create_help_formatter_large(prog : str) -> HelpFormatter: def create_config_program() -> ArgumentParser: program = ArgumentParser(add_help = False) - program.add_argument('-c', '--config-path', help = wording.get('help.config_path'), default = 'facefusion.ini') + group_paths = program.add_argument_group('paths') + group_paths.add_argument('-c', '--config-path', help = wording.get('help.config_path'), default = 'facefusion.ini') job_store.register_job_keys([ 'config-path' ]) apply_config_path(program) return program @@ -28,16 +29,18 @@ def create_config_program() -> ArgumentParser: def create_jobs_path_program() -> ArgumentParser: program = ArgumentParser(add_help = False) - program.add_argument('-j', '--jobs-path', help = wording.get('help.jobs_path'), default = config.get_str_value('paths.jobs_path', '.jobs')) + group_paths = program.add_argument_group('paths') + group_paths.add_argument('-j', '--jobs-path', help = wording.get('help.jobs_path'), default = config.get_str_value('paths.jobs_path', '.jobs')) job_store.register_job_keys([ 'jobs_path' ]) return program def create_paths_program() -> ArgumentParser: program = ArgumentParser(add_help = False) - program.add_argument('-s', '--source-paths', help = wording.get('help.source_paths'), action = 'append', default = config.get_str_list('paths.source_paths')) - program.add_argument('-t', '--target-path', help = wording.get('help.target_path'), default = config.get_str_value('paths.target_path')) - program.add_argument('-o', '--output-path', help = wording.get('help.output_path'), default = config.get_str_value('paths.output_path')) + group_paths = program.add_argument_group('paths') + group_paths.add_argument('-s', '--source-paths', help = wording.get('help.source_paths'), action = 'append', default = config.get_str_list('paths.source_paths')) + group_paths.add_argument('-t', '--target-path', help = wording.get('help.target_path'), default = config.get_str_value('paths.target_path')) + group_paths.add_argument('-o', '--output-path', help = wording.get('help.output_path'), default = config.get_str_value('paths.output_path')) job_store.register_step_keys([ 'source_paths', 'target_path', 'output_path' ]) return program @@ -67,10 +70,10 @@ def create_face_selector_program() -> ArgumentParser: group_face_selector = program.add_argument_group('face selector') group_face_selector.add_argument('--face-selector-mode', help = wording.get('help.face_selector_mode'), default = config.get_str_value('face_selector.face_selector_mode', 'reference'), choices = facefusion.choices.face_selector_modes) group_face_selector.add_argument('--face-selector-order', help = wording.get('help.face_selector_order'), default = config.get_str_value('face_selector.face_selector_order', 'large-small'), choices = facefusion.choices.face_selector_orders) - group_face_selector.add_argument('--face-selector-gender', help = wording.get('help.face_selector_gender'), default = config.get_str_value('face_selector.face_selector_gender'), choices = facefusion.choices.face_selector_genders) - group_face_selector.add_argument('--face-selector-race', help = wording.get('help.face_selector_race'), default = config.get_str_value('face_selector.face_selector_race'), choices = facefusion.choices.face_selector_races) group_face_selector.add_argument('--face-selector-age-start', help = wording.get('help.face_selector_age_start'), type = int, default = config.get_int_value('face_selector.face_selector_age_start'), choices = facefusion.choices.face_selector_age_range, metavar = create_int_metavar(facefusion.choices.face_selector_age_range)) group_face_selector.add_argument('--face-selector-age-end', help = wording.get('help.face_selector_age_end'), type = int, default = config.get_int_value('face_selector.face_selector_age_end'), choices = facefusion.choices.face_selector_age_range, metavar = create_int_metavar(facefusion.choices.face_selector_age_range)) + group_face_selector.add_argument('--face-selector-gender', help = wording.get('help.face_selector_gender'), default = config.get_str_value('face_selector.face_selector_gender'), choices = facefusion.choices.face_selector_genders) + group_face_selector.add_argument('--face-selector-race', help = wording.get('help.face_selector_race'), default = config.get_str_value('face_selector.face_selector_race'), choices = facefusion.choices.face_selector_races) group_face_selector.add_argument('--reference-face-position', help = wording.get('help.reference_face_position'), type = int, default = config.get_int_value('face_selector.reference_face_position', '0')) group_face_selector.add_argument('--reference-face-distance', help = wording.get('help.reference_face_distance'), type = float, default = config.get_float_value('face_selector.reference_face_distance', '0.6'), choices = facefusion.choices.reference_face_distance_range, metavar = create_float_metavar(facefusion.choices.reference_face_distance_range)) group_face_selector.add_argument('--reference-frame-number', help = wording.get('help.reference_frame_number'), type = int, default = config.get_int_value('face_selector.reference_frame_number', '0')) @@ -211,16 +214,16 @@ def create_program() -> ArgumentParser: sub_program.add_parser('headless-run', help = wording.get('help.headless_run'), parents = [ collect_step_program(), collect_job_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('force-download', help = wording.get('help.force_download'), parents = [ create_log_level_program() ], formatter_class = create_help_formatter_large) # job manager + sub_program.add_parser('job-list', help = wording.get('help.job_list'), parents = [ create_job_status_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('job-create', help = wording.get('help.job_create'), parents = [ create_job_id_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('job-submit', help = wording.get('help.job_submit'), parents = [ create_job_id_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('job-submit-all', help = wording.get('help.job_submit_all'), parents = [ create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('job-delete', help = wording.get('help.job_delete'), parents = [ create_job_id_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('job-delete-all', help = wording.get('help.job_delete_all'), parents = [ create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) - sub_program.add_parser('job-list', help = wording.get('help.job_list'), parents = [ create_job_status_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('job-add-step', help = wording.get('help.job_add_step'), parents = [ create_job_id_program(), collect_step_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) - sub_program.add_parser('job-remix-step', help = wording.get('help.job_remix_step'), parents = [ create_job_id_program(), create_step_index_program(), collect_step_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) + sub_program.add_parser('job-remix-step', help = wording.get('help.job_remix_step'), parents = [ create_job_id_program(), create_step_index_program(), remove_args(collect_step_program(), [ 'target_path' ]), create_log_level_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('job-insert-step', help = wording.get('help.job_insert_step'), parents = [ create_job_id_program(), create_step_index_program(), collect_step_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) - sub_program.add_parser('job-remove-step', help = wording.get('help.job_remove_step'), parents = [ create_job_id_program(), create_step_index_program(), collect_step_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) + sub_program.add_parser('job-remove-step', help = wording.get('help.job_remove_step'), parents = [ create_job_id_program(), create_step_index_program(), create_jobs_path_program(), create_log_level_program() ], formatter_class = create_help_formatter_large) # job runner sub_program.add_parser('job-run', help = wording.get('help.job_run'), parents = [ create_job_id_program(), create_config_program(), create_jobs_path_program(), collect_job_program() ], formatter_class = create_help_formatter_large) sub_program.add_parser('job-run-all', help = wording.get('help.job_run_all'), parents = [ create_config_program(), create_jobs_path_program(), collect_job_program() ], formatter_class = create_help_formatter_large) diff --git a/facefusion/program_helper.py b/facefusion/program_helper.py index 28648fb2..fb7738de 100644 --- a/facefusion/program_helper.py +++ b/facefusion/program_helper.py @@ -13,15 +13,14 @@ def find_argument_group(program : ArgumentParser, group_name : str) -> Optional[ def validate_args(program : ArgumentParser) -> bool: - if not validate_actions(program): - return False - - for action in program._actions: - if isinstance(action, _SubParsersAction): - for _, sub_program in action._name_parser_map.items(): - if not validate_args(sub_program): - return False - return True + if validate_actions(program): + for action in program._actions: + if isinstance(action, _SubParsersAction): + for _, sub_program in action._name_parser_map.items(): + if not validate_args(sub_program): + return False + return True + return False def validate_actions(program : ArgumentParser) -> bool: @@ -35,6 +34,14 @@ def validate_actions(program : ArgumentParser) -> bool: return True +def remove_args(program : ArgumentParser, remove_names : List[str]) -> ArgumentParser: + actions = [ action for action in program._actions if action.dest in remove_names ] + + for action in actions: + program._actions.remove(action) + return program + + def suggest_face_detector_choices(program : ArgumentParser) -> List[str]: known_args, _ = program.parse_known_args() return facefusion.choices.face_detector_set.get(known_args.face_detector_model) #type:ignore[call-overload] diff --git a/facefusion/typing.py b/facefusion/typing.py index f8ee1b54..5ce43ad5 100755 --- a/facefusion/typing.py +++ b/facefusion/typing.py @@ -47,7 +47,7 @@ FaceSet = Dict[str, List[Face]] FaceStore = TypedDict('FaceStore', { 'static_faces' : FaceSet, - 'reference_faces': FaceSet + 'reference_faces' : FaceSet }) VisionFrame = NDArray[Any] @@ -109,7 +109,7 @@ OutputVideoEncoder = Literal['libx264', 'libx265', 'libvpx-vp9', 'h264_nvenc', ' OutputVideoPreset = Literal['ultrafast', 'superfast', 'veryfast', 'faster', 'fast', 'medium', 'slow', 'slower', 'veryslow'] Download = TypedDict('Download', - { +{ 'url' : str, 'path' : str }) @@ -259,7 +259,7 @@ State = TypedDict('State', 'face_landmarker_score' : Score, 'face_selector_mode' : FaceSelectorMode, 'face_selector_order' : FaceSelectorOrder, - 'face_selector_race': Race, + 'face_selector_race' : Race, 'face_selector_gender' : Gender, 'face_selector_age_start' : int, 'face_selector_age_end' : int, @@ -287,16 +287,16 @@ State = TypedDict('State', 'open_browser' : bool, 'ui_layouts' : List[str], 'ui_workflow' : UiWorkflow, - 'execution_device_id': str, - 'execution_providers': List[ExecutionProviderKey], - 'execution_thread_count': int, - 'execution_queue_count': int, - 'video_memory_strategy': VideoMemoryStrategy, - 'system_memory_limit': int, - 'skip_download': bool, - 'log_level': LogLevel, - 'job_id': str, - 'job_status': JobStatus, - 'step_index': int + 'execution_device_id' : str, + 'execution_providers' : List[ExecutionProviderKey], + 'execution_thread_count' : int, + 'execution_queue_count' : int, + 'video_memory_strategy' : VideoMemoryStrategy, + 'system_memory_limit' : int, + 'skip_download' : bool, + 'log_level' : LogLevel, + 'job_id' : str, + 'job_status' : JobStatus, + 'step_index' : int }) StateSet = Dict[AppContext, State] diff --git a/facefusion/uis/components/benchmark.py b/facefusion/uis/components/benchmark.py index 9fbdbd81..7e30ffe3 100644 --- a/facefusion/uis/components/benchmark.py +++ b/facefusion/uis/components/benchmark.py @@ -82,6 +82,7 @@ def start(benchmark_runs : List[str], benchmark_cycles : int) -> Generator[List[ state_manager.init_item('face_landmarker_score', 0) state_manager.init_item('temp_frame_format', 'bmp') state_manager.init_item('output_video_preset', 'ultrafast') + state_manager.init_item('skip_audio', True) state_manager.sync_item('execution_providers') state_manager.sync_item('execution_thread_count') state_manager.sync_item('execution_queue_count') diff --git a/facefusion/uis/components/frame_colorizer_options.py b/facefusion/uis/components/frame_colorizer_options.py index f038392b..350d3e2e 100755 --- a/facefusion/uis/components/frame_colorizer_options.py +++ b/facefusion/uis/components/frame_colorizer_options.py @@ -10,14 +10,14 @@ from facefusion.processors.typing import FrameColorizerModel from facefusion.uis.core import get_ui_component, register_ui_component FRAME_COLORIZER_MODEL_DROPDOWN : Optional[gradio.Dropdown] = None -FRAME_COLORIZER_BLEND_SLIDER : Optional[gradio.Slider] = None FRAME_COLORIZER_SIZE_DROPDOWN : Optional[gradio.Dropdown] = None +FRAME_COLORIZER_BLEND_SLIDER : Optional[gradio.Slider] = None def render() -> None: global FRAME_COLORIZER_MODEL_DROPDOWN - global FRAME_COLORIZER_BLEND_SLIDER global FRAME_COLORIZER_SIZE_DROPDOWN + global FRAME_COLORIZER_BLEND_SLIDER FRAME_COLORIZER_MODEL_DROPDOWN = gradio.Dropdown( label = wording.get('uis.frame_colorizer_model_dropdown'), @@ -25,6 +25,12 @@ def render() -> None: value = state_manager.get_item('frame_colorizer_model'), visible = 'frame_colorizer' in state_manager.get_item('processors') ) + FRAME_COLORIZER_SIZE_DROPDOWN = gradio.Dropdown( + label = wording.get('uis.frame_colorizer_size_dropdown'), + choices = processors_choices.frame_colorizer_sizes, + value = state_manager.get_item('frame_colorizer_size'), + visible = 'frame_colorizer' in state_manager.get_item('processors') + ) FRAME_COLORIZER_BLEND_SLIDER = gradio.Slider( label = wording.get('uis.frame_colorizer_blend_slider'), value = state_manager.get_item('frame_colorizer_blend'), @@ -33,21 +39,15 @@ def render() -> None: maximum = processors_choices.frame_colorizer_blend_range[-1], visible = 'frame_colorizer' in state_manager.get_item('processors') ) - FRAME_COLORIZER_SIZE_DROPDOWN = gradio.Dropdown( - label = wording.get('uis.frame_colorizer_size_dropdown'), - choices = processors_choices.frame_colorizer_sizes, - value = state_manager.get_item('frame_colorizer_size'), - visible = 'frame_colorizer' in state_manager.get_item('processors') - ) register_ui_component('frame_colorizer_model_dropdown', FRAME_COLORIZER_MODEL_DROPDOWN) - register_ui_component('frame_colorizer_blend_slider', FRAME_COLORIZER_BLEND_SLIDER) register_ui_component('frame_colorizer_size_dropdown', FRAME_COLORIZER_SIZE_DROPDOWN) + register_ui_component('frame_colorizer_blend_slider', FRAME_COLORIZER_BLEND_SLIDER) def listen() -> None: FRAME_COLORIZER_MODEL_DROPDOWN.change(update_frame_colorizer_model, inputs = FRAME_COLORIZER_MODEL_DROPDOWN, outputs = FRAME_COLORIZER_MODEL_DROPDOWN) - FRAME_COLORIZER_BLEND_SLIDER.release(update_frame_colorizer_blend, inputs = FRAME_COLORIZER_BLEND_SLIDER) FRAME_COLORIZER_SIZE_DROPDOWN.change(update_frame_colorizer_size, inputs = FRAME_COLORIZER_SIZE_DROPDOWN) + FRAME_COLORIZER_BLEND_SLIDER.release(update_frame_colorizer_blend, inputs = FRAME_COLORIZER_BLEND_SLIDER) processors_checkbox_group = get_ui_component('processors_checkbox_group') if processors_checkbox_group: @@ -69,9 +69,12 @@ def update_frame_colorizer_model(frame_colorizer_model : FrameColorizerModel) -> return gradio.Dropdown() +def update_frame_colorizer_size(frame_colorizer_size : str) -> None: + state_manager.set_item('frame_colorizer_size', frame_colorizer_size) + + def update_frame_colorizer_blend(frame_colorizer_blend : float) -> None: state_manager.set_item('frame_colorizer_blend', int(frame_colorizer_blend)) -def update_frame_colorizer_size(frame_colorizer_size : str) -> None: - state_manager.set_item('frame_colorizer_size', frame_colorizer_size) + diff --git a/facefusion/uis/components/source.py b/facefusion/uis/components/source.py index 4f9c6f24..4b7d5720 100644 --- a/facefusion/uis/components/source.py +++ b/facefusion/uis/components/source.py @@ -21,13 +21,13 @@ def render() -> None: has_source_audio = has_audio(state_manager.get_item('source_paths')) has_source_image = has_image(state_manager.get_item('source_paths')) SOURCE_FILE = gradio.File( + label = wording.get('uis.source_file'), file_count = 'multiple', file_types = [ 'audio', 'image' ], - label = wording.get('uis.source_file'), value = state_manager.get_item('source_paths') if has_source_audio or has_source_image else None ) source_file_names = [ source_file_value.get('path') for source_file_value in SOURCE_FILE.value ] if SOURCE_FILE.value else None diff --git a/facefusion/uis/core.py b/facefusion/uis/core.py index e3b338f4..60f7b236 100644 --- a/facefusion/uis/core.py +++ b/facefusion/uis/core.py @@ -25,7 +25,6 @@ UI_LAYOUT_MODULES : List[ModuleType] = [] UI_LAYOUT_METHODS =\ [ 'pre_check', - 'pre_render', 'render', 'listen', 'run' @@ -83,14 +82,14 @@ def launch() -> None: with gradio.Blocks(theme = get_theme(), css = get_css(), title = metadata.get('name') + ' ' + metadata.get('version'), fill_width = True) as ui: for ui_layout in state_manager.get_item('ui_layouts'): ui_layout_module = load_ui_layout_module(ui_layout) - if ui_layout_module.pre_render(): - if ui_layouts_total > 1: - with gradio.Tab(ui_layout): - ui_layout_module.render() - ui_layout_module.listen() - else: + + if ui_layouts_total > 1: + with gradio.Tab(ui_layout): ui_layout_module.render() ui_layout_module.listen() + else: + ui_layout_module.render() + ui_layout_module.listen() for ui_layout in state_manager.get_item('ui_layouts'): ui_layout_module = load_ui_layout_module(ui_layout) diff --git a/facefusion/uis/layouts/benchmark.py b/facefusion/uis/layouts/benchmark.py index 7f501b81..72f75fb6 100644 --- a/facefusion/uis/layouts/benchmark.py +++ b/facefusion/uis/layouts/benchmark.py @@ -23,10 +23,6 @@ def pre_check() -> bool: return False -def pre_render() -> bool: - return True - - def render() -> gradio.Blocks: with gradio.Blocks() as layout: with gradio.Row(): diff --git a/facefusion/uis/layouts/default.py b/facefusion/uis/layouts/default.py index c5cf2d61..b57a9b8d 100755 --- a/facefusion/uis/layouts/default.py +++ b/facefusion/uis/layouts/default.py @@ -8,10 +8,6 @@ def pre_check() -> bool: return True -def pre_render() -> bool: - return True - - def render() -> gradio.Blocks: with gradio.Blocks() as layout: with gradio.Row(): diff --git a/facefusion/uis/layouts/jobs.py b/facefusion/uis/layouts/jobs.py index 740e31e8..ce38cc8f 100755 --- a/facefusion/uis/layouts/jobs.py +++ b/facefusion/uis/layouts/jobs.py @@ -8,10 +8,6 @@ def pre_check() -> bool: return True -def pre_render() -> bool: - return True - - def render() -> gradio.Blocks: with gradio.Blocks() as layout: with gradio.Row(): diff --git a/facefusion/uis/layouts/webcam.py b/facefusion/uis/layouts/webcam.py index 26f7f10f..2351d081 100644 --- a/facefusion/uis/layouts/webcam.py +++ b/facefusion/uis/layouts/webcam.py @@ -8,10 +8,6 @@ def pre_check() -> bool: return True -def pre_render() -> bool: - return True - - def render() -> gradio.Blocks: with gradio.Blocks() as layout: with gradio.Row(): diff --git a/facefusion/wording.py b/facefusion/wording.py index 3b754ba4..34760966 100755 --- a/facefusion/wording.py +++ b/facefusion/wording.py @@ -24,6 +24,8 @@ WORDING : Dict[str, Any] =\ 'merging_video_succeed': 'Merging video succeed', 'merging_video_failed': 'Merging video failed', 'skipping_audio': 'Skipping audio', + 'replacing_audio_succeed': 'Replacing audio succeed', + 'replacing_audio_skipped': 'Replacing audio skipped', 'restoring_audio_succeed': 'Restoring audio succeed', 'restoring_audio_skipped': 'Restoring audio skipped', 'clearing_temp': 'Clearing temporary resources', @@ -90,37 +92,38 @@ WORDING : Dict[str, Any] =\ # installer 'install_dependency': 'choose the variant of {dependency} to install', 'skip_conda': 'skip the conda environment check', - # general + # paths 'config_path': 'choose the config file to override defaults', + 'jobs_path': 'specify the directory to store jobs', 'source_paths': 'choose single or multiple source images or audios', 'target_path': 'choose single target image or video', 'output_path': 'specify the output image or video within a directory', - 'jobs_path': 'specify the directory to store jobs', - # face analyser + # face detector 'face_detector_model': 'choose the model responsible for detecting the faces', - 'face_detector_size': 'specify the size of the frame provided to the face detector', + 'face_detector_size': 'specify the frame size provided to the face detector', 'face_detector_angles': 'specify the angles to rotate the frame before detecting faces', 'face_detector_score': 'filter the detected faces base on the confidence score', + # face landmarker 'face_landmarker_model': 'choose the model responsible for detecting the face landmarks', 'face_landmarker_score': 'filter the detected face landmarks base on the confidence score', # face selector 'face_selector_mode': 'use reference based tracking or simple matching', 'face_selector_order': 'specify the order of the detected faces', + 'face_selector_age_start': 'filter the detected faces based the starting age', + 'face_selector_age_end': 'filter the detected faces based the ending age', 'face_selector_gender': 'filter the detected faces based on their gender', - 'face_selector_age_start': 'filter the detected faces based on their age start', - 'face_selector_age_end': 'filter the detected faces based on their age end', 'face_selector_race': 'filter the detected faces based on their race', 'reference_face_position': 'specify the position used to create the reference face', - 'reference_face_distance': 'specify the desired similarity between the reference face and target face', + 'reference_face_distance': 'specify the similarity between the reference face and target face', 'reference_frame_number': 'specify the frame used to create the reference face', - # face mask + # face masker 'face_mask_types': 'mix and match different face mask types (choices: {choices})', 'face_mask_blur': 'specify the degree of blur applied the box mask', 'face_mask_padding': 'apply top, right, bottom and left padding to the box mask', 'face_mask_regions': 'choose the facial features used for the region mask (choices: {choices})', # frame extraction - 'trim_frame_start': 'specify the the start frame of the target video', - 'trim_frame_end': 'specify the the end frame of the target video', + 'trim_frame_start': 'specify the starting frame of the target video', + 'trim_frame_end': 'specify the ending frame of the target video', 'temp_frame_format': 'specify the temporary resources format', 'keep_temp': 'keep the temporary resources after processing', # output creation @@ -134,11 +137,11 @@ WORDING : Dict[str, Any] =\ 'output_video_fps': 'specify the video output fps based on the target video', 'skip_audio': 'omit the audio from the target video', # processors - 'processors': 'load a single or multiple processors. (choices: {choices}, ...)', + 'processors': 'load a single or multiple processors (choices: {choices}, ...)', 'age_modifier_model': 'choose the model responsible for aging the face', 'age_modifier_direction': 'specify the direction in which the age should be modified', 'expression_restorer_model': 'choose the model responsible for restoring the expression', - 'expression_restorer_factor': 'restore factor of expression from target face', + 'expression_restorer_factor': 'restore factor of expression from the target face', 'face_debugger_items': 'load a single or multiple processors (choices: {choices})', 'face_editor_model': 'choose the model responsible for editing the face', 'face_editor_eyebrow_direction': 'specify the eyebrow direction', @@ -146,22 +149,22 @@ WORDING : Dict[str, Any] =\ 'face_editor_eye_gaze_vertical': 'specify the vertical eye gaze', 'face_editor_eye_open_ratio': 'specify the ratio of eye opening', 'face_editor_lip_open_ratio': 'specify the ratio of lip opening', - 'face_editor_mouth_grim': 'specify the mouth grim amount', - 'face_editor_mouth_pout': 'specify the mouth pout amount', - 'face_editor_mouth_purse': 'specify the mouth purse amount', - 'face_editor_mouth_smile': 'specify the mouth smile amount', - 'face_editor_mouth_position_horizontal': 'specify the mouth position horizontal amount', - 'face_editor_mouth_position_vertical': 'specify the mouth position vertical amount', - 'face_editor_head_pitch': 'specify the head pitch amount', - 'face_editor_head_yaw': 'specify the head yaw amount', - 'face_editor_head_roll': 'specify the head roll amount', + 'face_editor_mouth_grim': 'specify the mouth grim', + 'face_editor_mouth_pout': 'specify the mouth pout', + 'face_editor_mouth_purse': 'specify the mouth purse', + 'face_editor_mouth_smile': 'specify the mouth smile', + 'face_editor_mouth_position_horizontal': 'specify the horizontal mouth position', + 'face_editor_mouth_position_vertical': 'specify the vertical mouth position', + 'face_editor_head_pitch': 'specify the head pitch', + 'face_editor_head_yaw': 'specify the head yaw', + 'face_editor_head_roll': 'specify the head roll', 'face_enhancer_model': 'choose the model responsible for enhancing the face', 'face_enhancer_blend': 'blend the enhanced into the previous face', 'face_swapper_model': 'choose the model responsible for swapping the face', 'face_swapper_pixel_boost': 'choose the pixel boost resolution for the face swapper', 'frame_colorizer_model': 'choose the model responsible for colorizing the frame', + 'frame_colorizer_size': 'specify the frame size provided to the frame colorizer', 'frame_colorizer_blend': 'blend the colorized into the previous frame', - 'frame_colorizer_size': 'specify the size of the frame provided to the frame colorizer', 'frame_enhancer_model': 'choose the model responsible for enhancing the frame', 'frame_enhancer_blend': 'blend the enhanced into the previous frame', 'lip_syncer_model': 'choose the model responsible for syncing the lips', @@ -186,14 +189,15 @@ WORDING : Dict[str, Any] =\ 'force_download': 'force automate downloads and exit', # jobs 'job_id': 'specify the job id', + 'job_status': 'specify the job status', 'step_index': 'specify the step index', # job manager + 'job_list': 'list jobs by status', 'job_create': 'create a drafted job', 'job_submit': 'submit a drafted job to become a queued job', 'job_submit_all': 'submit all drafted jobs to become a queued jobs', 'job_delete': 'delete a drafted, queued, failed or completed job', 'job_delete_all': 'delete all drafted, queued, failed and completed jobs', - 'job_list': 'list jobs by status', 'job_add_step': 'add a step to a drafted job', 'job_remix_step': 'remix a previous step from a drafted job', 'job_insert_step': 'insert a step to a drafted job', diff --git a/tests/test_cli_job_manager.py b/tests/test_cli_job_manager.py index 7abddcc5..316488b0 100644 --- a/tests/test_cli_job_manager.py +++ b/tests/test_cli_job_manager.py @@ -24,6 +24,11 @@ def before_each() -> None: init_jobs(get_test_jobs_directory()) +@pytest.mark.skip() +def test_job_list() -> None: + pass + + def test_job_create() -> None: commands = [ sys.executable, 'facefusion.py', 'job-create', 'test-job-create', '-j', get_test_jobs_directory() ] diff --git a/tests/test_execution.py b/tests/test_execution.py index 1823f1e8..790b7408 100644 --- a/tests/test_execution.py +++ b/tests/test_execution.py @@ -11,7 +11,7 @@ def test_has_execution_provider() -> None: def test_multiple_execution_providers() -> None: - execution_provider_with_options =\ + execution_providers =\ [ ('CUDAExecutionProvider', { @@ -21,4 +21,4 @@ def test_multiple_execution_providers() -> None: 'CPUExecutionProvider' ] - assert create_execution_providers('1', [ 'cpu', 'cuda' ]) == execution_provider_with_options + assert create_execution_providers('1', [ 'cpu', 'cuda' ]) == execution_providers diff --git a/tests/test_program_helper.py b/tests/test_program_helper.py index 92b64fb2..f547fbc6 100644 --- a/tests/test_program_helper.py +++ b/tests/test_program_helper.py @@ -2,7 +2,7 @@ from argparse import ArgumentParser import pytest -from facefusion.program_helper import find_argument_group, validate_actions +from facefusion.program_helper import find_argument_group, remove_args, validate_actions def test_find_argument_group() -> None: @@ -38,3 +38,23 @@ def test_validate_actions() -> None: action.default = args[action.dest] assert validate_actions(program) is False + + +def test_remove_args() -> None: + program = ArgumentParser() + program.add_argument('--test-1') + program.add_argument('--test-2') + program.add_argument('--test-3') + + actions = [ action.dest for action in program._actions ] + + assert 'test_1' in actions + assert 'test_2' in actions + assert 'test_3' in actions + + program = remove_args(program, [ 'test_1', 'test_2' ]) + actions = [ action.dest for action in program._actions ] + + assert 'test_1' not in actions + assert 'test_2' not in actions + assert 'test_3' in actions