|
|
@@ -68,10 +68,18 @@ class AmazonImageUploader:
|
|
|
self.product_type = config["product_type"]
|
|
|
|
|
|
def sha256_hash(self, data):
|
|
|
- return hashlib.sha256(data.encode("utf-8")).hexdigest()
|
|
|
+ """兼容 str 和 bytes"""
|
|
|
+ if isinstance(data, str):
|
|
|
+ data = data.encode("utf-8")
|
|
|
+ return hashlib.sha256(data).hexdigest()
|
|
|
|
|
|
def hmac_sha256(self, key, msg):
|
|
|
- return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
|
|
|
+ """返回 bytes 类型的 digest,兼容 str 和 bytes 输入"""
|
|
|
+ if isinstance(key, str):
|
|
|
+ key = key.encode("utf-8")
|
|
|
+ if isinstance(msg, str):
|
|
|
+ msg = msg.encode("utf-8")
|
|
|
+ return hmac.new(key, msg, hashlib.sha256).digest()
|
|
|
|
|
|
def get_signature_key(self, secret_key, date_stamp, region, service):
|
|
|
k_date = self.hmac_sha256(("AWS4" + secret_key).encode("utf-8"), date_stamp)
|
|
|
@@ -120,7 +128,7 @@ class AmazonImageUploader:
|
|
|
)
|
|
|
|
|
|
signing_key = self.get_signature_key(self.aws_secret_key, date_stamp, self.aws_region, service)
|
|
|
- signature = self.hmac_sha256(signing_key, string_to_sign.encode("utf-8")).hexdigest()
|
|
|
+ signature = self.hmac_sha256(signing_key, string_to_sign).hex()
|
|
|
|
|
|
auth_header = (
|
|
|
f"{algorithm} Credential={self.aws_access_key}/{credential_scope}, "
|
|
|
@@ -348,45 +356,42 @@ class ImageMatcherApp:
|
|
|
"""创建亚马逊主图更换选项卡"""
|
|
|
tab = self.tab_amazon
|
|
|
tab.columnconfigure(1, weight=1)
|
|
|
- tab.rowconfigure(6, weight=1)
|
|
|
+ tab.rowconfigure(5, weight=1)
|
|
|
|
|
|
# 标题
|
|
|
- title_label = ttk.Label(tab, text="亚马逊主图/附图批量更换", font=("Arial", 14, "bold"))
|
|
|
+ title_label = ttk.Label(tab, text="亚马逊主图/附图更换", font=("Arial", 14, "bold"))
|
|
|
title_label.grid(row=0, column=0, columnspan=3, pady=10)
|
|
|
|
|
|
- # 模板文件选择
|
|
|
- ttk.Label(tab, text="模板文件:").grid(row=1, column=0, sticky=tk.W, pady=5)
|
|
|
- self.amazon_template_path = tk.StringVar()
|
|
|
- ttk.Entry(tab, textvariable=self.amazon_template_path, width=60).grid(row=1, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)
|
|
|
- ttk.Button(tab, text="选择模板", command=self.select_amazon_template).grid(row=1, column=2, pady=5, padx=5)
|
|
|
-
|
|
|
- # SKU输入(手动输入,不是从表格读取)
|
|
|
- ttk.Label(tab, text="SKU (手动输入):").grid(row=2, column=0, sticky=tk.W, pady=5)
|
|
|
+ # SKU输入
|
|
|
+ ttk.Label(tab, text="SKU:").grid(row=1, column=0, sticky=tk.W, pady=5)
|
|
|
self.sku_input = tk.StringVar(value="bar grey 03")
|
|
|
- ttk.Entry(tab, textvariable=self.sku_input, width=40).grid(row=2, column=1, sticky=tk.W, pady=5, padx=5)
|
|
|
-
|
|
|
- # 主图列选择
|
|
|
- ttk.Label(tab, text="主图列名:").grid(row=3, column=0, sticky=tk.W, pady=5)
|
|
|
- self.main_image_column = tk.StringVar(value="产品主图")
|
|
|
- ttk.Entry(tab, textvariable=self.main_image_column, width=30).grid(row=3, column=1, sticky=tk.W, pady=5, padx=5)
|
|
|
-
|
|
|
- # 附图列选择
|
|
|
- ttk.Label(tab, text="附图列名(逗号分隔):").grid(row=4, column=0, sticky=tk.W, pady=5)
|
|
|
- self.other_image_columns = tk.StringVar(value="上架图片1,上架图片2,上架图片3,上架图片4,上架图片5,上架图片6,上架图片7,上架图片8")
|
|
|
- ttk.Entry(tab, textvariable=self.other_image_columns, width=60).grid(row=4, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)
|
|
|
+ ttk.Entry(tab, textvariable=self.sku_input, width=40).grid(row=1, column=1, sticky=tk.W, pady=5, padx=5)
|
|
|
+
|
|
|
+ # 图片文件夹选择
|
|
|
+ ttk.Label(tab, text="图片文件夹:").grid(row=2, column=0, sticky=tk.W, pady=5)
|
|
|
+ self.amazon_image_dir = tk.StringVar()
|
|
|
+ ttk.Entry(tab, textvariable=self.amazon_image_dir, width=60).grid(row=2, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)
|
|
|
+ ttk.Button(tab, text="选择文件夹", command=self.select_amazon_image_dir).grid(row=2, column=2, pady=5, padx=5)
|
|
|
+
|
|
|
+ # 说明标签
|
|
|
+ note_label = ttk.Label(
|
|
|
+ tab,
|
|
|
+ text="说明:文件夹中的图片将按文件名排序,第1张作为主图,第2-9张作为附图1-8\n"
|
|
|
+ "支持格式:.jpg / .jpeg / .png",
|
|
|
+ foreground="gray"
|
|
|
+ )
|
|
|
+ note_label.grid(row=3, column=0, columnspan=3, sticky=tk.W, pady=5, padx=5)
|
|
|
|
|
|
# 按钮框架
|
|
|
button_frame = ttk.Frame(tab)
|
|
|
- button_frame.grid(row=5, column=0, columnspan=3, pady=10)
|
|
|
+ button_frame.grid(row=4, column=0, columnspan=3, pady=10)
|
|
|
|
|
|
- ttk.Button(button_frame, text="保存配置", command=self.save_amazon_config, width=12).pack(side=tk.LEFT, padx=5)
|
|
|
- ttk.Button(button_frame, text="加载配置", command=self.load_amazon_config, width=12).pack(side=tk.LEFT, padx=5)
|
|
|
ttk.Button(button_frame, text="开始上传", command=self.start_amazon_upload, width=15).pack(side=tk.LEFT, padx=5)
|
|
|
ttk.Button(button_frame, text="清空日志", command=self.clear_amazon_log, width=15).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
# 日志框
|
|
|
log_frame = ttk.LabelFrame(tab, text="上传日志", padding="5")
|
|
|
- log_frame.grid(row=6, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
|
|
|
+ log_frame.grid(row=5, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
|
|
|
log_frame.columnconfigure(0, weight=1)
|
|
|
log_frame.rowconfigure(0, weight=1)
|
|
|
|
|
|
@@ -396,64 +401,70 @@ class ImageMatcherApp:
|
|
|
# 进度标签
|
|
|
self.amazon_progress_var = tk.StringVar(value="就绪")
|
|
|
progress_label = ttk.Label(tab, textvariable=self.amazon_progress_var, relief=tk.SUNKEN, anchor=tk.W)
|
|
|
- progress_label.grid(row=7, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
|
|
|
+ progress_label.grid(row=6, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
|
|
|
|
|
|
def create_detail_tab(self):
|
|
|
"""创建亚马逊详情图更换选项卡 (A+ Content)"""
|
|
|
tab = self.tab_detail
|
|
|
tab.columnconfigure(1, weight=1)
|
|
|
- tab.rowconfigure(8, weight=1)
|
|
|
+ tab.rowconfigure(9, weight=1)
|
|
|
|
|
|
# 标题
|
|
|
title_label = ttk.Label(tab, text="亚马逊详情图更换 (A+ Content)", font=("Arial", 14, "bold"))
|
|
|
title_label.grid(row=0, column=0, columnspan=3, pady=10)
|
|
|
|
|
|
- # 模板文件选择
|
|
|
- ttk.Label(tab, text="模板文件:").grid(row=1, column=0, sticky=tk.W, pady=5)
|
|
|
- self.detail_template_path = tk.StringVar()
|
|
|
- ttk.Entry(tab, textvariable=self.detail_template_path, width=60).grid(row=1, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)
|
|
|
- ttk.Button(tab, text="选择模板", command=self.select_detail_template).grid(row=1, column=2, pady=5, padx=5)
|
|
|
-
|
|
|
# ASIN输入
|
|
|
- ttk.Label(tab, text="ASIN (手动输入):").grid(row=2, column=0, sticky=tk.W, pady=5)
|
|
|
+ ttk.Label(tab, text="ASIN:").grid(row=1, column=0, sticky=tk.W, pady=5)
|
|
|
self.asin_input = tk.StringVar(value="B0FR1WRK4M")
|
|
|
- ttk.Entry(tab, textvariable=self.asin_input, width=40).grid(row=2, column=1, sticky=tk.W, pady=5, padx=5)
|
|
|
+ ttk.Entry(tab, textvariable=self.asin_input, width=40).grid(row=1, column=1, sticky=tk.W, pady=5, padx=5)
|
|
|
+
|
|
|
+ # 标题输入
|
|
|
+ ttk.Label(tab, text="标题:").grid(row=2, column=0, sticky=tk.W, pady=5)
|
|
|
+ self.detail_headline = tk.StringVar(value="Premium Quality Product")
|
|
|
+ ttk.Entry(tab, textvariable=self.detail_headline, width=60).grid(row=2, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)
|
|
|
|
|
|
- # 图片URL列选择(详情图)
|
|
|
- ttk.Label(tab, text="详情图列名(逗号分隔):").grid(row=3, column=0, sticky=tk.W, pady=5)
|
|
|
- self.detail_image_columns = tk.StringVar(value="详情图URL1,详情图URL2,详情图URL3")
|
|
|
- ttk.Entry(tab, textvariable=self.detail_image_columns, width=60).grid(row=3, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)
|
|
|
+ # 描述输入
|
|
|
+ ttk.Label(tab, text="描述:").grid(row=3, column=0, sticky=tk.W, pady=5)
|
|
|
+ self.detail_body = tk.StringVar(value="High-quality product designed for your needs.")
|
|
|
+ ttk.Entry(tab, textvariable=self.detail_body, width=60).grid(row=3, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)
|
|
|
+
|
|
|
+ # 图片文件夹选择
|
|
|
+ ttk.Label(tab, text="图片文件夹:").grid(row=4, column=0, sticky=tk.W, pady=5)
|
|
|
+ self.detail_image_dir = tk.StringVar()
|
|
|
+ ttk.Entry(tab, textvariable=self.detail_image_dir, width=60).grid(row=4, column=1, sticky=(tk.W, tk.E), pady=5, padx=5)
|
|
|
+ ttk.Button(tab, text="选择文件夹", command=self.select_detail_image_dir).grid(row=4, column=2, pady=5, padx=5)
|
|
|
|
|
|
# 图片尺寸设置
|
|
|
- ttk.Label(tab, text="图片宽度(像素):").grid(row=4, column=0, sticky=tk.W, pady=5)
|
|
|
+ size_frame = ttk.Frame(tab)
|
|
|
+ size_frame.grid(row=5, column=0, columnspan=3, sticky=tk.W, pady=5, padx=5)
|
|
|
+
|
|
|
+ ttk.Label(size_frame, text="图片宽度(像素):").pack(side=tk.LEFT)
|
|
|
self.image_width = tk.StringVar(value="970")
|
|
|
- ttk.Entry(tab, textvariable=self.image_width, width=15).grid(row=4, column=1, sticky=tk.W, pady=5, padx=5)
|
|
|
+ ttk.Entry(size_frame, textvariable=self.image_width, width=10).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
- ttk.Label(tab, text="图片高度(像素):").grid(row=5, column=0, sticky=tk.W, pady=5)
|
|
|
+ ttk.Label(size_frame, text="图片高度(像素):").pack(side=tk.LEFT, padx=(20, 0))
|
|
|
self.image_height = tk.StringVar(value="600")
|
|
|
- ttk.Entry(tab, textvariable=self.image_height, width=15).grid(row=5, column=1, sticky=tk.W, pady=5, padx=5)
|
|
|
-
|
|
|
- # 标题和描述设置
|
|
|
- ttk.Label(tab, text="模块标题:").grid(row=6, column=0, sticky=tk.W, pady=5)
|
|
|
- self.module_headline = tk.StringVar(value="Premium Quality Product")
|
|
|
- ttk.Entry(tab, textvariable=self.module_headline, width=50).grid(row=6, column=1, sticky=tk.W, pady=5, padx=5)
|
|
|
-
|
|
|
- ttk.Label(tab, text="模块描述:").grid(row=7, column=0, sticky=tk.W, pady=5)
|
|
|
- self.module_body = tk.StringVar(value="High-quality product designed for your needs.")
|
|
|
- ttk.Entry(tab, textvariable=self.module_body, width=50).grid(row=7, column=1, sticky=tk.W, pady=5, padx=5)
|
|
|
+ ttk.Entry(size_frame, textvariable=self.image_height, width=10).pack(side=tk.LEFT, padx=5)
|
|
|
+
|
|
|
+ # 说明标签
|
|
|
+ note_label = ttk.Label(
|
|
|
+ tab,
|
|
|
+ text="说明:文件夹中的所有图片将按文件名顺序上传,每张图片作为一个 A+ 模块\n"
|
|
|
+ "支持格式:.jpg / .jpeg / .png",
|
|
|
+ foreground="gray"
|
|
|
+ )
|
|
|
+ note_label.grid(row=6, column=0, columnspan=3, sticky=tk.W, pady=5, padx=5)
|
|
|
|
|
|
# 按钮框架
|
|
|
button_frame = ttk.Frame(tab)
|
|
|
- button_frame.grid(row=8, column=0, columnspan=3, pady=10)
|
|
|
+ button_frame.grid(row=7, column=0, columnspan=3, pady=10)
|
|
|
|
|
|
- ttk.Button(button_frame, text="保存配置", command=self.save_detail_config, width=12).pack(side=tk.LEFT, padx=5)
|
|
|
- ttk.Button(button_frame, text="加载配置", command=self.load_detail_config, width=12).pack(side=tk.LEFT, padx=5)
|
|
|
ttk.Button(button_frame, text="开始上传", command=self.start_detail_upload, width=15).pack(side=tk.LEFT, padx=5)
|
|
|
ttk.Button(button_frame, text="清空日志", command=self.clear_detail_log, width=15).pack(side=tk.LEFT, padx=5)
|
|
|
|
|
|
# 日志框
|
|
|
log_frame = ttk.LabelFrame(tab, text="上传日志", padding="5")
|
|
|
- log_frame.grid(row=9, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
|
|
|
+ log_frame.grid(row=8, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
|
|
|
log_frame.columnconfigure(0, weight=1)
|
|
|
log_frame.rowconfigure(0, weight=1)
|
|
|
|
|
|
@@ -463,7 +474,7 @@ class ImageMatcherApp:
|
|
|
# 进度标签
|
|
|
self.detail_progress_var = tk.StringVar(value="就绪")
|
|
|
progress_label = ttk.Label(tab, textvariable=self.detail_progress_var, relief=tk.SUNKEN, anchor=tk.W)
|
|
|
- progress_label.grid(row=10, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
|
|
|
+ progress_label.grid(row=9, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
|
|
|
|
|
|
def create_settings_tab(self):
|
|
|
"""创建设置选项卡"""
|
|
|
@@ -712,6 +723,7 @@ class ImageMatcherApp:
|
|
|
except Exception:
|
|
|
pass
|
|
|
|
|
|
+ # ==================== 文件/目录选择 ====================
|
|
|
def select_template(self):
|
|
|
filename = filedialog.askopenfilename(
|
|
|
title="选择模板文件",
|
|
|
@@ -726,25 +738,20 @@ class ImageMatcherApp:
|
|
|
if directory:
|
|
|
self.image_dir.set(directory)
|
|
|
self.log(f"已选择图片目录: {directory}")
|
|
|
-
|
|
|
- def select_amazon_template(self):
|
|
|
- filename = filedialog.askopenfilename(
|
|
|
- title="选择模板文件",
|
|
|
- filetypes=[("Excel文件", "*.xlsx *.xls"), ("所有文件", "*.*")]
|
|
|
- )
|
|
|
- if filename:
|
|
|
- self.amazon_template_path.set(filename)
|
|
|
- self.amazon_log(f"已选择模板文件: {filename}")
|
|
|
|
|
|
- def select_detail_template(self):
|
|
|
- filename = filedialog.askopenfilename(
|
|
|
- title="选择模板文件",
|
|
|
- filetypes=[("Excel文件", "*.xlsx *.xls"), ("所有文件", "*.*")]
|
|
|
- )
|
|
|
- if filename:
|
|
|
- self.detail_template_path.set(filename)
|
|
|
- self.detail_log(f"已选择模板文件: {filename}")
|
|
|
-
|
|
|
+ def select_amazon_image_dir(self):
|
|
|
+ directory = filedialog.askdirectory(title="选择图片文件夹")
|
|
|
+ if directory:
|
|
|
+ self.amazon_image_dir.set(directory)
|
|
|
+ self.amazon_log(f"已选择图片文件夹: {directory}")
|
|
|
+
|
|
|
+ def select_detail_image_dir(self):
|
|
|
+ directory = filedialog.askdirectory(title="选择图片文件夹")
|
|
|
+ if directory:
|
|
|
+ self.detail_image_dir.set(directory)
|
|
|
+ self.detail_log(f"已选择图片文件夹: {directory}")
|
|
|
+
|
|
|
+ # ==================== 日志方法 ====================
|
|
|
def log(self, message):
|
|
|
"""在匹配选项卡日志框中添加消息"""
|
|
|
self.log_text.insert(tk.END, f"{message}\n")
|
|
|
@@ -774,100 +781,28 @@ class ImageMatcherApp:
|
|
|
def clear_detail_log(self):
|
|
|
"""清空详情图选项卡日志"""
|
|
|
self.detail_log_text.delete(1.0, tk.END)
|
|
|
-
|
|
|
- def get_config_path(self):
|
|
|
- """获取配置文件路径"""
|
|
|
+
|
|
|
+ # ==================== 配置路径 ====================
|
|
|
+ def get_amazon_config_path(self):
|
|
|
+ """获取主图配置路径"""
|
|
|
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "amazon_config.json")
|
|
|
|
|
|
def get_detail_config_path(self):
|
|
|
- """获取详情图配置文件路径"""
|
|
|
+ """获取详情图配置路径"""
|
|
|
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "amazon_detail_config.json")
|
|
|
-
|
|
|
- def save_amazon_config(self):
|
|
|
- """保存亚马逊配置"""
|
|
|
- config = {
|
|
|
- "sku": self.sku_input.get(),
|
|
|
- "main_image_column": self.main_image_column.get(),
|
|
|
- "other_image_columns": self.other_image_columns.get(),
|
|
|
- "template_path": self.amazon_template_path.get()
|
|
|
- }
|
|
|
- try:
|
|
|
- with open(self.get_config_path(), 'w', encoding='utf-8') as f:
|
|
|
- json.dump(config, f, ensure_ascii=False, indent=2)
|
|
|
- self.amazon_log("✅ 配置已保存")
|
|
|
- except Exception as e:
|
|
|
- self.amazon_log(f"❌ 保存配置失败: {str(e)}")
|
|
|
-
|
|
|
- def load_amazon_config(self):
|
|
|
- """加载亚马逊配置"""
|
|
|
- try:
|
|
|
- config_path = self.get_config_path()
|
|
|
- if os.path.exists(config_path):
|
|
|
- with open(config_path, 'r', encoding='utf-8') as f:
|
|
|
- config = json.load(f)
|
|
|
- self.sku_input.set(config.get("sku", "bar grey 03"))
|
|
|
- self.main_image_column.set(config.get("main_image_column", "产品主图"))
|
|
|
- self.other_image_columns.set(config.get("other_image_columns", "上架图片1,上架图片2,上架图片3,上架图片4,上架图片5,上架图片6,上架图片7,上架图片8"))
|
|
|
- if config.get("template_path"):
|
|
|
- self.amazon_template_path.set(config.get("template_path"))
|
|
|
- self.amazon_log("✅ 配置已加载")
|
|
|
- else:
|
|
|
- self.amazon_log("⚠️ 未找到配置文件,使用默认配置")
|
|
|
- except Exception as e:
|
|
|
- self.amazon_log(f"❌ 加载配置失败: {str(e)}")
|
|
|
|
|
|
- def save_detail_config(self):
|
|
|
- """保存详情图配置"""
|
|
|
- config = {
|
|
|
- "asin": self.asin_input.get(),
|
|
|
- "detail_image_columns": self.detail_image_columns.get(),
|
|
|
- "template_path": self.detail_template_path.get(),
|
|
|
- "image_width": self.image_width.get(),
|
|
|
- "image_height": self.image_height.get(),
|
|
|
- "module_headline": self.module_headline.get(),
|
|
|
- "module_body": self.module_body.get()
|
|
|
- }
|
|
|
- try:
|
|
|
- with open(self.get_detail_config_path(), 'w', encoding='utf-8') as f:
|
|
|
- json.dump(config, f, ensure_ascii=False, indent=2)
|
|
|
- self.detail_log("✅ 配置已保存")
|
|
|
- except Exception as e:
|
|
|
- self.detail_log(f"❌ 保存配置失败: {str(e)}")
|
|
|
-
|
|
|
- def load_detail_config(self):
|
|
|
- """加载详情图配置"""
|
|
|
- try:
|
|
|
- config_path = self.get_detail_config_path()
|
|
|
- if os.path.exists(config_path):
|
|
|
- with open(config_path, 'r', encoding='utf-8') as f:
|
|
|
- config = json.load(f)
|
|
|
- self.asin_input.set(config.get("asin", "B0FR1WRK4M"))
|
|
|
- self.detail_image_columns.set(config.get("detail_image_columns", "详情图URL1,详情图URL2,详情图URL3"))
|
|
|
- self.image_width.set(config.get("image_width", "970"))
|
|
|
- self.image_height.set(config.get("image_height", "600"))
|
|
|
- self.module_headline.set(config.get("module_headline", "Premium Quality Product"))
|
|
|
- self.module_body.set(config.get("module_body", "High-quality product designed for your needs."))
|
|
|
- if config.get("template_path"):
|
|
|
- self.detail_template_path.set(config.get("template_path"))
|
|
|
- self.detail_log("✅ 配置已加载")
|
|
|
- else:
|
|
|
- self.detail_log("⚠️ 未找到配置文件,使用默认配置")
|
|
|
- except Exception as e:
|
|
|
- self.detail_log(f"❌ 加载配置失败: {str(e)}")
|
|
|
-
|
|
|
+ # ==================== 配置文件 ====================
|
|
|
def load_config(self):
|
|
|
- """启动时自动加载所有配置"""
|
|
|
- # 加载亚马逊上传配置
|
|
|
+ """启动时自动加载配置"""
|
|
|
+ # 加载主图配置
|
|
|
try:
|
|
|
- config_path = self.get_config_path()
|
|
|
+ config_path = self.get_amazon_config_path()
|
|
|
if os.path.exists(config_path):
|
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
|
config = json.load(f)
|
|
|
self.sku_input.set(config.get("sku", "bar grey 03"))
|
|
|
- self.main_image_column.set(config.get("main_image_column", "产品主图"))
|
|
|
- self.other_image_columns.set(config.get("other_image_columns", "上架图片1,上架图片2,上架图片3,上架图片4,上架图片5,上架图片6,上架图片7,上架图片8"))
|
|
|
- if config.get("template_path"):
|
|
|
- self.amazon_template_path.set(config.get("template_path"))
|
|
|
+ if config.get("image_dir"):
|
|
|
+ self.amazon_image_dir.set(config["image_dir"])
|
|
|
except Exception:
|
|
|
pass
|
|
|
|
|
|
@@ -878,19 +813,19 @@ class ImageMatcherApp:
|
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
|
config = json.load(f)
|
|
|
self.asin_input.set(config.get("asin", "B0FR1WRK4M"))
|
|
|
- self.detail_image_columns.set(config.get("detail_image_columns", "详情图URL1,详情图URL2,详情图URL3"))
|
|
|
+ self.detail_headline.set(config.get("headline", "Premium Quality Product"))
|
|
|
+ self.detail_body.set(config.get("body", "High-quality product designed for your needs."))
|
|
|
self.image_width.set(config.get("image_width", "970"))
|
|
|
self.image_height.set(config.get("image_height", "600"))
|
|
|
- self.module_headline.set(config.get("module_headline", "Premium Quality Product"))
|
|
|
- self.module_body.set(config.get("module_body", "High-quality product designed for your needs."))
|
|
|
- if config.get("template_path"):
|
|
|
- self.detail_template_path.set(config.get("template_path"))
|
|
|
+ if config.get("image_dir"):
|
|
|
+ self.detail_image_dir.set(config["image_dir"])
|
|
|
except Exception:
|
|
|
pass
|
|
|
|
|
|
# 加载设置配置
|
|
|
self.load_settings()
|
|
|
|
|
|
+ # ==================== 图片匹配上传 ====================
|
|
|
def start_matching(self):
|
|
|
"""开始匹配处理"""
|
|
|
if not self.template_path.get():
|
|
|
@@ -1019,6 +954,7 @@ class ImageMatcherApp:
|
|
|
break
|
|
|
|
|
|
if not local_file:
|
|
|
+ self.log(f"找不到目录:{filepath}")
|
|
|
return None
|
|
|
|
|
|
try:
|
|
|
@@ -1047,55 +983,104 @@ class ImageMatcherApp:
|
|
|
self.log(f" 上传出错: {str(e)}")
|
|
|
return None
|
|
|
|
|
|
+ # ==================== 亚马逊主图更换 ====================
|
|
|
def start_amazon_upload(self):
|
|
|
- """开始亚马逊图片上传"""
|
|
|
- if not self.amazon_template_path.get():
|
|
|
- messagebox.showerror("错误", "请先选择模板文件!")
|
|
|
- return
|
|
|
-
|
|
|
+ """开始亚马逊主图上传"""
|
|
|
if not self.sku_input.get().strip():
|
|
|
messagebox.showerror("错误", "请先输入SKU!")
|
|
|
return
|
|
|
+
|
|
|
+ if not self.amazon_image_dir.get():
|
|
|
+ messagebox.showerror("错误", "请先选择图片文件夹!")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 保存配置
|
|
|
+ self.save_amazon_config()
|
|
|
|
|
|
thread = threading.Thread(target=self.process_amazon_upload, daemon=True)
|
|
|
thread.start()
|
|
|
+
|
|
|
+ def save_amazon_config(self):
|
|
|
+ """保存主图配置"""
|
|
|
+ config = {
|
|
|
+ "sku": self.sku_input.get(),
|
|
|
+ "image_dir": self.amazon_image_dir.get(),
|
|
|
+ }
|
|
|
+ try:
|
|
|
+ with open(self.get_amazon_config_path(), 'w', encoding='utf-8') as f:
|
|
|
+ json.dump(config, f, ensure_ascii=False, indent=2)
|
|
|
+ except Exception:
|
|
|
+ pass
|
|
|
+
|
|
|
+ def get_image_files(self, folder):
|
|
|
+ """获取文件夹中的图片文件,按文件名排序"""
|
|
|
+ extensions = ['.jpg', '.jpeg', '.png']
|
|
|
+ files = []
|
|
|
+ if not os.path.isdir(folder):
|
|
|
+ return files
|
|
|
+ for f in os.listdir(folder):
|
|
|
+ ext = os.path.splitext(f)[1].lower()
|
|
|
+ if ext in extensions:
|
|
|
+ files.append(os.path.join(folder, f))
|
|
|
+ # 按文件名排序
|
|
|
+ files.sort(key=lambda x: os.path.basename(x).lower())
|
|
|
+ return files
|
|
|
+
|
|
|
+ def upload_image_to_cos(self, local_file):
|
|
|
+ """上传图片到图片服务器,返回URL"""
|
|
|
+ try:
|
|
|
+ full_url = f"{self.upload_url}?group={self.upload_group}&user={self.upload_user}"
|
|
|
+
|
|
|
+ content_type = 'image/jpeg'
|
|
|
+ if local_file.lower().endswith('.png'):
|
|
|
+ content_type = 'image/png'
|
|
|
+
|
|
|
+ with open(local_file, 'rb') as f:
|
|
|
+ files = {'file': (os.path.basename(local_file), f, content_type)}
|
|
|
+ response = requests.post(full_url, files=files, timeout=30)
|
|
|
+
|
|
|
+ if response.status_code == 200:
|
|
|
+ result = response.json()
|
|
|
+ if result.get('err') == 0:
|
|
|
+ return result.get('url')
|
|
|
+ else:
|
|
|
+ raise Exception(result.get('msg', '未知错误'))
|
|
|
+ else:
|
|
|
+ raise Exception(f"HTTP {response.status_code}")
|
|
|
+ except Exception as e:
|
|
|
+ raise Exception(f"上传失败: {str(e)}")
|
|
|
|
|
|
def process_amazon_upload(self):
|
|
|
- """执行亚马逊图片上传"""
|
|
|
+ """执行亚马逊主图上传"""
|
|
|
try:
|
|
|
self.amazon_progress_var.set("处理中...")
|
|
|
self.amazon_log("=" * 50)
|
|
|
- self.amazon_log("开始亚马逊图片上传...")
|
|
|
+ self.amazon_log("开始亚马逊主图/附图上传...")
|
|
|
|
|
|
- template_path = self.amazon_template_path.get()
|
|
|
sku = self.sku_input.get().strip()
|
|
|
- main_img_col_name = self.main_image_column.get()
|
|
|
- other_cols_str = self.other_image_columns.get()
|
|
|
- other_col_names = [col.strip() for col in other_cols_str.split(',') if col.strip()]
|
|
|
+ image_dir = self.amazon_image_dir.get()
|
|
|
|
|
|
- self.amazon_log(f"读取模板文件: {template_path}")
|
|
|
- self.amazon_log(f"使用的SKU: {sku}")
|
|
|
+ self.amazon_log(f"SKU: {sku}")
|
|
|
+ self.amazon_log(f"图片文件夹: {image_dir}")
|
|
|
|
|
|
- # 读取Excel
|
|
|
- df = pd.read_excel(template_path)
|
|
|
- self.amazon_log(f"找到 {len(df)} 行数据")
|
|
|
-
|
|
|
- # 检查必要的列
|
|
|
- if main_img_col_name not in df.columns:
|
|
|
- self.amazon_log(f"❌ 未找到主图列: {main_img_col_name}")
|
|
|
+ # 获取图片文件
|
|
|
+ image_files = self.get_image_files(image_dir)
|
|
|
+ if not image_files:
|
|
|
+ self.amazon_log("❌ 未找到任何图片文件")
|
|
|
self.amazon_progress_var.set("上传失败")
|
|
|
return
|
|
|
-
|
|
|
- # 检查附图列
|
|
|
- existing_other_cols = [col for col in other_col_names if col in df.columns]
|
|
|
- if not existing_other_cols:
|
|
|
- self.amazon_log("⚠️ 未找到任何附图列")
|
|
|
|
|
|
- self.amazon_log(f"主图列: {main_img_col_name}")
|
|
|
- self.amazon_log(f"附图列: {existing_other_cols}")
|
|
|
+ self.amazon_log(f"找到 {len(image_files)} 张图片")
|
|
|
+ for i, f in enumerate(image_files, start=1):
|
|
|
+ self.amazon_log(f" {i}. {os.path.basename(f)}")
|
|
|
+
|
|
|
+ # 限制最多9张(1主图+8附图)
|
|
|
+ if len(image_files) > 9:
|
|
|
+ self.amazon_log(f"⚠️ 图片数量超过9张,只使用前9张")
|
|
|
+ image_files = image_files[:9]
|
|
|
|
|
|
# 获取Access Token
|
|
|
- self.amazon_log("🔑 获取Access Token...")
|
|
|
+ self.amazon_log("\n🔑 获取Access Token...")
|
|
|
try:
|
|
|
token = self.amazon_uploader.get_access_token()
|
|
|
self.amazon_log("✅ Token获取成功")
|
|
|
@@ -1104,66 +1089,57 @@ class ImageMatcherApp:
|
|
|
self.amazon_progress_var.set("上传失败")
|
|
|
return
|
|
|
|
|
|
- # 处理每一行
|
|
|
+ # 处理每张图片
|
|
|
success_count = 0
|
|
|
fail_count = 0
|
|
|
- total_rows = len(df)
|
|
|
|
|
|
- for idx, row in df.iterrows():
|
|
|
- self.amazon_log(f"\n处理第 {idx+1}/{total_rows} 行")
|
|
|
+ for i, local_file in enumerate(image_files):
|
|
|
+ filename = os.path.basename(local_file)
|
|
|
|
|
|
- # 处理主图
|
|
|
- main_img_url = str(row[main_img_col_name]).strip()
|
|
|
- if main_img_url and main_img_url != 'nan' and main_img_url != 'None':
|
|
|
- if main_img_url.startswith('http'):
|
|
|
- try:
|
|
|
- resp = self.amazon_uploader.patch_image(token, "main", main_img_url, sku)
|
|
|
- if resp.status_code == 200:
|
|
|
- self.amazon_log(f" ✅ 主图上传成功")
|
|
|
- success_count += 1
|
|
|
- else:
|
|
|
- self.amazon_log(f" ❌ 主图上传失败: {resp.status_code}")
|
|
|
- fail_count += 1
|
|
|
- # 打印错误详情
|
|
|
- try:
|
|
|
- error_data = resp.json()
|
|
|
- self.amazon_log(f" 错误详情: {json.dumps(error_data, ensure_ascii=False)[:200]}")
|
|
|
- except:
|
|
|
- pass
|
|
|
- except Exception as e:
|
|
|
- self.amazon_log(f" ❌ 主图上传异常: {str(e)}")
|
|
|
- fail_count += 1
|
|
|
- else:
|
|
|
- self.amazon_log(f" ⚠️ 主图URL无效: {main_img_url[:50]}...")
|
|
|
+ # 确定图片类型
|
|
|
+ if i == 0:
|
|
|
+ image_type = "main"
|
|
|
+ type_name = "主图"
|
|
|
else:
|
|
|
- self.amazon_log(f" ⚠️ 主图为空,跳过")
|
|
|
+ image_type = f"other_{i}"
|
|
|
+ type_name = f"附图{i}"
|
|
|
|
|
|
- # 处理附图
|
|
|
- for i, col_name in enumerate(existing_other_cols, start=1):
|
|
|
- img_url = str(row[col_name]).strip()
|
|
|
- if img_url and img_url != 'nan' and img_url != 'None':
|
|
|
- if img_url.startswith('http'):
|
|
|
- try:
|
|
|
- image_type = f"other_{i}"
|
|
|
- resp = self.amazon_uploader.patch_image(token, image_type, img_url, sku)
|
|
|
- if resp.status_code == 200:
|
|
|
- self.amazon_log(f" ✅ 附图{i}上传成功")
|
|
|
- success_count += 1
|
|
|
- else:
|
|
|
- self.amazon_log(f" ❌ 附图{i}上传失败: {resp.status_code}")
|
|
|
- fail_count += 1
|
|
|
- except Exception as e:
|
|
|
- self.amazon_log(f" ❌ 附图{i}上传异常: {str(e)}")
|
|
|
- fail_count += 1
|
|
|
- else:
|
|
|
- self.amazon_log(f" ⚠️ 附图{i} URL无效: {img_url[:50]}...")
|
|
|
+ self.amazon_log(f"\n处理 {type_name}: {filename}")
|
|
|
+
|
|
|
+ # 上传到图片服务器获取URL
|
|
|
+ try:
|
|
|
+ self.amazon_log(f" 📤 上传到图片服务器...")
|
|
|
+ img_url = self.upload_image_to_cos(local_file)
|
|
|
+ self.amazon_log(f" ✅ 图片服务器上传成功: {img_url[:60]}...")
|
|
|
+ except Exception as e:
|
|
|
+ self.amazon_log(f" ❌ 图片服务器上传失败: {str(e)}")
|
|
|
+ fail_count += 1
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 调用亚马逊API更换图片
|
|
|
+ try:
|
|
|
+ self.amazon_log(f" 🔄 调用亚马逊API更换{type_name}...")
|
|
|
+ resp = self.amazon_uploader.patch_image(token, image_type, img_url, sku)
|
|
|
+ if resp.status_code == 200:
|
|
|
+ self.amazon_log(f" ✅ {type_name}更换成功")
|
|
|
+ success_count += 1
|
|
|
else:
|
|
|
- self.amazon_log(f" ⚠️ 附图{i}为空,跳过")
|
|
|
+ self.amazon_log(f" ❌ {type_name}更换失败: HTTP {resp.status_code}")
|
|
|
+ try:
|
|
|
+ error_data = resp.json()
|
|
|
+ self.amazon_log(f" 错误详情: {json.dumps(error_data, ensure_ascii=False)[:300]}")
|
|
|
+ except:
|
|
|
+ self.amazon_log(f" 响应: {resp.text[:300]}")
|
|
|
+ fail_count += 1
|
|
|
+ except Exception as e:
|
|
|
+ self.amazon_log(f" ❌ {type_name}更换异常: {str(e)}")
|
|
|
+ fail_count += 1
|
|
|
|
|
|
# 避免请求过快
|
|
|
time.sleep(0.5)
|
|
|
|
|
|
- self.amazon_log(f"\n✅ 处理完成!成功: {success_count}, 失败: {fail_count}")
|
|
|
+ self.amazon_log(f"\n{'=' * 50}")
|
|
|
+ self.amazon_log(f"✅ 处理完成!成功: {success_count}, 失败: {fail_count}")
|
|
|
self.amazon_progress_var.set(f"上传完成!成功: {success_count}, 失败: {fail_count}")
|
|
|
messagebox.showinfo("完成", f"亚马逊图片上传完成!\n成功: {success_count}\n失败: {fail_count}")
|
|
|
|
|
|
@@ -1175,19 +1151,39 @@ class ImageMatcherApp:
|
|
|
import traceback
|
|
|
self.amazon_log(traceback.format_exc())
|
|
|
|
|
|
+ # ==================== 亚马逊详情图更换 ====================
|
|
|
def start_detail_upload(self):
|
|
|
"""开始亚马逊详情图上传 (A+ Content)"""
|
|
|
- if not self.detail_template_path.get():
|
|
|
- messagebox.showerror("错误", "请先选择模板文件!")
|
|
|
- return
|
|
|
-
|
|
|
if not self.asin_input.get().strip():
|
|
|
messagebox.showerror("错误", "请先输入ASIN!")
|
|
|
return
|
|
|
+
|
|
|
+ if not self.detail_image_dir.get():
|
|
|
+ messagebox.showerror("错误", "请先选择图片文件夹!")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 保存配置
|
|
|
+ self.save_detail_config()
|
|
|
|
|
|
thread = threading.Thread(target=self.process_detail_upload, daemon=True)
|
|
|
thread.start()
|
|
|
|
|
|
+ def save_detail_config(self):
|
|
|
+ """保存详情图配置"""
|
|
|
+ config = {
|
|
|
+ "asin": self.asin_input.get(),
|
|
|
+ "headline": self.detail_headline.get(),
|
|
|
+ "body": self.detail_body.get(),
|
|
|
+ "image_width": self.image_width.get(),
|
|
|
+ "image_height": self.image_height.get(),
|
|
|
+ "image_dir": self.detail_image_dir.get(),
|
|
|
+ }
|
|
|
+ try:
|
|
|
+ with open(self.get_detail_config_path(), 'w', encoding='utf-8') as f:
|
|
|
+ json.dump(config, f, ensure_ascii=False, indent=2)
|
|
|
+ except Exception:
|
|
|
+ pass
|
|
|
+
|
|
|
def process_detail_upload(self):
|
|
|
"""执行亚马逊详情图上传 (A+ Content)"""
|
|
|
try:
|
|
|
@@ -1195,10 +1191,10 @@ class ImageMatcherApp:
|
|
|
self.detail_log("=" * 50)
|
|
|
self.detail_log("开始亚马逊详情图上传 (A+ Content)...")
|
|
|
|
|
|
- template_path = self.detail_template_path.get()
|
|
|
asin = self.asin_input.get().strip()
|
|
|
- detail_cols_str = self.detail_image_columns.get()
|
|
|
- detail_col_names = [col.strip() for col in detail_cols_str.split(',') if col.strip()]
|
|
|
+ headline = self.detail_headline.get().strip()
|
|
|
+ body_text = self.detail_body.get().strip()
|
|
|
+ image_dir = self.detail_image_dir.get()
|
|
|
|
|
|
# 获取图片尺寸
|
|
|
try:
|
|
|
@@ -1209,25 +1205,22 @@ class ImageMatcherApp:
|
|
|
self.detail_progress_var.set("上传失败")
|
|
|
return
|
|
|
|
|
|
- module_headline = self.module_headline.get().strip()
|
|
|
- module_body = self.module_body.get().strip()
|
|
|
-
|
|
|
- self.detail_log(f"读取模板文件: {template_path}")
|
|
|
- self.detail_log(f"使用的ASIN: {asin}")
|
|
|
+ self.detail_log(f"ASIN: {asin}")
|
|
|
+ self.detail_log(f"标题: {headline}")
|
|
|
+ self.detail_log(f"描述: {body_text}")
|
|
|
self.detail_log(f"图片尺寸: {target_width}x{target_height}")
|
|
|
+ self.detail_log(f"图片文件夹: {image_dir}")
|
|
|
|
|
|
- # 读取Excel
|
|
|
- df = pd.read_excel(template_path)
|
|
|
- self.detail_log(f"找到 {len(df)} 行数据")
|
|
|
-
|
|
|
- # 检查详情图列
|
|
|
- existing_detail_cols = [col for col in detail_col_names if col in df.columns]
|
|
|
- if not existing_detail_cols:
|
|
|
- self.detail_log("❌ 未找到任何详情图列")
|
|
|
+ # 获取图片文件
|
|
|
+ image_files = self.get_image_files(image_dir)
|
|
|
+ if not image_files:
|
|
|
+ self.detail_log("❌ 未找到任何图片文件")
|
|
|
self.detail_progress_var.set("上传失败")
|
|
|
return
|
|
|
|
|
|
- self.detail_log(f"详情图列: {existing_detail_cols}")
|
|
|
+ self.detail_log(f"\n找到 {len(image_files)} 张图片")
|
|
|
+ for i, f in enumerate(image_files, start=1):
|
|
|
+ self.detail_log(f" {i}. {os.path.basename(f)}")
|
|
|
|
|
|
# 尝试导入PIL
|
|
|
try:
|
|
|
@@ -1239,7 +1232,7 @@ class ImageMatcherApp:
|
|
|
self.detail_log("⚠️ 未安装Pillow,将使用原始图片尺寸")
|
|
|
|
|
|
# 获取Access Token
|
|
|
- self.detail_log("🔑 获取Access Token...")
|
|
|
+ self.detail_log("\n🔑 获取Access Token...")
|
|
|
try:
|
|
|
token = self.amazon_uploader.get_access_token()
|
|
|
self.detail_log("✅ Token获取成功")
|
|
|
@@ -1248,39 +1241,19 @@ class ImageMatcherApp:
|
|
|
self.detail_progress_var.set("上传失败")
|
|
|
return
|
|
|
|
|
|
- # 处理每一行
|
|
|
+ # 处理每张图片
|
|
|
success_count = 0
|
|
|
fail_count = 0
|
|
|
- total_rows = min(len(df), 1) # 每次只处理一行(A+ Content每次只能绑定一个ASIN)
|
|
|
-
|
|
|
- # 只处理第一行
|
|
|
- row = df.iloc[0]
|
|
|
-
|
|
|
- # 收集所有图片URL
|
|
|
- image_urls = []
|
|
|
- for col_name in existing_detail_cols:
|
|
|
- img_url = str(row[col_name]).strip()
|
|
|
- if img_url and img_url != 'nan' and img_url != 'None' and img_url.startswith('http'):
|
|
|
- image_urls.append(img_url)
|
|
|
-
|
|
|
- if not image_urls:
|
|
|
- self.detail_log("❌ 未找到有效的图片URL")
|
|
|
- self.detail_progress_var.set("上传失败")
|
|
|
- return
|
|
|
-
|
|
|
- self.detail_log(f"找到 {len(image_urls)} 张详情图")
|
|
|
-
|
|
|
- # 处理每张图片
|
|
|
upload_ids = []
|
|
|
- for i, img_url in enumerate(image_urls, start=1):
|
|
|
- self.detail_log(f"\n处理第 {i} 张图片: {img_url[:60]}...")
|
|
|
+
|
|
|
+ for i, local_file in enumerate(image_files, start=1):
|
|
|
+ filename = os.path.basename(local_file)
|
|
|
+ self.detail_log(f"\n处理第 {i}/{len(image_files)} 张图片: {filename}")
|
|
|
|
|
|
try:
|
|
|
- # 下载图片
|
|
|
- img_resp = requests.get(img_url, timeout=30)
|
|
|
- img_resp.raise_for_status()
|
|
|
-
|
|
|
- image_binary = img_resp.content
|
|
|
+ # 读取图片
|
|
|
+ with open(local_file, 'rb') as f:
|
|
|
+ image_binary = f.read()
|
|
|
|
|
|
# 调整尺寸
|
|
|
if HAS_PIL:
|
|
|
@@ -1291,7 +1264,7 @@ class ImageMatcherApp:
|
|
|
img_resized = img.resize((target_width, target_height), Image.LANCZOS)
|
|
|
buffer = BytesIO()
|
|
|
# 判断原图格式
|
|
|
- if img_url.lower().endswith('.png'):
|
|
|
+ if filename.lower().endswith('.png'):
|
|
|
img_resized.save(buffer, format="PNG")
|
|
|
else:
|
|
|
img_resized.save(buffer, format="JPEG")
|
|
|
@@ -1306,7 +1279,7 @@ class ImageMatcherApp:
|
|
|
|
|
|
# 确定Content-Type
|
|
|
content_type = "image/jpeg"
|
|
|
- if img_url.lower().endswith('.png'):
|
|
|
+ if filename.lower().endswith('.png'):
|
|
|
content_type = "image/png"
|
|
|
|
|
|
self.detail_log(f" 图片大小: {len(image_binary)} bytes")
|
|
|
@@ -1347,7 +1320,7 @@ class ImageMatcherApp:
|
|
|
return
|
|
|
|
|
|
# 构建A+ Content内容文档
|
|
|
- self.detail_log("\n📝 构建A+ Content文档...")
|
|
|
+ self.detail_log(f"\n📝 构建A+ Content文档 ({len(upload_ids)} 个模块)...")
|
|
|
|
|
|
# 构建模块列表 - 每张图片一个模块
|
|
|
content_module_list = []
|
|
|
@@ -1356,7 +1329,7 @@ class ImageMatcherApp:
|
|
|
"contentModuleType": "STANDARD_HEADER_IMAGE_TEXT",
|
|
|
"standardHeaderImageText": {
|
|
|
"headline": {
|
|
|
- "value": module_headline if i == 0 else f"{module_headline} - {i+1}",
|
|
|
+ "value": headline if i == 0 else f"{headline} - {i+1}",
|
|
|
"decoratorSet": []
|
|
|
},
|
|
|
"block": {
|
|
|
@@ -1381,7 +1354,7 @@ class ImageMatcherApp:
|
|
|
"body": {
|
|
|
"textList": [
|
|
|
{
|
|
|
- "value": module_body if i == 0 else f"Product detail view {i+1}",
|
|
|
+ "value": body_text if i == 0 else f"Product detail view {i+1}",
|
|
|
"decoratorSet": []
|
|
|
}
|
|
|
]
|
|
|
@@ -1430,7 +1403,8 @@ class ImageMatcherApp:
|
|
|
self.detail_progress_var.set("上传失败")
|
|
|
return
|
|
|
|
|
|
- self.detail_log(f"\n✅ 处理完成!成功: {success_count}, 失败: {fail_count}")
|
|
|
+ self.detail_log(f"\n{'=' * 50}")
|
|
|
+ self.detail_log(f"✅ 处理完成!成功: {success_count}, 失败: {fail_count}")
|
|
|
self.detail_log(f" Content Key: {content_key}")
|
|
|
self.detail_log(f" ASIN: {asin}")
|
|
|
self.detail_progress_var.set(f"上传完成!成功: {success_count}, 失败: {fail_count}")
|