Files
real-e-party-iOS/issues/scripts/2_rename_assets.py
edwinQQQ a35a711be6 chore: Initial clean commit
- Removed YuMi/Library/ (138 MB, not tracked)
- Removed YuMi/Resources/ (23 MB, not tracked)
- Removed old version assets (566 files, not tracked)
- Excluded Pods/, xcuserdata/ and other build artifacts
- Clean repository optimized for company server deployment
2025-10-09 16:19:14 +08:00

337 lines
12 KiB
Python
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
图片资源批量处理脚本
用法: python3 2_rename_assets.py
"""
import os
import json
import hashlib
import shutil
from pathlib import Path
from typing import Dict, List, Set
import re
# 颜色输出
class Colors:
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BLUE = '\033[94m'
END = '\033[0m'
# 配置
PROJECT_ROOT = "/Users/edwinqqq/Local/Company Projects/peko-ios"
ASSETS_DIR = os.path.join(PROJECT_ROOT, "YuMi/Assets.xcassets")
SOURCE_DIR = os.path.join(PROJECT_ROOT, "YuMi")
BACKUP_DIR = os.path.join(PROJECT_ROOT, "issues/temp_rename/assets_backup")
MAPPING_FILE = os.path.join(PROJECT_ROOT, "issues/temp_rename/asset_name_mapping.json")
# 存储映射关系
asset_mapping: Dict[str, str] = {}
def print_header(text):
"""打印标题"""
print(f"\n{Colors.GREEN}{'='*50}{Colors.END}")
print(f"{Colors.GREEN}{text}{Colors.END}")
print(f"{Colors.GREEN}{'='*50}{Colors.END}\n")
def print_info(text):
"""打印信息"""
print(f"{Colors.BLUE} {text}{Colors.END}")
def print_warning(text):
"""打印警告"""
print(f"{Colors.YELLOW}⚠️ {text}{Colors.END}")
def print_error(text):
"""打印错误"""
print(f"{Colors.RED}{text}{Colors.END}")
def print_success(text):
"""打印成功"""
print(f"{Colors.GREEN}{text}{Colors.END}")
def generate_new_name(old_name: str, index: int) -> str:
"""
生成新的资源名称
策略:使用语义化的随机名称,而不是完全随机
"""
# 提取原名称中的关键信息
parts = old_name.lower().split('_')
# 常见的UI元素映射
element_map = {
'icon': ['symbol', 'mark', 'sign', 'emblem'],
'button': ['btn', 'action', 'control', 'press'],
'background': ['bg', 'backdrop', 'layer', 'base'],
'avatar': ['profile', 'face', 'user', 'photo'],
'banner': ['header', 'top', 'promo', 'ad'],
'gift': ['present', 'reward', 'prize', 'bonus'],
'room': ['space', 'zone', 'area', 'hall'],
'home': ['main', 'index', 'start', 'feed'],
'message': ['msg', 'chat', 'talk', 'dialog'],
'mine': ['profile', 'me', 'personal', 'user'],
'login': ['signin', 'auth', 'entry', 'access'],
'arrow': ['pointer', 'indicator', 'direction', 'nav'],
'close': ['dismiss', 'exit', 'cancel', 'remove'],
'back': ['return', 'previous', 'prev', 'retreat'],
'next': ['forward', 'proceed', 'continue', 'advance'],
'play': ['start', 'run', 'launch', 'begin'],
'pause': ['stop', 'halt', 'freeze', 'wait'],
'more': ['additional', 'extra', 'plus', 'expand'],
}
# 尝试替换关键词
new_parts = []
for part in parts:
replaced = False
for key, alternatives in element_map.items():
if key in part:
# 使用 index 作为种子选择一个替代词
alt_index = (index + hash(part)) % len(alternatives)
new_part = part.replace(key, alternatives[alt_index])
new_parts.append(new_part)
replaced = True
break
if not replaced:
# 保留数字和常见后缀
if part.isdigit() or part in ['selected', 'normal', 'disabled', 'active', 'inactive']:
new_parts.append(part)
else:
# 使用简单的字符替换
new_part = ''.join([chr((ord(c) - 97 + 13) % 26 + 97) if c.isalpha() else c for c in part])
new_parts.append(new_part)
new_name = '_'.join(new_parts)
# 如果生成的名称和原名称一样,添加一个后缀
if new_name == old_name:
new_name = f"asset_{hashlib.md5(old_name.encode()).hexdigest()[:8]}"
return new_name
def backup_assets():
"""备份原始资源"""
print_header("第1步备份原始资源")
if os.path.exists(BACKUP_DIR):
print_warning("备份目录已存在,将覆盖...")
shutil.rmtree(BACKUP_DIR)
print_info(f"正在备份 {ASSETS_DIR}{BACKUP_DIR} ...")
shutil.copytree(ASSETS_DIR, BACKUP_DIR)
print_success(f"备份完成!")
def scan_imagesets() -> List[str]:
"""扫描所有 .imageset 目录"""
print_header("第2步扫描图片资源")
imagesets = []
for root, dirs, files in os.walk(ASSETS_DIR):
for dir_name in dirs:
if dir_name.endswith('.imageset'):
full_path = os.path.join(root, dir_name)
imagesets.append(full_path)
print_success(f"找到 {len(imagesets)} 个图片资源")
return imagesets
def rename_imageset(imageset_path: str, index: int) -> tuple:
"""
重命名单个 imageset
返回 (old_name, new_name)
"""
dir_name = os.path.basename(imageset_path)
old_name = dir_name.replace('.imageset', '')
# 跳过 AppIcon
if 'AppIcon' in old_name:
print_info(f"跳过 AppIcon: {old_name}")
return (old_name, old_name)
# 生成新名称
new_name = generate_new_name(old_name, index)
new_dir_name = f"{new_name}.imageset"
new_path = os.path.join(os.path.dirname(imageset_path), new_dir_name)
# 重命名目录
try:
os.rename(imageset_path, new_path)
# 更新 Contents.json
contents_json = os.path.join(new_path, 'Contents.json')
if os.path.exists(contents_json):
with open(contents_json, 'r', encoding='utf-8') as f:
data = json.load(f)
# 更新图片文件名引用
for image in data.get('images', []):
if 'filename' in image:
old_filename = image['filename']
if old_name in old_filename:
new_filename = old_filename.replace(old_name, new_name)
# 重命名实际的图片文件
old_file_path = os.path.join(new_path, old_filename)
new_file_path = os.path.join(new_path, new_filename)
if os.path.exists(old_file_path):
os.rename(old_file_path, new_file_path)
image['filename'] = new_filename
# 写回 Contents.json
with open(contents_json, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
return (old_name, new_name)
except Exception as e:
print_error(f"重命名失败 {old_name}: {str(e)}")
return (old_name, old_name)
def rename_all_imagesets():
"""批量重命名所有图片资源"""
print_header("第3步批量重命名图片资源")
imagesets = scan_imagesets()
total = len(imagesets)
success_count = 0
for index, imageset_path in enumerate(imagesets, 1):
old_name, new_name = rename_imageset(imageset_path, index)
if old_name != new_name:
asset_mapping[old_name] = new_name
success_count += 1
# 显示进度
if index % 100 == 0:
print_info(f"进度: {index}/{total} ({index*100//total}%)")
print_success(f"完成!成功重命名 {success_count} 个资源")
# 保存映射关系
os.makedirs(os.path.dirname(MAPPING_FILE), exist_ok=True)
with open(MAPPING_FILE, 'w', encoding='utf-8') as f:
json.dump(asset_mapping, f, indent=2, ensure_ascii=False)
print_success(f"映射关系已保存到: {MAPPING_FILE}")
def update_code_references():
"""更新代码中的图片资源引用"""
print_header("第4步更新代码引用")
print_warning("这一步需要手动处理,因为图片引用方式多样")
print_info("常见的引用方式:")
print(" 1. [UIImage imageNamed:@\"old_name\"]")
print(" 2. UIImage(named: \"old_name\")")
print(" 3. @\"old_name\"")
print("")
# 生成替换脚本
replace_script = os.path.join(PROJECT_ROOT, "issues/temp_rename/replace_image_refs.sh")
with open(replace_script, 'w') as f:
f.write("#!/bin/bash\n")
f.write("# 自动生成的图片引用替换脚本\n\n")
f.write(f"SOURCE_DIR=\"{SOURCE_DIR}\"\n\n")
for old_name, new_name in asset_mapping.items():
# 转义特殊字符
old_escaped = old_name.replace('"', '\\"')
new_escaped = new_name.replace('"', '\\"')
f.write(f"# {old_name}{new_name}\n")
f.write(f"find \"$SOURCE_DIR\" -type f \\( -name \"*.m\" -o -name \"*.h\" -o -name \"*.swift\" \\) -exec sed -i '' 's/@\"{old_escaped}\"/@\"{new_escaped}\"/g' {{}} +\n")
f.write(f"find \"$SOURCE_DIR\" -type f \\( -name \"*.m\" -o -name \"*.h\" -o -name \"*.swift\" \\) -exec sed -i '' 's/\"{old_escaped}\"/\"{new_escaped}\"/g' {{}} +\n\n")
os.chmod(replace_script, 0o755)
print_success(f"替换脚本已生成: {replace_script}")
print_warning("请手动执行该脚本进行代码替换")
def generate_report():
"""生成替换报告"""
print_header("第5步生成报告")
report_file = os.path.join(PROJECT_ROOT, "issues/temp_rename/assets_rename_report.txt")
with open(report_file, 'w', encoding='utf-8') as f:
f.write("=" * 60 + "\n")
f.write("图片资源批量重命名报告\n")
f.write("=" * 60 + "\n\n")
f.write(f"执行时间: {os.popen('date').read()}\n")
f.write(f"总共处理: {len(asset_mapping)} 个资源\n\n")
f.write("=" * 60 + "\n")
f.write("重命名映射前50个:\n")
f.write("=" * 60 + "\n\n")
count = 0
for old_name, new_name in asset_mapping.items():
f.write(f"{old_name:40}{new_name}\n")
count += 1
if count >= 50:
f.write(f"\n... 还有 {len(asset_mapping) - 50} 个资源\n")
break
f.write("\n" + "=" * 60 + "\n")
f.write("下一步操作:\n")
f.write("=" * 60 + "\n\n")
f.write("1. 执行生成的替换脚本更新代码引用\n")
f.write("2. 在 Xcode 中清理并重新构建项目\n")
f.write("3. 运行应用,检查所有图片是否正常显示\n")
f.write("4. 使用 UI 测试验证关键页面\n\n")
f.write("备份位置:\n")
f.write(f" {BACKUP_DIR}\n\n")
f.write("映射文件:\n")
f.write(f" {MAPPING_FILE}\n\n")
f.write("替换脚本:\n")
f.write(f" {os.path.join(PROJECT_ROOT, 'issues/temp_rename/replace_image_refs.sh')}\n\n")
print_success(f"报告已生成: {report_file}")
# 打印简要统计
print("")
print(f" 📊 总共重命名: {len(asset_mapping)} 个资源")
print(f" 📁 备份位置: {BACKUP_DIR}")
print(f" 📄 映射文件: {MAPPING_FILE}")
print("")
def main():
"""主函数"""
print_header("白牌项目 - 图片资源批量重命名工具")
# 确认操作
print_warning("此脚本将重命名所有图片资源!")
print_warning("请确保:")
print(" 1. 已经运行了类名替换脚本")
print(" 2. 已经提交了所有更改")
print(" 3. 已经创建了新的 Git 分支")
print("")
confirm = input("确认继续?(y/N): ")
if confirm.lower() != 'y':
print("已取消操作")
return
try:
# 执行步骤
backup_assets()
rename_all_imagesets()
update_code_references()
generate_report()
print_header("✅ 图片资源批量重命名完成!")
print_warning("记得执行生成的替换脚本更新代码引用!")
except Exception as e:
print_error(f"执行过程中出错: {str(e)}")
print_warning("你可以从备份恢复:")
print(f" rm -rf {ASSETS_DIR}")
print(f" cp -r {BACKUP_DIR} {ASSETS_DIR}")
if __name__ == "__main__":
main()