diff --git a/.gitignore b/.gitignore index ca46faeb..240f5253 100644 --- a/.gitignore +++ b/.gitignore @@ -171,4 +171,7 @@ config.backup.yaml # runtime runtime/ dev/ -installer_files/ \ No newline at end of file +installer_files/ + +core/capcut_api/ +.vscode/ \ No newline at end of file diff --git a/config.yaml b/config.yaml index 099b4bdb..5fe998a5 100644 --- a/config.yaml +++ b/config.yaml @@ -22,7 +22,7 @@ max_workers: 4 target_language: '简体中文' # Whether to use Demucs for vocal separation before transcription -demucs: true +demucs: false whisper: # ["large-v3", "large-v3-turbo"]. Note: for zh model will force to use Belle/large-v3 @@ -179,3 +179,53 @@ language_split_with_space: language_split_without_space: - 'zh' - 'ja' + +# CapCut/剪映 settings +capcut: + installed: false + trans_draft_id: '' + dub_draft_id: '' + # 草稿目录路径 + draft_folder: '' + # 启用导出剪映功能 + enable_export: false + editor: "CapCut" + + + # 字幕位置设置 + # 原始文本设置 + orig_text: + font: '' # 字体 + font_size: 8 # 字号 + color: '#FFFFFF' # 颜色 + bold: false # 粗体 + italic: false # 斜体 + underline: false # 下划线 + stroke: true # 是否启用描边 + stroke_color: '#000000' # 描边颜色 + y_offset: -0.8 # Y方向位置移动,范围[-2,2],相对于原始视频的高度 + stroke_width: 40 # 描边粗细 + # 翻译文本设置 + trans_text: + font: '' # 字体 + font_size: 8 # 字号 + color: '#FFFFFF' # 颜色 + bold: false # 粗体 + italic: false # 斜体 + underline: false # 下划线 + stroke: true # 是否启用描边 + stroke_color: '#000000' # 描边颜色 + stroke_width: 40 # 描边粗细 + y_offset: -0.6 # Y方向位置移动,范围[-2,2],相对于原始视频的高度 + # 配音文本设置 + dub_text: + font: '' # 字体 + font_size: 8 # 字号 + color: '#FFFFFF' # 颜色 + bold: false # 粗体 + italic: false # 斜体 + underline: false # 下划线 + stroke: true # 是否启用描边 + stroke_color: '#000000' # 描边颜色 + stroke_width: 40 # 描边粗细 + y_offset: -0.4 # Y方向位置移动,范围[-2,2],相对于原始视频的高度 diff --git a/core/_12_dub_to_vid.py b/core/_12_dub_to_vid.py index da7b2895..e6f7c0e4 100644 --- a/core/_12_dub_to_vid.py +++ b/core/_12_dub_to_vid.py @@ -1,14 +1,20 @@ import platform import subprocess +import os +import shutil import cv2 import numpy as np +import requests from rich.console import Console from core._1_ytdlp import find_video_files from core.asr_backend.audio_preprocess import normalize_audio_volume from core.utils import * from core.utils.models import * +from core.capcut_process.request_capcut_api import create_draft, add_video_impl, add_audio_track, add_subtitle, save_draft + +from core._11_merge_audio import load_and_flatten_data, get_audio_files console = Console() @@ -28,10 +34,109 @@ TRANS_OUTLINE_WIDTH = 1 TRANS_BACK_COLOR = '&H33000000' +def create_capcut_draft(video_file, TARGET_WIDTH, TARGET_HEIGHT): + # 创建一个空白草稿 + draft_folder = "/Users/sunguannan/Movies/JianyingPro/User Data/Projects/com.lveditor.draft" + draft_data = create_draft(TARGET_WIDTH, TARGET_HEIGHT) + print(draft_data) + + # 获取草稿id + draft_id = draft_data["output"]["draft_id"] + + # 保存draft_id到配置文件,以便其他文件使用 + update_key("capcut.dub_draft_id", draft_id) + + # 添加原始视频 + add_video_response = add_video_impl( + video_url=os.path.abspath(video_file), + draft_id=draft_id + ) + print(add_video_response) + + # 获取音频文件列表和时间信息 + df, lines, new_sub_times = load_and_flatten_data(_8_1_AUDIO_TASK) + audios = get_audio_files(df) + + # 添加每个音频片段到草稿中 + for i, (audio_file, time_range) in enumerate(zip(audios, new_sub_times)): + if not os.path.exists(audio_file): + print(f"警告:音频文件 {audio_file} 不存在,跳过...") + continue + + start_time, end_time = time_range + duration = end_time - start_time + + # 添加音频到草稿 + add_audio_response = add_audio_track( + audio_url=os.path.abspath(audio_file), + start=0, # 音频文件的起始时间 + end=duration, # 音频文件的结束时间 + target_start=start_time, # 设置音频在时间线上的起始位置 + volume=1.0, # 设置音量 + track_name="dub_tracks", # 为每个音频片段创建单独的轨道 + draft_id=draft_id + ) + print(f"添加音频 {audio_file} 结果:", add_audio_response) + + # 添加dub字幕 + # 从配置文件中获取配音文本设置 + dub_font = load_key("capcut.dub_text.font") + dub_font_size = load_key("capcut.dub_text.font_size") + dub_color = load_key("capcut.dub_text.color") + dub_stroke = load_key("capcut.dub_text.stroke") + dub_stroke_color = load_key("capcut.dub_text.stroke_color") + dub_stroke_width = load_key("capcut.dub_text.stroke_width") + dub_y_offset = load_key("capcut.dub_text.y_offset") + + add_srt_response = add_subtitle( + srt=os.path.abspath(DUB_SUB_FILE), + track_name="src_srt", + draft_id=draft_id, + font=dub_font, + font_size=dub_font_size, + font_color=dub_color, + border_color=dub_stroke_color if dub_stroke else None, + border_width=dub_stroke_width if dub_stroke else 0, + transform_y=dub_y_offset + ) + print(add_srt_response) + + # 保存草稿 + save_response = save_draft(draft_id, draft_folder) + print("保存草稿结果:", save_response) + + # 复制草稿文件夹到指定目录 + source_draft_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "core/capcut_api", draft_id) + target_draft_dir = os.path.join(draft_folder, draft_id) + + if os.path.exists(source_draft_dir): + # 如果目标目录已存在,先删除 + if os.path.exists(target_draft_dir): + print(f"目标文件夹 {target_draft_dir} 已存在,正在删除...") + shutil.rmtree(target_draft_dir) + + # 复制文件夹 + print(f"正在将草稿文件夹从 {source_draft_dir} 复制到 {target_draft_dir}...") + shutil.move(source_draft_dir, target_draft_dir) + print(f"草稿文件夹复制完成") + else: + print(f"警告:源草稿文件夹 {source_draft_dir} 不存在") + + return draft_id + def merge_video_audio(): """Merge video and audio, and reduce video volume""" VIDEO_FILE = find_video_files() background_file = _BACKGROUND_AUDIO_FILE + + video = cv2.VideoCapture(VIDEO_FILE) + TARGET_WIDTH = int(video.get(cv2.CAP_PROP_FRAME_WIDTH)) + TARGET_HEIGHT = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)) + video.release() + + # 创建剪映草稿 + if load_key("capcut.enable_export"): + create_capcut_draft(VIDEO_FILE, TARGET_WIDTH, TARGET_HEIGHT) if not load_key("burn_subtitles"): rprint("[bold yellow]Warning: A 0-second black video will be generated as a placeholder as subtitles are not burned in.[/bold yellow]") @@ -51,10 +156,6 @@ def merge_video_audio(): normalize_audio_volume(DUB_AUDIO, normalized_dub_audio) # Merge video and audio with translated subtitles - video = cv2.VideoCapture(VIDEO_FILE) - TARGET_WIDTH = int(video.get(cv2.CAP_PROP_FRAME_WIDTH)) - TARGET_HEIGHT = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)) - video.release() rprint(f"[bold green]Video resolution: {TARGET_WIDTH}x{TARGET_HEIGHT}[/bold green]") subtitle_filter = ( diff --git a/core/_7_sub_into_vid.py b/core/_7_sub_into_vid.py index 7a2e253f..9353f0f8 100644 --- a/core/_7_sub_into_vid.py +++ b/core/_7_sub_into_vid.py @@ -4,6 +4,10 @@ import numpy as np import platform from core.utils import * +import requests +import shutil +from core.capcut_process.request_capcut_api import create_draft, add_video_impl, add_subtitle, save_draft + SRC_FONT_SIZE = 15 TRANS_FONT_SIZE = 17 @@ -40,10 +44,96 @@ def check_gpu_available(): except: return False +def create_capcut_draft(video_file, TARGET_WIDTH, TARGET_HEIGHT): + # 从配置文件读取草稿目录路径 + draft_folder = load_key("capcut.draft_folder") + response = create_draft(TARGET_WIDTH, TARGET_HEIGHT) + print(f"create_draft:{response}") + + # 获取草稿id + draft_data = response + draft_id = draft_data["output"]["draft_id"] + + # 保存draft_id到配置文件,以便其他文件使用 + update_key("capcut.trans_draft_id", draft_id) + + # 添加原始视频 + add_video_response = add_video_impl( + video_url=os.path.abspath(video_file), + draft_id=draft_id + ) + print(f"add_video_response:{add_video_response}") + + # 从配置文件读取原始文本设置 + orig_text_config = load_key("capcut.orig_text") + + # 添加原始字幕 + add_srt_response = add_subtitle( + srt=os.path.abspath(SRC_SRT), + track_name="src_srt", + draft_id=draft_id, + font = orig_text_config["font"], + font_size=orig_text_config["font_size"], + font_color=orig_text_config["color"], + border_color=orig_text_config["stroke_color"] if orig_text_config["stroke"] else None, + border_width=orig_text_config["stroke_width"] if orig_text_config["stroke"] else 0, + transform_y=orig_text_config["y_offset"] + ) + print(add_srt_response) + + # 从配置文件读取翻译文本设置 + trans_text_config = load_key("capcut.trans_text") + + # 添加翻译字幕 + add_srt_response = add_subtitle( + srt=os.path.abspath(TRANS_SRT), + track_name="trans_srt", + draft_id=draft_id, + font = trans_text_config["font"], + font_size=trans_text_config["font_size"], + font_color=trans_text_config["color"], + border_color=trans_text_config["stroke_color"] if trans_text_config["stroke"] else None, + border_width=trans_text_config["stroke_width"] if trans_text_config["stroke"] else 0, + transform_y=trans_text_config["y_offset"] + ) + print(add_srt_response) + + # 保存草稿 + save_response = save_draft(draft_id, draft_folder) + print("保存草稿结果:", save_response) + + # 复制草稿文件夹到指定目录 + source_draft_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "core/capcut_api", draft_id) + target_draft_dir = os.path.join(draft_folder, draft_id) + + if os.path.exists(source_draft_dir): + # 如果目标目录已存在,先删除 + if os.path.exists(target_draft_dir): + print(f"目标文件夹 {target_draft_dir} 已存在,正在删除...") + shutil.rmtree(target_draft_dir) + + # 复制文件夹 + print(f"正在将草稿文件夹从 {source_draft_dir} 复制到 {target_draft_dir}...") + shutil.move(source_draft_dir, target_draft_dir) + print(f"草稿文件夹复制完成") + else: + print(f"警告:源草稿文件夹 {source_draft_dir} 不存在") + + return draft_id + def merge_subtitles_to_video(): video_file = find_video_files() os.makedirs(os.path.dirname(OUTPUT_VIDEO), exist_ok=True) + video = cv2.VideoCapture(video_file) + TARGET_WIDTH = int(video.get(cv2.CAP_PROP_FRAME_WIDTH)) + TARGET_HEIGHT = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)) + video.release() + + # 创建剪映草稿 + if load_key("capcut.enable_export"): + create_capcut_draft(video_file, TARGET_WIDTH, TARGET_HEIGHT) + # Check resolution if not load_key("burn_subtitles"): rprint("[bold yellow]Warning: A 0-second black video will be generated as a placeholder as subtitles are not burned in.[/bold yellow]") @@ -62,10 +152,6 @@ def merge_subtitles_to_video(): rprint("Subtitle files not found in the 'output' directory.") exit(1) - video = cv2.VideoCapture(video_file) - TARGET_WIDTH = int(video.get(cv2.CAP_PROP_FRAME_WIDTH)) - TARGET_HEIGHT = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)) - video.release() rprint(f"[bold green]Video resolution: {TARGET_WIDTH}x{TARGET_HEIGHT}[/bold green]") ffmpeg_cmd = [ 'ffmpeg', '-i', video_file, diff --git a/core/__init__.py b/core/__init__.py index d42b9c33..b15e0ff3 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -20,6 +20,7 @@ from .utils import * from .utils.onekeycleanup import cleanup from .utils.delete_retry_dubbing import delete_dubbing_files + from . import capcut_api except ImportError: pass diff --git a/core/capcut_process/capcut_server_manager.py b/core/capcut_process/capcut_server_manager.py new file mode 100644 index 00000000..033e1916 --- /dev/null +++ b/core/capcut_process/capcut_server_manager.py @@ -0,0 +1,66 @@ +import subprocess +import sys +import os +import signal +import atexit + +# 全局变量,用于跟踪服务器进程 +capcut_server_process = None + +# 启动剪映服务器的函数 +def start_capcut_server(): + global capcut_server_process + + # 如果服务器已经在运行,则不需要再次启动 + if capcut_server_process and capcut_server_process.poll() is None: + return + + try: + # 获取capcut_server.py的绝对路径 + server_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "capcut_api", "capcut_server.py") + + # 启动服务器进程 + process = subprocess.Popen([sys.executable, server_path], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + # 保存进程引用 + capcut_server_process = process + + except Exception as e: + print(f"Error starting capcut server: {e}") + +# 停止剪映服务器的函数 +def stop_capcut_server(): + global capcut_server_process + + # 如果进程存在且仍在运行,则终止进程 + if capcut_server_process and capcut_server_process.poll() is None: + try: + capcut_server_process.terminate() + try: + capcut_server_process.wait(timeout=5) + except subprocess.TimeoutExpired: + capcut_server_process.kill() + except Exception as e: + print(f"Error stopping capcut server: {e}") + +# 注册退出处理函数,确保在应用退出时停止服务器 +def cleanup_capcut_server(): + stop_capcut_server() + +atexit.register(cleanup_capcut_server) + +# 处理信号,确保在接收到终止信号时停止服务器 +def signal_handler(sig, frame): + cleanup_capcut_server() + sys.exit(0) + +# 注册信号处理器 +try: + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) +except ValueError: + # 如果在非主线程中执行,忽略错误 + pass \ No newline at end of file diff --git a/core/capcut_process/request_capcut_api.py b/core/capcut_process/request_capcut_api.py new file mode 100644 index 00000000..df369b52 --- /dev/null +++ b/core/capcut_process/request_capcut_api.py @@ -0,0 +1,375 @@ +import requests +import json +import sys +import time +import functools +import threading +import os + +from core.utils.config_utils import load_key + +# 添加项目根目录到Python路径 +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) + +# 尝试导入,如果未安装则忽略导入错误 +try: + if load_key("capcut.installed"): + from core.capcut_api.settings.local import PORT + # Base URL of the service, please modify according to actual situation + BASE_URL = f"http://localhost:{PORT}" + else: + PORT = None + BASE_URL = None +except ImportError: + PORT = None + BASE_URL = None + +# 装饰器,检查是否安装了CapCutAPI +def require_capcut_api(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + if not load_key("capcut.installed") or BASE_URL is None: + print(f"CapCutAPI not installed. Function '{func.__name__}' cannot be used.") + return {"success": False, "error": "CapCutAPI not installed"} + return func(*args, **kwargs) + return wrapper + +@require_capcut_api +def make_request(endpoint, data, method='POST'): + """Send HTTP request to the server and handle the response""" + url = f"{BASE_URL}/{endpoint}" + headers = {'Content-Type': 'application/json'} + + try: + if method == 'POST': + response = requests.post(url, data=json.dumps(data), headers=headers) + elif method == 'GET': + response = requests.get(url, params=data, headers=headers) + else: + raise ValueError(f"Unsupported HTTP method: {method}") + + response.raise_for_status() # Raise an exception if the request fails + return response.json() + except requests.exceptions.RequestException as e: + print(f"Request error: {e}") + except json.JSONDecodeError: + print("Unable to parse server response") + +@require_capcut_api +def get_font_types(): + """Get the list of available font types from the CapCut server""" + try: + return make_request("get_font_types", {}, method='GET') + except Exception as e: + print(f"Error getting font types: {e}") + return {"success": False, "output": [], "error": str(e)} + +@require_capcut_api +def add_audio_track(audio_url, start, end, target_start, volume=1.0, + speed=1.0, track_name="main_audio", effect_type=None, effect_params=None, draft_id=None): + """API call to add audio track""" + data = { + "audio_url": audio_url, + "start": start, + "end": end, + "target_start": target_start, + "volume": volume, + "speed": speed, + "track_name": track_name, + "effect_type": effect_type, + "effect_params": effect_params + } + + if draft_id: + data["draft_id"] = draft_id + + return make_request("add_audio", data) + +@require_capcut_api +def add_text_impl(text, start, end, font, font_color, font_size, track_name,draft_folder="123", draft_id=None, + vertical=False, transform_x=0.5, transform_y=0.5, font_alpha=1.0, + border_color=None, border_width=0.0, border_alpha=1.0, + background_color=None, background_alpha=1.0, background_style=None, + bubble_effect_id=None, bubble_resource_id=None, + effect_effect_id=None, outro_animation=None): + """API call to add text""" + data = { + "draft_folder": draft_folder, + "text": text, + "start": start, + "end": end, + "font": font, + "color": font_color, + "size": font_size, + "alpha": font_alpha, + "track_name": track_name, + "vertical": vertical, + "transform_x": transform_x, + "transform_y": transform_y + } + + # Add border parameters + if border_color: + data["border_color"] = border_color + data["border_width"] = border_width + data["border_alpha"] = border_alpha + + # Add background parameters + if background_color: + data["background_color"] = background_color + data["background_alpha"] = background_alpha + if background_style: + data["background_style"] = background_style + + # Add bubble effect parameters + if bubble_effect_id: + data["bubble_effect_id"] = bubble_effect_id + if bubble_resource_id: + data["bubble_resource_id"] = bubble_resource_id + + # Add text effect parameters + if effect_effect_id: + data["effect_effect_id"] = effect_effect_id + + if draft_id: + data["draft_id"] = draft_id + + if outro_animation: + data["outro_animation"] = outro_animation + + return make_request("add_text", data) + +@require_capcut_api +def add_image_impl(image_url, width, height, start, end, track_name, draft_id=None, + transform_x=0, transform_y=0, scale_x=1.0, scale_y=1.0, transition=None, transition_duration=None, + # New mask-related parameters + mask_type=None, mask_center_x=0.0, mask_center_y=0.0, mask_size=0.5, + mask_rotation=0.0, mask_feather=0.0, mask_invert=False, + mask_rect_width=None, mask_round_corner=None): + """API call to add image""" + data = { + "image_url": image_url, + "width": width, + "height": height, + "start": start, + "end": end, + "track_name": track_name, + "transform_x": transform_x, + "transform_y": transform_y, + "scale_x": scale_x, + "scale_y": scale_y, + "transition": transition, + "transition_duration": transition_duration or 0.5, # Default transition duration is 0.5 seconds + # Add mask-related parameters + "mask_type": mask_type, + "mask_center_x": mask_center_x, + "mask_center_y": mask_center_y, + "mask_size": mask_size, + "mask_rotation": mask_rotation, + "mask_feather": mask_feather, + "mask_invert": mask_invert, + "mask_rect_width": mask_rect_width, + "mask_round_corner": mask_round_corner + } + + if draft_id: + data["draft_id"] = draft_id + + return make_request("add_image", data) + +@require_capcut_api +def generate_image_impl(prompt, width, height, start, end, track_name, draft_id=None, + transform_x=0, transform_y=0, scale_x=1.0, scale_y=1.0, transition=None, transition_duration=None): + """API call to add image""" + data = { + "prompt": prompt, + "width": width, + "height": height, + "start": start, + "end": end, + "track_name": track_name, + "transform_x": transform_x, + "transform_y": transform_y, + "scale_x": scale_x, + "scale_y": scale_y, + "transition": transition, + "transition_duration": transition_duration or 0.5 # Default transition duration is 0.5 seconds + } + + if draft_id: + data["draft_id"] = draft_id + + return make_request("generate_image", data) + +@require_capcut_api +def add_sticker_impl(resource_id, start, end, draft_id=None, transform_x=0, transform_y=0, + alpha=1.0, flip_horizontal=False, flip_vertical=False, rotation=0.0, + scale_x=1.0, scale_y=1.0, track_name="sticker_main", relative_index=0, + width=1080, height=1920): + """API call to add sticker""" + data = { + "sticker_id": resource_id, + "start": start, + "end": end, + "transform_x": transform_x, + "transform_y": transform_y, + "alpha": alpha, + "flip_horizontal": flip_horizontal, + "flip_vertical": flip_vertical, + "rotation": rotation, + "scale_x": scale_x, + "scale_y": scale_y, + "track_name": track_name, + "relative_index": relative_index, + "width": width, + "height": height + } + + if draft_id: + data["draft_id"] = draft_id + + return make_request("add_sticker", data) + +@require_capcut_api +def add_video_keyframe_impl(draft_id, track_name, property_type=None, time=None, value=None, + property_types=None, times=None, values=None): + """API call to add video keyframe + + Supports two modes: + 1. Single keyframe: using property_type, time, value parameters + 2. Batch keyframes: using property_types, times, values parameters (in list form) + """ + data = { + "draft_id": draft_id, + "track_name": track_name + } + + # Add single keyframe parameters (if provided) + if property_type is not None: + data["property_type"] = property_type + if time is not None: + data["time"] = time + if value is not None: + data["value"] = value + + # Add batch keyframe parameters (if provided) + if property_types is not None: + data["property_types"] = property_types + if times is not None: + data["times"] = times + if values is not None: + data["values"] = values + + return make_request("add_video_keyframe", data) + +@require_capcut_api +def add_video_impl(video_url, start=None, end=None, width=None, height=None, track_name="main", + draft_id=None, transform_y=0, scale_x=1, scale_y=1, transform_x=0, + speed=1.0, target_start=0, relative_index=0, transition=None, transition_duration=None, + # Mask-related parameters + mask_type=None, mask_center_x=0.5, mask_center_y=0.5, mask_size=1.0, + mask_rotation=0.0, mask_feather=0.0, mask_invert=False, + mask_rect_width=None, mask_round_corner=None): + """API call to add video track""" + data = { + "video_url": video_url, + "height": height, + "track_name": track_name, + "transform_y": transform_y, + "scale_x": scale_x, + "scale_y": scale_y, + "transform_x": transform_x, + "speed": speed, + "target_start": target_start, + "relative_index": relative_index, + "transition": transition, + "transition_duration": transition_duration or 0.5, # Default transition duration is 0.5 seconds + # Mask-related parameters + "mask_type": mask_type, + "mask_center_x": mask_center_x, + "mask_center_y": mask_center_y, + "mask_size": mask_size, + "mask_rotation": mask_rotation, + "mask_feather": mask_feather, + "mask_invert": mask_invert, + "mask_rect_width": mask_rect_width, + "mask_round_corner": mask_round_corner + } + if draft_id: + data['draft_id'] = draft_id + if start: + data["start"] = start + if end: + data["end"] = end + if width: + data["width"] = width + if height: + data["height"] = height + return make_request("add_video", data) + +@require_capcut_api +def add_effect(effect_type, start, end, draft_id=None, track_name="effect_01", + params=None, width=1080, height=1920): + """API call to add effect""" + data = { + "effect_type": effect_type, + "start": start, + "end": end, + "track_name": track_name, + "params": params or [], + "width": width, + "height": height + } + + if draft_id: + data["draft_id"] = draft_id + + return make_request("add_effect", data) + +@require_capcut_api +def create_draft(width, height, draft_id=None): + """API call to create a new draft""" + data = { + "width": width, + "height": height + } + + if draft_id: + data["draft_id"] = draft_id + + return make_request("create_draft", data) + +@require_capcut_api +def add_subtitle(srt, track_name="subtitle_main", draft_id=None, font_size=8, + font = None, + font_color="#FFFFFF", border_color=None, border_width=0, transform_y=0): + """API call to add subtitle from SRT file""" + data = { + "srt": srt, + "track_name": track_name, + "font_size": font_size, + "font_color": font_color, + "transform_y": transform_y + } + + if font: + data["font"] = font + + if border_color: + data["border_color"] = border_color + data["border_width"] = border_width + + if draft_id: + data["draft_id"] = draft_id + + return make_request("add_subtitle", data) + +@require_capcut_api +def save_draft(draft_id, draft_folder): + """API call to save draft to specified folder""" + data = { + "draft_id": draft_id, + "draft_folder": draft_folder + } + + return make_request("save_draft", data) diff --git a/core/st_utils/sidebar_setting.py b/core/st_utils/sidebar_setting.py index 5c5d24aa..9d01b29f 100644 --- a/core/st_utils/sidebar_setting.py +++ b/core/st_utils/sidebar_setting.py @@ -2,6 +2,7 @@ from translations.translations import translate as t from translations.translations import DISPLAY_LANGUAGES from core.utils import * +import requests def config_input(label, key, help=None): """Generic config input handler""" @@ -151,7 +152,395 @@ def page_setting(): elif select_tts == "f5tts": config_input("302ai API", "f5tts.302_api") + + with st.expander(t("CapCut Settings"), expanded=True): + # 显示CapCutAPI信息 + st.markdown( + t("This feature is powered by [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), an open-source API for automating CapCut draft creation and editing."), + unsafe_allow_html=True + ) + + # 添加额外的空间 + st.markdown("
", unsafe_allow_html=True) + + # 检查是否已安装CapCutAPI + is_installed = load_key("capcut.installed") if "installed" in load_key("capcut") else False + if not is_installed: + # 显示安装按钮 + st.info(t("CapCutAPI is not installed. Install it to export translated videos to CapCut.")) + + # 询问使用剪映还是剪映国际版 + editor_options = ["CapCut", "剪映"] + editor_choice = st.radio(t("Which editor do you use?"), options=editor_options, index=0) + + if st.button(t("Install CapCutAPI"), type="primary"): + # 创建一个容器来显示安装日志 + log_container = st.empty() + log_container.info(t("Installing CapCutAPI...")) + + try: + # 检查目录是否存在,如果存在则先删除 + import shutil, subprocess, sys, json, os + if os.path.exists("core/capcut_api"): + shutil.rmtree("core/capcut_api") + log_container.info(t("Removed existing CapCutAPI directory.")) + + # 克隆CapCutAPI仓库 + log_container.info(t("Cloning CapCutAPI repository...")) + subprocess.check_call(["git", "clone", "--depth=1", "https://github.com/sun-guannan/CapCutAPI.git", "core/capcut_api"]) + + + # 安装依赖 + log_container.info(t("Installing dependencies...")) + subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", "core/capcut_api/requirements.txt"]) + + # 复制配置文件 + if not os.path.exists("core/capcut_api/config.json"): + shutil.copy("core/capcut_api/config.json.example", "core/capcut_api/config.json") + log_container.info(t("Created configuration file.")) + + # 根据选择修改配置文件 + with open("core/capcut_api/config.json", "r", encoding="utf-8") as f: + config = json.load(f) + + # 设置是否为国际版 + config["is_capcut_env"] = editor_choice == "CapCut" + + # 保存修改后的配置 + with open("core/capcut_api/config.json", "w", encoding="utf-8") as f: + json.dump(config, f, indent=2, ensure_ascii=False) + + # 更新VideoLingo配置 + # update_key("capcut.editor", editor_choice) + update_key("capcut.installed", True) + + # 提示用户安装成功 + log_container.success(t("✅ CapCutAPI installed successfully!")) + st.info(t("Note: CapCutAPI will use port 9001 by default. If you need to change the port, please edit core/capcut_api/config.json")) + + # 重新加载页面以显示剪映设置 + st.rerun() + + except Exception as e: + log_container.error(f"Installation failed: {str(e)}") + st.error(t("Failed to install CapCutAPI. Please try again or install manually.")) + else: + # 已安装,显示正常的剪映设置 + from core.capcut_process.request_capcut_api import get_font_types + from core.capcut_process.capcut_server_manager import start_capcut_server, stop_capcut_server, cleanup_capcut_server + # 1. 剪映草稿目录设置 + draft_folder = st.text_input( + t("CapCut Draft Folder"), + value=load_key("capcut.draft_folder") if "draft_folder" in load_key("capcut") else "", + help=t("The directory path where CapCut drafts are saved, get it from Global Settings->Draft Location, e.g.: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft"), + placeholder=t("Please enter CapCut draft directory path") + ) + if draft_folder != load_key("capcut.draft_folder") if "draft_folder" in load_key("capcut") else "": + update_key("capcut.draft_folder", draft_folder) + + # 添加是否启用导出剪映功能的开关 + enable_capcut = st.toggle( + t("Enable CapCut Export"), + value=load_key("capcut.enable_export"), + disabled=not draft_folder, # 当剪映草稿目录为空时禁用 + help=t("When enabled, CapCut drafts will be automatically generated") + ) + + # 更新配置并启动/停止服务器 + if enable_capcut != load_key("capcut.enable_export"): + update_key("capcut.enable_export", enable_capcut) + st.rerun() + + # 根据开关状态启动或停止服务器 + if enable_capcut: + pass + start_capcut_server() + else: + stop_capcut_server() + + # 定义字体选项(所有tab共用) + font_options = [] + if enable_capcut: + try: + # 使用函数获取字体列表 + data = get_font_types() + if data["success"] and data["output"]: + font_options = [item["name"] for item in data["output"]] + else: + # 如果API返回成功但没有数据,使用默认字体列表 + font_options = [] + except Exception as e: + # 如果发生异常,延迟3秒后重试 + try: + # 延迟3秒 + import time + time.sleep(3) + # 重新尝试获取字体列表 + data = get_font_types() + if data["success"] and data["output"]: + font_options = [item["name"] for item in data["output"]] + else: + font_options = [] + except Exception as e2: + st.warning(t("Failed to get font types, try refreshing this page.")) + font_options = [] + + # 只有当启用导出剪映功能时,才显示文字字体设置 + if enable_capcut: + # 2. 原始文本字体设置 + # 文本设置部分 - 使用tabs横向排布三种文本设置 + st.subheader(t("Text Settings")) + + # 创建三个横向排布的tab + orig_tab, trans_tab, dub_tab = st.tabs([t("Original Text"), t("Translated Text"), t("Dubbed Text")]) + + # 1. 原始文本设置 + with orig_tab: + # 原始文本字体 + selected_orig_font = st.selectbox( + t("Font"), + options=font_options, + index=font_options.index(load_key("capcut.orig_text.font")) if load_key("capcut.orig_text.font") in font_options else 0, + key="orig_font" + ) + if selected_orig_font != load_key("capcut.orig_text.font"): + update_key("capcut.orig_text.font", selected_orig_font) + st.rerun() + + # 原始文本字体大小 + orig_font_size = st.slider( + t("Font Size"), + min_value=1, + max_value=100, + value=int(load_key("capcut.orig_text.font_size")) if load_key("capcut.orig_text.font_size") else 36, + key="orig_font_size" + ) + if orig_font_size != load_key("capcut.orig_text.font_size"): + update_key("capcut.orig_text.font_size", orig_font_size) + st.rerun() + + # 原始文本字体颜色 + orig_font_color = st.color_picker( + t("Font Color"), + value=load_key("capcut.orig_text.color") if load_key("capcut.orig_text.color") else "#FFCC00", + key="orig_font_color" + ) + if orig_font_color != load_key("capcut.orig_text.color"): + update_key("capcut.orig_text.color", orig_font_color) + st.rerun() + + # Y方向位置移动(原始文本独立控制) + orig_y_offset = st.slider( + t("Vertical Position Offset"), + min_value=-2.0, + max_value=2.0, + value=float(load_key("capcut.orig_text.y_offset")) if load_key("capcut.orig_text.y_offset") is not None else 0.0, + step=0.1, + key="orig_y_offset", + help=t("Positive values move upward, 1 represents one video height") + ) + if orig_y_offset != load_key("capcut.orig_text.y_offset"): + update_key("capcut.orig_text.y_offset", orig_y_offset) + st.rerun() + + # 描边设置 + orig_stroke = st.toggle( + t("Stroke"), + value=load_key("capcut.orig_text.stroke") if load_key("capcut.orig_text.stroke") is not None else True, + key="orig_stroke" + ) + if orig_stroke != load_key("capcut.orig_text.stroke"): + update_key("capcut.orig_text.stroke", orig_stroke) + st.rerun() + + if orig_stroke: + # 原始文本描边颜色 + orig_stroke_color = st.color_picker( + t("Stroke Color"), + value=load_key("capcut.orig_text.stroke_color") if load_key("capcut.orig_text.stroke_color") else "#000000", + key="orig_stroke_color" + ) + if orig_stroke_color != load_key("capcut.orig_text.stroke_color"): + update_key("capcut.orig_text.stroke_color", orig_stroke_color) + st.rerun() + + # 原始文本描边粗细 + orig_stroke_width = st.slider( + t("Stroke Width"), + min_value=0, + max_value=100, + value=int(load_key("capcut.orig_text.stroke_width")) if load_key("capcut.orig_text.stroke_width") else 30, + key="orig_stroke_width" + ) + if orig_stroke_width != load_key("capcut.orig_text.stroke_width"): + update_key("capcut.orig_text.stroke_width", orig_stroke_width) + st.rerun() + + # 2. 翻译文本设置 + with trans_tab: + # 翻译文本字体 + selected_trans_font = st.selectbox( + t("Font"), + options=font_options, + index=font_options.index(load_key("capcut.trans_text.font")) if load_key("capcut.trans_text.font") in font_options else 0, + key="trans_font" + ) + if selected_trans_font != load_key("capcut.trans_text.font"): + update_key("capcut.trans_text.font", selected_trans_font) + st.rerun() + + # 翻译文本字体大小 + trans_font_size = st.slider( + t("Font Size"), + min_value=1, + max_value=100, + value=int(load_key("capcut.trans_text.font_size")) if load_key("capcut.trans_text.font_size") else 36, + key="trans_font_size" + ) + if trans_font_size != load_key("capcut.trans_text.font_size"): + update_key("capcut.trans_text.font_size", trans_font_size) + st.rerun() + + # 翻译文本字体颜色 + trans_font_color = st.color_picker( + t("Font Color"), + value=load_key("capcut.trans_text.color") if load_key("capcut.trans_text.color") else "#FFFFFF", + key="trans_font_color" + ) + if trans_font_color != load_key("capcut.trans_text.color"): + update_key("capcut.trans_text.color", trans_font_color) + st.rerun() + + # Y方向位置移动(翻译文本独立控制) + trans_y_offset = st.slider( + t("Vertical Position Offset"), + min_value=-2.0, + max_value=2.0, + value=float(load_key("capcut.trans_text.y_offset")) if load_key("capcut.trans_text.y_offset") is not None else 0.0, + step=0.1, + key="trans_y_offset", + help=t("Positive values move upward, 1 represents one video height") + ) + if trans_y_offset != load_key("capcut.trans_text.y_offset"): + update_key("capcut.trans_text.y_offset", trans_y_offset) + st.rerun() + + # 描边设置 + trans_stroke = st.toggle( + t("Stroke"), + value=load_key("capcut.trans_text.stroke") if load_key("capcut.trans_text.stroke") is not None else True, + key="trans_stroke" + ) + if trans_stroke != load_key("capcut.trans_text.stroke"): + update_key("capcut.trans_text.stroke", trans_stroke) + st.rerun() + + if trans_stroke: + # 翻译文本描边颜色 + trans_stroke_color = st.color_picker( + t("Stroke Color"), + value=load_key("capcut.trans_text.stroke_color") if load_key("capcut.trans_text.stroke_color") else "#000000", + key="trans_stroke_color" + ) + if trans_stroke_color != load_key("capcut.trans_text.stroke_color"): + update_key("capcut.trans_text.stroke_color", trans_stroke_color) + st.rerun() + + # 翻译文本描边粗细 + trans_stroke_width = st.slider( + t("Stroke Width"), + min_value=0, + max_value=100, + value=int(load_key("capcut.trans_text.stroke_width")) if load_key("capcut.trans_text.stroke_width") else 30, + key="trans_stroke_width" + ) + if trans_stroke_width != load_key("capcut.trans_text.stroke_width"): + update_key("capcut.trans_text.stroke_width", trans_stroke_width) + st.rerun() + + # 3. 配音文本设置 + with dub_tab: + # 配音文本字体 + selected_dub_font = st.selectbox( + t("Font"), + options=font_options, + index=font_options.index(load_key("capcut.dub_text.font")) if load_key("capcut.dub_text.font") in font_options else 0, + key="dub_font" + ) + if selected_dub_font != load_key("capcut.dub_text.font"): + update_key("capcut.dub_text.font", selected_dub_font) + st.rerun() + + # 配音文本字体大小 + dub_font_size = st.slider( + t("Font Size"), + min_value=1, + max_value=100, + value=int(load_key("capcut.dub_text.font_size")) if load_key("capcut.dub_text.font_size") else 8, + key="dub_font_size" + ) + if dub_font_size != load_key("capcut.dub_text.font_size"): + update_key("capcut.dub_text.font_size", dub_font_size) + st.rerun() + + # 配音文本字体颜色 + dub_font_color = st.color_picker( + t("Font Color"), + value=load_key("capcut.dub_text.color") if load_key("capcut.dub_text.color") else "#FFFFFF", + key="dub_font_color" + ) + if dub_font_color != load_key("capcut.dub_text.color"): + update_key("capcut.dub_text.color", dub_font_color) + st.rerun() + + # Y方向位置移动(配音文本独立控制) + dub_y_offset = st.slider( + t("Vertical Position Offset"), + min_value=-3.0, + max_value=3.0, + value=float(load_key("capcut.dub_text.y_offset")) if load_key("capcut.dub_text.y_offset") is not None else 0.0, + step=0.1, + key="dub_y_offset", + help=t("Positive values move upward, 1 represents one video height") + ) + if dub_y_offset != load_key("capcut.dub_text.y_offset"): + update_key("capcut.dub_text.y_offset", dub_y_offset) + st.rerun() + + # 描边设置 + dub_stroke = st.toggle( + t("Stroke"), + value=load_key("capcut.dub_text.stroke") if load_key("capcut.dub_text.stroke") is not None else True, + key="dub_stroke" + ) + if dub_stroke != load_key("capcut.dub_text.stroke"): + update_key("capcut.dub_text.stroke", dub_stroke) + st.rerun() + + if dub_stroke: + # 配音文本描边颜色 + dub_stroke_color = st.color_picker( + t("Stroke Color"), + value=load_key("capcut.dub_text.stroke_color") if load_key("capcut.dub_text.stroke_color") else "#000000", + key="dub_stroke_color" + ) + if dub_stroke_color != load_key("capcut.dub_text.stroke_color"): + update_key("capcut.dub_text.stroke_color", dub_stroke_color) + st.rerun() + + # 配音文本描边粗细 + dub_stroke_width = st.slider( + t("Stroke Width"), + min_value=0, + max_value=100, + value=int(load_key("capcut.dub_text.stroke_width")) if load_key("capcut.dub_text.stroke_width") else 40, + key="dub_stroke_width" + ) + if dub_stroke_width != load_key("capcut.dub_text.stroke_width"): + update_key("capcut.dub_text.stroke_width", dub_stroke_width) + st.rerun() + def check_api(): try: resp = ask_gpt("This is a test, response 'message':'success' in json format.", diff --git a/install.py b/install.py index 697f1752..82579402 100644 --- a/install.py +++ b/install.py @@ -1,6 +1,7 @@ import os, sys import platform import subprocess +import json sys.path.append(os.path.dirname(os.path.abspath(__file__))) ascii_logo = """ @@ -158,6 +159,69 @@ def install_noto_font(): install_requirements() check_ffmpeg() + # 询问是否安装CapCutAPI + @except_handler("Failed to install CapCutAPI") + def install_capcut_api(): + # 询问是否需要安装CapCutAPI + install_choice = inquirer.confirm( + message=t("Do you want to install CapCutAPI? This allows exporting translated videos to CapCut/JianYing."), + default=True + ).execute() + + if install_choice: + console.print(Panel(t("Installing CapCutAPI..."), style="cyan")) + + # 检查目录是否存在,如果存在则先删除 + import shutil + if os.path.exists("core/capcut_api"): + shutil.rmtree("core/capcut_api") + + # 克隆CapCutAPI仓库 + subprocess.check_call(["git", "clone", "--depth=1", "https://github.com/sun-guannan/CapCutAPI.git", "core/capcut_api"]) + + # 安装依赖 + subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", "core/capcut_api/requirements.txt"]) + + # 复制配置文件 + if not os.path.exists("core/capcut_api/config.json"): + shutil.copy("core/capcut_api/config.json.example", "core/capcut_api/config.json") + + # 询问使用剪映还是剪映国际版 + editor_choice = inquirer.select( + message=t("Which editor do you use?"), + choices=["CapCut", "JianYing (剪映中文版)"], + default="CapCut" + ).execute() + + # 根据选择修改配置文件 + with open("core/capcut_api/config.json", "r", encoding="utf-8") as f: + config = json.load(f) + + # 设置是否为国际版 + config["is_capcut_env"] = editor_choice == "CapCut (International)" + + # 保存修改后的配置 + with open("core/capcut_api/config.json", "w", encoding="utf-8") as f: + json.dump(config, f, indent=2, ensure_ascii=False) + + # 更新VideoLingo配置 + update_key("capcut.editor", "CapCut" if editor_choice == "CapCut (International)" else "JianYing") + + # 记录用户是否安装CapCutAPI的标记位 + update_key("capcut.installed", install_choice) + + # 提示用户安装成功 + console.print(Panel( + t("✅ CapCutAPI installed successfully!") + "\n\n" + + t("Note: CapCutAPI will use port 9001 by default.") + "\n" + + t("If you need to change the port, please edit core/capcut_api/config.json") + "\n" + + t("If you need to switch between CapCut and JianYing, you can modify the 'is_capcut_env' setting in the same file."), + style="green" + )) + + # 安装CapCutAPI + install_capcut_api() + # First panel with installation complete and startup command panel1_text = ( t("Installation completed") + "\n\n" + diff --git a/requirements.txt b/requirements.txt index d2f55474..9caebbb2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,3 +31,4 @@ pypinyin g2p-en xmltodict +httpx[socks] diff --git a/st.py b/st.py index 4d963f82..672ad705 100644 --- a/st.py +++ b/st.py @@ -40,6 +40,12 @@ def text_processing_section(): if st.button(t("Archive to 'history'"), key="cleanup_in_text_processing"): cleanup() st.rerun() + + # 添加剪映草稿提示 + if load_key("capcut.enable_export"): + draft_id = load_key("capcut.trans_draft_id") + st.info(t("You can find the generated draft in CapCut with name: ") + f"{draft_id}") + return True def process_text(): @@ -88,6 +94,12 @@ def audio_processing_section(): if st.button(t("Archive to 'history'"), key="cleanup_in_audio_processing"): cleanup() st.rerun() + + # 添加剪映草稿提示 + if load_key("capcut.enable_export"): + draft_id = load_key("capcut.dub_draft_id") + st.info(t("You can find the generated draft in CapCut with name: ") + f"{draft_id}") + def process_audio(): with st.spinner(t("Generate audio tasks")): diff --git a/translations/en.json b/translations/en.json index fb216bce..f9bce17c 100644 --- a/translations/en.json +++ b/translations/en.json @@ -106,5 +106,39 @@ "No NVIDIA GPU detected": "No NVIDIA GPU detected", "No NVIDIA GPU detected or NVIDIA drivers not properly installed": "No NVIDIA GPU detected or NVIDIA drivers not properly installed", "LLM JSON Format Support": "LLM JSON Format Support", - "Enable if your LLM supports JSON mode output": "Enable if your LLM supports JSON mode output" + "Enable if your LLM supports JSON mode output": "Enable if your LLM supports JSON mode output", + "CapCut Settings": "CapCut Settings", + "CapCut Draft Folder": "CapCut Draft Folder", + "The directory path where CapCut drafts are saved, get it from Global Settings->Draft Location, e.g.: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft": "The directory path where CapCut drafts are saved, get it from Global Settings->Draft Location, e.g.: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft", + "Please enter CapCut draft directory path": "Please enter CapCut draft directory path", + "Enable CapCut Export": "Enable CapCut Export", + "When enabled, CapCut drafts will be automatically generated": "When enabled, CapCut drafts will be automatically generated", + "Text Settings": "Text Settings", + "Original Text": "Original Text", + "Translated Text": "Translated Text", + "Dubbed Text": "Dubbed Text", + "Font": "Font", + "Font Size": "Font Size", + "Font Color": "Font Color", + "Vertical Position Offset": "Vertical Position Offset", + "Positive values move upward, 1 represents one video height": "Positive values move upward, 1 represents one video height", + "Stroke": "Stroke", + "Stroke Color": "Stroke Color", + "Stroke Width": "Stroke Width", + "CapCut server started successfully!": "CapCut server started successfully!", + "CapCut server stopped.": "CapCut server stopped.", + "This feature is powered by [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), an open-source API for automating CapCut draft creation and editing.": "This feature is powered by [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), an open-source API for automating CapCut draft creation and editing.", + "You can find the generated draft in CapCut with name: ": "You can find the generated draft in CapCut with name: ", + "Failed to get font types, try refreshing this page.": "Failed to get font types, try refreshing this page.", + "CapCutAPI is not installed. Install it to export translated videos to CapCut.": "CapCutAPI is not installed. Install it to export translated videos to CapCut.", + "Which editor do you use?": "Which editor do you use?", + "Install CapCutAPI": "Install CapCutAPI", + "Installing CapCutAPI...": "Installing CapCutAPI...", + "Removed existing CapCutAPI directory.": "Removed existing CapCutAPI directory.", + "Cloning CapCutAPI repository...": "Cloning CapCutAPI repository...", + "Installing dependencies...": "Installing dependencies...", + "Created configuration file.": "Created configuration file.", + "✅ CapCutAPI installed successfully!": "✅ CapCutAPI installed successfully!", + "Note: CapCutAPI will use port 9001 by default. If you need to change the port, please edit core/capcut_api/config.json": "Note: CapCutAPI will use port 9001 by default. If you need to change the port, please edit core/capcut_api/config.json", + "Failed to install CapCutAPI. Please try again or install manually.": "Failed to install CapCutAPI. Please try again or install manually." } diff --git a/translations/es.json b/translations/es.json index 15fadf5f..c685b3b4 100644 --- a/translations/es.json +++ b/translations/es.json @@ -106,5 +106,39 @@ "No NVIDIA GPU detected": "No se detectó GPU NVIDIA", "No NVIDIA GPU detected or NVIDIA drivers not properly installed": "No se detectó GPU NVIDIA o los controladores NVIDIA no están instalados correctamente", "LLM JSON Format Support": "Soporte de formato JSON para LLM", - "Enable if your LLM supports JSON mode output": "Activar si su LLM admite salida en modo JSON" + "Enable if your LLM supports JSON mode output": "Activar si su LLM admite salida en modo JSON", + "CapCut Settings": "Configuración de CapCut", + "CapCut Draft Folder": "Carpeta de borradores de CapCut", + "The directory path where CapCut drafts are saved, get it from Global Settings->Draft Location, e.g.: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft": "La ruta del directorio donde se guardan los borradores de CapCut, obténgala de Configuración global->Ubicación de borradores, por ejemplo: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft", + "Please enter CapCut draft directory path": "Por favor ingrese la ruta del directorio de borradores de CapCut", + "Enable CapCut Export": "Habilitar exportación a CapCut", + "When enabled, CapCut drafts will be automatically generated": "Cuando está habilitado, los borradores de CapCut se generarán automáticamente", + "Text Settings": "Configuración de texto", + "Original Text": "Texto original", + "Translated Text": "Texto traducido", + "Dubbed Text": "Texto doblado", + "Font": "Fuente", + "Font Size": "Tamaño de fuente", + "Font Color": "Color de fuente", + "Vertical Position Offset": "Desplazamiento de posición vertical", + "Positive values move upward, 1 represents one video height": "Los valores positivos se mueven hacia arriba, 1 representa una altura de video", + "Stroke": "Contorno", + "Stroke Color": "Color de contorno", + "Stroke Width": "Grosor de contorno", + "CapCut server started successfully!": "¡Servidor de CapCut iniciado con éxito!", + "CapCut server stopped.": "Servidor de CapCut detenido.", + "This feature is powered by [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), an open-source API for automating CapCut draft creation and editing.": "Esta función está impulsada por [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), una API de código abierto para automatizar la creación y edición de borradores de CapCut.", + "You can find the generated draft in CapCut with name: ": "Puede encontrar el borrador generado en CapCut con el nombre: ", + "Failed to get font types, try refreshing this page.": "Error al obtener tipos de fuente, intente actualizar esta página.", + "CapCutAPI is not installed. Install it to export translated videos to CapCut.": "CapCutAPI no está instalado. Instálelo para exportar videos traducidos a CapCut.", + "Which editor do you use?": "¿Qué editor utilizas?", + "Install CapCutAPI": "Instalar CapCutAPI", + "Installing CapCutAPI...": "Instalando CapCutAPI...", + "Removed existing CapCutAPI directory.": "Se eliminó el directorio CapCutAPI existente.", + "Cloning CapCutAPI repository...": "Clonando repositorio CapCutAPI...", + "Installing dependencies...": "Instalando dependencias...", + "Created configuration file.": "Archivo de configuración creado.", + "✅ CapCutAPI installed successfully!": "✅ ¡CapCutAPI instalado correctamente!", + "Note: CapCutAPI will use port 9001 by default. If you need to change the port, please edit core/capcut_api/config.json": "Nota: CapCutAPI utilizará el puerto 9001 por defecto. Si necesita cambiar el puerto, edite core/capcut_api/config.json", + "Failed to install CapCutAPI. Please try again or install manually.": "Error al instalar CapCutAPI. Inténtelo de nuevo o instálelo manualmente." } diff --git a/translations/fr.json b/translations/fr.json index 446f3bbe..b87e074c 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -106,5 +106,39 @@ "No NVIDIA GPU detected": "Aucun GPU NVIDIA détecté", "No NVIDIA GPU detected or NVIDIA drivers not properly installed": "Aucun GPU NVIDIA détecté ou pilotes NVIDIA mal installés", "LLM JSON Format Support": "Support du format JSON pour LLM", - "Enable if your LLM supports JSON mode output": "Activer si votre LLM prend en charge la sortie en mode JSON" + "Enable if your LLM supports JSON mode output": "Activer si votre LLM prend en charge la sortie en mode JSON", + "CapCut Settings": "Paramètres CapCut", + "CapCut Draft Folder": "Dossier des brouillons CapCut", + "The directory path where CapCut drafts are saved, get it from Global Settings->Draft Location, e.g.: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft": "Le chemin du répertoire où les brouillons CapCut sont enregistrés, obtenez-le depuis Paramètres globaux->Emplacement des brouillons, ex.: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft", + "Please enter CapCut draft directory path": "Veuillez entrer le chemin du répertoire des brouillons CapCut", + "Enable CapCut Export": "Activer l'exportation vers CapCut", + "When enabled, CapCut drafts will be automatically generated": "Lorsque activé, les brouillons CapCut seront générés automatiquement", + "Text Settings": "Paramètres de texte", + "Original Text": "Texte original", + "Translated Text": "Texte traduit", + "Dubbed Text": "Texte doublé", + "Font": "Police", + "Font Size": "Taille de police", + "Font Color": "Couleur de police", + "Vertical Position Offset": "Décalage de position verticale", + "Positive values move upward, 1 represents one video height": "Les valeurs positives déplacent vers le haut, 1 représente une hauteur de vidéo", + "Stroke": "Contour", + "Stroke Color": "Couleur du contour", + "Stroke Width": "Épaisseur du contour", + "CapCut server started successfully!": "Serveur CapCut démarré avec succès !", + "CapCut server stopped.": "Serveur CapCut arrêté.", + "This feature is powered by [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), an open-source API for automating CapCut draft creation and editing.": "Cette fonctionnalité est alimentée par [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), une API open-source pour automatiser la création et l'édition de brouillons CapCut.", + "You can find the generated draft in CapCut with name: ": "Vous pouvez trouver le brouillon généré dans CapCut avec le nom : ", + "Failed to get font types, try refreshing this page.": "Échec de l'obtention des types de police, essayez d'actualiser cette page.", + "CapCutAPI is not installed. Install it to export translated videos to CapCut.": "CapCutAPI n'est pas installé. Installez-le pour exporter des vidéos traduites vers CapCut.", + "Which editor do you use?": "Quel éditeur utilisez-vous ?", + "Install CapCutAPI": "Installer CapCutAPI", + "Installing CapCutAPI...": "Installation de CapCutAPI...", + "Removed existing CapCutAPI directory.": "Répertoire CapCutAPI existant supprimé.", + "Cloning CapCutAPI repository...": "Clonage du dépôt CapCutAPI...", + "Installing dependencies...": "Installation des dépendances...", + "Created configuration file.": "Fichier de configuration créé.", + "✅ CapCutAPI installed successfully!": "✅ CapCutAPI installé avec succès !", + "Note: CapCutAPI will use port 9001 by default. If you need to change the port, please edit core/capcut_api/config.json": "Remarque : CapCutAPI utilisera le port 9001 par défaut. Si vous devez changer de port, veuillez éditer core/capcut_api/config.json", + "Failed to install CapCutAPI. Please try again or install manually.": "Échec de l'installation de CapCutAPI. Veuillez réessayer ou installer manuellement." } diff --git a/translations/ja.json b/translations/ja.json index 1bce1783..41010382 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -106,5 +106,39 @@ "No NVIDIA GPU detected": "NVIDIA GPUが検出されません", "No NVIDIA GPU detected or NVIDIA drivers not properly installed": "NVIDIA GPUが検出されないか、NVIDIAドライバーが正しくインストールされていません", "LLM JSON Format Support": "LLM JSON形式サポート", - "Enable if your LLM supports JSON mode output": "LLMがJSON出力モードをサポートしている場合に有効化" + "Enable if your LLM supports JSON mode output": "LLMがJSON出力モードをサポートしている場合に有効化", + "CapCut Settings": "CapCut設定", + "CapCut Draft Folder": "CapCut下書きフォルダ", + "The directory path where CapCut drafts are saved, get it from Global Settings->Draft Location, e.g.: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft": "CapCut下書きが保存されるディレクトリパス、グローバル設定->下書き場所から取得、例:C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft", + "Please enter CapCut draft directory path": "CapCut下書きディレクトリパスを入力してください", + "Enable CapCut Export": "CapCutエクスポートを有効化", + "When enabled, CapCut drafts will be automatically generated": "有効にすると、CapCut下書きが自動的に生成されます", + "Text Settings": "テキスト設定", + "Original Text": "原文テキスト", + "Translated Text": "翻訳テキスト", + "Dubbed Text": "吹き替えテキスト", + "Font": "フォント", + "Font Size": "フォントサイズ", + "Font Color": "フォント色", + "Vertical Position Offset": "垂直位置オフセット", + "Positive values move upward, 1 represents one video height": "正の値は上方向に移動、1は動画の高さ1つ分を表します", + "Stroke": "縁取り", + "Stroke Color": "縁取り色", + "Stroke Width": "縁取り幅", + "CapCut server started successfully!": "CapCutサーバーが正常に起動しました!", + "CapCut server stopped.": "CapCutサーバーが停止しました。", + "This feature is powered by [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), an open-source API for automating CapCut draft creation and editing.": "この機能は[CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file)によって提供されています。これはCapCutドラフトの作成と編集を自動化するためのオープンソースAPIです。", + "You can find the generated draft in CapCut with name: ": "CapCutで生成された下書きは次の名前で見つけられます:", + "Failed to get font types, try refreshing this page.": "フォントタイプの取得に失敗しました。ページを更新してください。", + "CapCutAPI is not installed. Install it to export translated videos to CapCut.": "CapCutAPIがインストールされていません。翻訳されたビデオをCapCutにエクスポートするためにインストールしてください。", + "Which editor do you use?": "どのエディターを使用しますか?", + "Install CapCutAPI": "CapCutAPIをインストール", + "Installing CapCutAPI...": "CapCutAPIをインストール中...", + "Removed existing CapCutAPI directory.": "既存のCapCutAPIディレクトリを削除しました。", + "Cloning CapCutAPI repository...": "CapCutAPIリポジトリをクローン中...", + "Installing dependencies...": "依存関係をインストール中...", + "Created configuration file.": "設定ファイルを作成しました。", + "✅ CapCutAPI installed successfully!": "✅ CapCutAPIが正常にインストールされました!", + "Note: CapCutAPI will use port 9001 by default. If you need to change the port, please edit core/capcut_api/config.json": "注意:CapCutAPIはデフォルトでポート9001を使用します。ポートを変更する必要がある場合は、core/capcut_api/config.jsonを編集してください", + "Failed to install CapCutAPI. Please try again or install manually.": "CapCutAPIのインストールに失敗しました。もう一度試すか、手動でインストールしてください。" } diff --git a/translations/ru.json b/translations/ru.json index 07ba21cb..0e86864e 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -106,5 +106,39 @@ "No NVIDIA GPU detected": "GPU NVIDIA не обнаружен", "No NVIDIA GPU detected or NVIDIA drivers not properly installed": "GPU NVIDIA не обнаружен или драйверы NVIDIA установлены неправильно", "LLM JSON Format Support": "Поддержка формата JSON для LLM", - "Enable if your LLM supports JSON mode output": "Включите, если ваш LLM поддерживает вывод в формате JSON" + "Enable if your LLM supports JSON mode output": "Включите, если ваш LLM поддерживает вывод в формате JSON", + "CapCut Settings": "Настройки CapCut", + "CapCut Draft Folder": "Папка черновиков CapCut", + "The directory path where CapCut drafts are saved, get it from Global Settings->Draft Location, e.g.: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft": "Путь к директории, где сохраняются черновики CapCut, возьмите его из Глобальных настроек->Расположение черновиков, например: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft", + "Please enter CapCut draft directory path": "Пожалуйста, введите путь к директории черновиков CapCut", + "Enable CapCut Export": "Включить экспорт в CapCut", + "When enabled, CapCut drafts will be automatically generated": "При включении черновики CapCut будут создаваться автоматически", + "Text Settings": "Настройки текста", + "Original Text": "Исходный текст", + "Translated Text": "Переведенный текст", + "Dubbed Text": "Текст дубляжа", + "Font": "Шрифт", + "Font Size": "Размер шрифта", + "Font Color": "Цвет шрифта", + "Vertical Position Offset": "Смещение вертикальной позиции", + "Positive values move upward, 1 represents one video height": "Положительные значения смещают вверх, 1 представляет одну высоту видео", + "Stroke": "Обводка", + "Stroke Color": "Цвет обводки", + "Stroke Width": "Толщина обводки", + "CapCut server started successfully!": "Сервер CapCut успешно запущен!", + "CapCut server stopped.": "Сервер CapCut остановлен.", + "This feature is powered by [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), an open-source API for automating CapCut draft creation and editing.": "Эта функция работает на основе [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), API с открытым исходным кодом для автоматизации создания и редактирования черновиков CapCut.", + "You can find the generated draft in CapCut with name: ": "Вы можете найти созданный черновик в CapCut с именем: ", + "Failed to get font types, try refreshing this page.": "Не удалось получить типы шрифтов, попробуйте обновить эту страницу.", + "CapCutAPI is not installed. Install it to export translated videos to CapCut.": "CapCutAPI не установлен. Установите его для экспорта переведенных видео в CapCut.", + "Which editor do you use?": "Какой редактор вы используете?", + "Install CapCutAPI": "Установить CapCutAPI", + "Installing CapCutAPI...": "Установка CapCutAPI...", + "Removed existing CapCutAPI directory.": "Удалена существующая директория CapCutAPI.", + "Cloning CapCutAPI repository...": "Клонирование репозитория CapCutAPI...", + "Installing dependencies...": "Установка зависимостей...", + "Created configuration file.": "Создан файл конфигурации.", + "✅ CapCutAPI installed successfully!": "✅ CapCutAPI успешно установлен!", + "Note: CapCutAPI will use port 9001 by default. If you need to change the port, please edit core/capcut_api/config.json": "Примечание: CapCutAPI по умолчанию использует порт 9001. Если вам нужно изменить порт, отредактируйте core/capcut_api/config.json", + "Failed to install CapCutAPI. Please try again or install manually.": "Не удалось установить CapCutAPI. Попробуйте еще раз или установите вручную." } diff --git a/translations/zh-CN.json b/translations/zh-CN.json index 04596db6..d10bff32 100644 --- a/translations/zh-CN.json +++ b/translations/zh-CN.json @@ -106,6 +106,40 @@ "No NVIDIA GPU detected": "未检测到NVIDIA GPU", "No NVIDIA GPU detected or NVIDIA drivers not properly installed": "未检测到NVIDIA GPU或NVIDIA驱动未正确安装", "LLM JSON Format Support": "LLM JSON格式支持", - "Enable if your LLM supports JSON mode output": "如果选用的LLM支持JSON模式输出,请启用" + "Enable if your LLM supports JSON mode output": "如果选用的LLM支持JSON模式输出,请启用", + "CapCut Settings": "剪映设置", + "CapCut Draft Folder": "剪映草稿目录", + "The directory path where CapCut drafts are saved, get it from Global Settings->Draft Location, e.g.: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft": "剪映草稿保存的目录路径,可从全局设置->草稿位置获取,例如:C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft", + "Please enter CapCut draft directory path": "请输入剪映草稿目录路径", + "Enable CapCut Export": "启用导出剪映功能", + "When enabled, CapCut drafts will be automatically generated": "启用后,将自动生成剪映草稿", + "Text Settings": "文本设置", + "Original Text": "原始文本", + "Translated Text": "翻译文本", + "Dubbed Text": "配音文本", + "Font": "字体", + "Font Size": "字体大小", + "Font Color": "字体颜色", + "Vertical Position Offset": "垂直位置偏移", + "Positive values move upward, 1 represents one video height": "正值向上移动,1代表一个视频高度", + "Stroke": "描边", + "Stroke Color": "描边颜色", + "Stroke Width": "描边粗细", + "CapCut server started successfully!": "剪映服务器启动成功!", + "CapCut server stopped.": "剪映服务器已停止。", + "This feature is powered by [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), an open-source API for automating CapCut draft creation and editing.": "此功能由[CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file)提供支持,这是一个用于自动化剪映草稿创建和编辑的开源API。", + "You can find the generated draft in CapCut with name: ": "您可以在剪映中找到生成的草稿,名称为:", + "Failed to get font types, try refreshing this page.": "获取字体类型失败,请尝试刷新页面。", + "CapCutAPI is not installed. Install it to export translated videos to CapCut.": "CapCutAPI未安装。安装它以将翻译后的视频导出到剪映。", + "Which editor do you use?": "您使用哪个编辑器?", + "Install CapCutAPI": "安装CapCutAPI", + "Installing CapCutAPI...": "正在安装CapCutAPI...", + "Removed existing CapCutAPI directory.": "已删除现有的CapCutAPI目录。", + "Cloning CapCutAPI repository...": "正在克隆CapCutAPI仓库...", + "Installing dependencies...": "正在安装依赖项...", + "Created configuration file.": "已创建配置文件。", + "✅ CapCutAPI installed successfully!": "✅ CapCutAPI安装成功!", + "Note: CapCutAPI will use port 9001 by default. If you need to change the port, please edit core/capcut_api/config.json": "注意:CapCutAPI默认使用9001端口。如需更改端口,请编辑core/capcut_api/config.json", + "Failed to install CapCutAPI. Please try again or install manually.": "CapCutAPI安装失败。请重试或手动安装。" } diff --git a/translations/zh-HK.json b/translations/zh-HK.json index cedc73f6..ab38766c 100644 --- a/translations/zh-HK.json +++ b/translations/zh-HK.json @@ -106,5 +106,39 @@ "No NVIDIA GPU detected": "未檢測到NVIDIA GPU", "No NVIDIA GPU detected or NVIDIA drivers not properly installed": "未檢測到NVIDIA GPU或NVIDIA驅動未正確安裝", "LLM JSON Format Support": "LLM JSON格式支持", - "Enable if your LLM supports JSON mode output": "如果選用的LLM支持JSON模式輸出,請啟用" + "Enable if your LLM supports JSON mode output": "如果選用的LLM支持JSON模式輸出,請啟用", + "CapCut Settings": "剪映設置", + "CapCut Draft Folder": "剪映草稿目錄", + "The directory path where CapCut drafts are saved, get it from Global Settings->Draft Location, e.g.: C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft": "剪映草稿保存的目錄路徑,可從全局設置->草稿位置獲取,例如:C:\\Users\\Administrator\\AppData\\Local\\JianyingPro\\User Data\\Projects\\com.lveditor.draft", + "Please enter CapCut draft directory path": "請輸入剪映草稿目錄路徑", + "Enable CapCut Export": "啟用導出剪映功能", + "When enabled, CapCut drafts will be automatically generated": "啟用後,將自動生成剪映草稿", + "Text Settings": "文本設置", + "Original Text": "原始文本", + "Translated Text": "翻譯文本", + "Dubbed Text": "配音文本", + "Font": "字體", + "Font Size": "字體大小", + "Font Color": "字體顏色", + "Vertical Position Offset": "垂直位置偏移", + "Positive values move upward, 1 represents one video height": "正值向上移動,1代表一個視頻高度", + "Stroke": "描邊", + "Stroke Color": "描邊顏色", + "Stroke Width": "描邊粗細", + "CapCut server started successfully!": "剪映伺服器啟動成功!", + "CapCut server stopped.": "剪映伺服器已停止。", + "This feature is powered by [CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file), an open-source API for automating CapCut draft creation and editing.": "此功能由[CapCutAPI](https://github.com/sun-guannan/CapCutAPI?tab=readme-ov-file)提供支持,這是一個用於自動化剪映草稿創建和編輯的開源API。", + "You can find the generated draft in CapCut with name: ": "您可以在剪映中找到生成的草稿,名稱為:", + "Failed to get font types, try refreshing this page.": "獲取字體類型失敗,請嘗試刷新頁面。", + "CapCutAPI is not installed. Install it to export translated videos to CapCut.": "CapCutAPI未安裝。安裝它以將翻譯後的視頻導出到剪映。", + "Which editor do you use?": "您使用哪個編輯器?", + "Install CapCutAPI": "安裝CapCutAPI", + "Installing CapCutAPI...": "正在安裝CapCutAPI...", + "Removed existing CapCutAPI directory.": "已刪除現有的CapCutAPI目錄。", + "Cloning CapCutAPI repository...": "正在克隆CapCutAPI倉庫...", + "Installing dependencies...": "正在安裝依賴項...", + "Created configuration file.": "已創建配置文件。", + "✅ CapCutAPI installed successfully!": "✅ CapCutAPI安裝成功!", + "Note: CapCutAPI will use port 9001 by default. If you need to change the port, please edit core/capcut_api/config.json": "注意:CapCutAPI默認使用9001端口。如需更改端口,請編輯core/capcut_api/config.json", + "Failed to install CapCutAPI. Please try again or install manually.": "CapCutAPI安裝失敗。請重試或手動安裝。" }