Kamixitong/test.py

220 lines
8.3 KiB
Python
Raw Normal View History

2025-11-12 15:11:05 +08:00
import json
import zipfile
import os
import shutil
from typing import Optional, Dict, Any
class JianYingConverter:
def __init__(self, input_cmp: str, output_cmp: str, target_version: str = "4.0"):
"""
初始化剪映草稿转换器
:param input_cmp: 输入高版本.cmp草稿路径
:param output_cmp: 输出低版本.cmp草稿路径
:param target_version: 目标低版本支持 "3.0" / "4.0"
"""
self.input_cmp = input_cmp
self.output_cmp = output_cmp
self.target_version = target_version
self.temp_dir = "temp_jianying" # 临时解压目录
self.core_json = "project.json" # 核心配置文件
def _unzip_cmp(self) -> bool:
"""解压.cmp文件到临时目录"""
if not os.path.exists(self.input_cmp):
print(f"错误:输入文件不存在 → {self.input_cmp}")
return False
# 清空并创建临时目录
if os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
os.makedirs(self.temp_dir)
try:
with zipfile.ZipFile(self.input_cmp, 'r') as zip_ref:
zip_ref.extractall(self.temp_dir)
print(f"成功解压到临时目录 → {self.temp_dir}")
return True
except Exception as e:
print(f"解压失败:{str(e)}")
return False
def _load_project_json(self) -> Optional[Dict[str, Any]]:
"""读取project.json"""
json_path = os.path.join(self.temp_dir, self.core_json)
if not os.path.exists(json_path):
print(f"错误:未找到核心配置文件 → {json_path}")
return None
try:
with open(json_path, 'r', encoding='utf-8') as f:
data = json.load(f)
print("成功读取project.json")
return data
except json.JSONDecodeError:
# 部分高版本草稿可能有简单加密如Base64尝试解密
try:
import base64
with open(json_path, 'r', encoding='utf-8') as f:
encrypted = f.read()
decrypted = base64.b64decode(encrypted).decode('utf-8')
data = json.loads(decrypted)
print("成功解密并读取project.json")
return data
except Exception as e:
print(f"读取/解密JSON失败{str(e)}")
return None
def _downgrade_json(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""根据目标版本降级JSON结构"""
print(f"正在降级到剪映 v{self.target_version} 格式...")
# 1. 移除高版本新增的顶层字段(基于逆向分析)
high_version_fields = [
"meta_info", "plugin_version", "advanced_settings",
"ai_editor_info", "cloud_project_info", "multi_track_info"
]
for field in high_version_fields:
data.pop(field, None)
# 2. 降级项目配置(强制设置兼容版本号)
if "project_config" in data:
if self.target_version == "3.0":
data["project_config"]["version"] = "3.9.0"
data["project_config"]["compatible_version"] = "3.0.0"
else: # 4.0
data["project_config"]["version"] = "4.9.0"
data["project_config"]["compatible_version"] = "4.0.0"
# 3. 处理轨道数据(移除高版本特效/转场)
if "tracks" in data:
self._downgrade_tracks(data["tracks"])
# 4. 处理资源列表(移除云资源引用)
if "resources" in data:
data["resources"] = [res for res in data["resources"] if not res.get("is_cloud", False)]
print("JSON降级完成")
return data
def _downgrade_tracks(self, tracks: list):
"""降级轨道数据(移除低版本不支持的效果)"""
for track in tracks:
if "clips" not in track:
continue
for clip in track["clips"]:
# 移除高版本转场(保留基础转场)
if "transition" in clip:
transition_type = clip["transition"].get("type", "")
# 低版本支持的基础转场列表(可根据需求扩展)
supported_transitions = ["none", "fade", "slide", "push", "zoom"]
if transition_type not in supported_transitions:
clip["transition"] = {"type": "none", "duration": 0.3}
# 移除高版本特效如AI特效、高级滤镜
if "effects" in clip:
clip["effects"] = [
eff for eff in clip["effects"]
if eff.get("type") in ["filter", "adjust", "text", "sticker"] # 保留基础效果
]
# 降级音频效果移除3D音效等高级功能
if "audio_effects" in clip:
clip["audio_effects"] = [eff for eff in clip["audio_effects"] if eff.get("type") == "volume"]
def _save_project_json(self, data: Dict[str, Any]) -> bool:
"""保存降级后的JSON到临时目录"""
json_path = os.path.join(self.temp_dir, self.core_json)
try:
with open(json_path, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print("成功保存降级后的project.json")
return True
except Exception as e:
print(f"保存JSON失败{str(e)}")
return False
def _zip_to_cmp(self) -> bool:
"""将临时目录重新压缩为.cmp文件"""
try:
with zipfile.ZipFile(self.output_cmp, 'w', zipfile.ZIP_DEFLATED) as zip_ref:
# 遍历临时目录所有文件,添加到压缩包
for root, dirs, files in os.walk(self.temp_dir):
for file in files:
file_path = os.path.join(root, file)
# 保持压缩包内的相对路径
arcname = os.path.relpath(file_path, self.temp_dir)
zip_ref.write(file_path, arcname)
print(f"成功生成低版本草稿 → {self.output_cmp}")
return True
except Exception as e:
print(f"压缩失败:{str(e)}")
return False
def clean_temp(self):
"""清理临时目录"""
if os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
print("临时目录已清理")
def convert(self) -> bool:
"""执行完整转换流程"""
print("=" * 50)
print("剪映草稿高版本转低版本工具")
print(f"输入:{self.input_cmp}")
print(f"输出:{self.output_cmp}")
print(f"目标版本v{self.target_version}")
print("=" * 50)
try:
# 1. 解压
if not self._unzip_cmp():
return False
# 2. 读取JSON
data = self._load_project_json()
if not data:
return False
# 3. 降级JSON
downgraded_data = self._downgrade_json(data)
# 4. 保存JSON
if not self._save_project_json(downgraded_data):
return False
# 5. 重新压缩
if not self._zip_to_cmp():
return False
# 6. 清理临时文件
self.clean_temp()
print("=" * 50)
print("转换成功!请用目标版本剪映打开输出文件")
print("注意:复杂特效/AI功能可能已降级为基础效果")
print("=" * 50)
return True
except Exception as e:
print(f"转换异常:{str(e)}")
self.clean_temp()
return False
# ------------------------------
# 用法示例
# ------------------------------
if __name__ == "__main__":
# 请修改以下参数
INPUT_CMP = "high_version_project.cmp" # 高版本草稿路径
OUTPUT_CMP = "low_version_project.cmp" # 输出低版本草稿路径
TARGET_VERSION = "4.0" # 目标低版本3.0 或 4.0
# 创建转换器并执行
converter = JianYingConverter(
input_cmp=INPUT_CMP,
output_cmp=OUTPUT_CMP,
target_version=TARGET_VERSION
)
converter.convert()