Kamixitong/test.py
2025-11-12 15:11:05 +08:00

220 lines
8.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()