Skip to content

Export Data to DailyRoutines-导出数据到DailyRoutines #9

@LittleNightmare

Description

@LittleNightmare

近期,DailyRoutines的新模块可以支持好友列表显示备注,由于我懒,简单用AI搞了一个导出工具,可以将UsedName的数据导出到DailyRoutines。代码和exe都放在这里

Recently, the new module of DailyRoutines supports displaying nickname for friend lists. Since I'm lazy, I simply used AI to create an export tool that can export UsedName data to DailyRoutines. The code and the exe are placed here.

下载exe/Download exe

import json
import os
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import string
import locale

# 语言配置
translations = {
    "en": {
        "title": "UsedName to DailyRoutines",
        "file_paths": "File Paths",
        "browse": "Browse...",
        "user_list": "User List",
        "id": "ID",
        "current_name": "Current Name",
        "nick_name": "Nickname",
        "used_names": "Used Names",
        "filter": "Filter:",
        "refresh": "Refresh",
        "import_selected": "Import Selected",
        "import_all": "Import All",
        "save": "Save",
        "warning": "Warning",
        "file_not_exist": "File does not exist: ",
        "cannot_create_dir": "Cannot create directory: ",
        "notice": "Notice",
        "will_create_file": "Will create new file: ",
        "error": "Error",
        "error_loading": "Error loading data: ",
        "select_users": "Please select users to import",
        "imported_count": "Successfully imported {0} users",
        "no_import": "No user imported",
        "imported_all": "Successfully imported all users ({0})",
        "save_success": "Data successfully saved to ",
        "save_error": "Error saving data: ",
        "language": "Language",
        "chinese": "中文",
        "english": "English"
    },
    "zh": {
        "title": "UsedName to DailyRoutines",
        "file_paths": "文件路径",
        "browse": "浏览...",
        "user_list": "用户列表",
        "id": "ID",
        "current_name": "当前名称",
        "nick_name": "昵称",
        "used_names": "历史名称",
        "filter": "筛选:",
        "refresh": "刷新",
        "import_selected": "导入选中",
        "import_all": "导入全部",
        "save": "保存",
        "warning": "警告",
        "file_not_exist": "文件不存在: ",
        "cannot_create_dir": "无法创建目录: ",
        "notice": "提示",
        "will_create_file": "将创建新文件: ",
        "error": "错误",
        "error_loading": "加载数据时出错: ",
        "select_users": "请先选择要导入的用户",
        "imported_count": "已成功导入 {0} 个用户",
        "no_import": "没有导入任何用户",
        "imported_all": "已成功导入所有用户 ({0} 个)",
        "save_success": "数据已成功保存到 ",
        "save_error": "保存数据时出错: ",
        "language": "语言",
        "chinese": "中文",
        "english": "English"
    }
}

class UsernameImportTool:
    def __init__(self, root):
        self.root = root
        
        # 设置语言
        self.detect_language()
        
        self.root.title(self.translate("title"))
        self.root.geometry("700x600")
        
        # 获取默认路径
        self.appdata_path = os.path.expandvars("%APPDATA%")
        
        # 默认路径列表
        self.default_store_paths = [
            os.path.join(self.appdata_path, "XIVLauncherCN", "pluginConfigs", "UsedName", "storeNames.json"),
            os.path.join(self.appdata_path, "XIVLauncher", "pluginConfigs", "UsedName", "storeNames.json"),
            "storeNames.json"
        ]
        
        self.default_friend_paths = [
            os.path.join(self.appdata_path, "XIVLauncherCN", "pluginConfigs", "DailyRoutines", "FriendListRemarks.json"),
            os.path.join(self.appdata_path, "XIVLauncher", "pluginConfigs", "DailyRoutines", "FriendListRemarks.json"),
            "FriendListRemarks.json"
        ]
        
        # 自动查找存在的文件
        self.store_names_path = self.find_existing_file(self.default_store_paths)
        self.friend_list_path = self.find_existing_file(self.default_friend_paths)
        
        # 检查DailyRoutines配置文件和文件夹
        self.check_dailyroutines_config()
        
        self.store_data = {}
        self.friend_data = {}
        
        # 创建UI元素
        self.create_ui()
        
        # 更新UI文本
        self.update_ui_texts()
        
        self.load_data()

    def detect_language(self):
        """检测系统语言并设置语言"""
        try:
            # 设置locale为系统默认
            locale.setlocale(locale.LC_CTYPE, '')
            # 获取当前locale设置
            current_locale = locale.getlocale(locale.LC_CTYPE)
            
            # 从locale中提取语言信息
            if current_locale and current_locale[0]:
                # 对于Windows返回的格式如('Chinese (Simplified)_China', '936')
                locale_str = current_locale[0].lower()
                if "chinese" in locale_str:
                    self.language = "zh"
                else:
                    self.language = "en"
            else:
                # 尝试使用备用方法(虽然已弃用但在移除前仍可用)
                try:
                    lang, _ = locale.getdefaultlocale()
                    if lang and lang.startswith("zh"):
                        self.language = "zh"
                    else:
                        self.language = "en"
                except:
                    self.language = "en"  # 默认英文
        except Exception as e:
            print(f"语言检测错误: {e}")
            # 如果出现任何错误,默认使用英文
            self.language = "en"
    
    def translate(self, key, *args):
        """根据当前语言返回翻译文本"""
        return translations[self.language].get(key, key).format(*args)
    
    def find_existing_file(self, path_list):
        """查找列表中第一个存在的文件路径"""
        for path in path_list:
            if os.path.exists(path):
                return path
        # 如果都不存在,返回第一个路径
        return path_list[0]
    
    def check_dailyroutines_config(self):
        """检查DailyRoutines配置和文件夹,如需要则创建FriendListRemarks路径"""
        # 检查DailyRoutines.json是否存在
        dr_config_cn = os.path.join(self.appdata_path, "XIVLauncherCN", "pluginConfigs", "DailyRoutines.json")
        dr_config = os.path.join(self.appdata_path, "XIVLauncher", "pluginConfigs", "DailyRoutines.json")
        
        # 检查DailyRoutines文件夹是否存在
        dr_folder_cn = os.path.join(self.appdata_path, "XIVLauncherCN", "pluginConfigs", "DailyRoutines")
        dr_folder = os.path.join(self.appdata_path, "XIVLauncher", "pluginConfigs", "DailyRoutines")
        
        # 如果配置存在且文件夹也存在,则使用路径
        if os.path.exists(dr_config_cn) and os.path.exists(dr_folder_cn):
            self.friend_list_path = os.path.join(dr_folder_cn, "FriendListRemarks.json")
        elif os.path.exists(dr_config) and os.path.exists(dr_folder):
            self.friend_list_path = os.path.join(dr_folder, "FriendListRemarks.json")
    
    def create_ui(self):
        """创建UI元素结构,只在初始化时调用一次"""
        # 创建主框架
        self.main_frame = ttk.Frame(self.root, padding=10)
        self.main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 文件路径框架
        self.path_frame = ttk.LabelFrame(self.main_frame, text="", padding=5)
        self.path_frame.pack(fill=tk.X, pady=5)
        
        # storeNames.json 路径
        self.store_label = ttk.Label(self.path_frame, text="storeNames.json:")
        self.store_label.grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
        self.store_path_var = tk.StringVar(value=self.store_names_path)
        ttk.Entry(self.path_frame, textvariable=self.store_path_var, width=50).grid(row=0, column=1, padx=5, pady=5)
        self.browse_store_btn = ttk.Button(self.path_frame, text="", command=lambda: self.browse_file("store"))
        self.browse_store_btn.grid(row=0, column=2, padx=5, pady=5)
        
        # FriendListRemarks.json 路径
        self.friend_label = ttk.Label(self.path_frame, text="FriendListRemarks.json:")
        self.friend_label.grid(row=1, column=0, sticky=tk.W, padx=5, pady=5)
        self.friend_path_var = tk.StringVar(value=self.friend_list_path)
        ttk.Entry(self.path_frame, textvariable=self.friend_path_var, width=50).grid(row=1, column=1, padx=5, pady=5)
        self.browse_friend_btn = ttk.Button(self.path_frame, text="", command=lambda: self.browse_file("friend"))
        self.browse_friend_btn.grid(row=1, column=2, padx=5, pady=5)
        
        # 语言切换框架
        self.lang_frame = ttk.Frame(self.main_frame)
        self.lang_frame.pack(fill=tk.X, pady=5)
        
        self.lang_label = ttk.Label(self.lang_frame, text="")
        self.lang_label.pack(side=tk.LEFT, padx=5)
        self.lang_var = tk.StringVar(value=self.language)
        self.lang_menu = ttk.OptionMenu(self.lang_frame, self.lang_var, self.language, "zh", "en", command=self.change_language)
        self.lang_menu.pack(side=tk.LEFT, padx=5)
        
        # 用户列表框架
        self.list_frame = ttk.LabelFrame(self.main_frame, text="", padding=5)
        self.list_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        # 创建树状视图
        columns = ("id", "current_name", "nick_name", "used_names")
        self.tree = ttk.Treeview(self.list_frame, columns=columns, show="headings", selectmode="extended")
        
        # 设置列宽度
        self.tree.column("id", width=150)
        self.tree.column("current_name", width=150)
        self.tree.column("nick_name", width=150)
        self.tree.column("used_names", width=200)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(self.list_frame, orient=tk.VERTICAL, command=self.tree.yview)
        self.tree.configure(yscrollcommand=scrollbar.set)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 筛选框架
        self.filter_frame = ttk.Frame(self.main_frame)
        self.filter_frame.pack(fill=tk.X, pady=5)
        
        self.filter_label = ttk.Label(self.filter_frame, text="")
        self.filter_label.pack(side=tk.LEFT, padx=5)
        self.filter_var = tk.StringVar()
        self.filter_entry = ttk.Entry(self.filter_frame, textvariable=self.filter_var, width=30)
        self.filter_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        self.filter_entry.bind("<KeyRelease>", self.filter_treeview)
        
        # 按钮框架
        self.button_frame = ttk.Frame(self.main_frame)
        self.button_frame.pack(fill=tk.X, pady=10)
        
        self.refresh_btn = ttk.Button(self.button_frame, text="", command=self.load_data)
        self.refresh_btn.pack(side=tk.LEFT, padx=5)
        self.import_selected_btn = ttk.Button(self.button_frame, text="", command=self.import_selected)
        self.import_selected_btn.pack(side=tk.LEFT, padx=5)
        self.import_all_btn = ttk.Button(self.button_frame, text="", command=self.import_all)
        self.import_all_btn.pack(side=tk.LEFT, padx=5)
        self.save_btn = ttk.Button(self.button_frame, text="", command=self.save_data)
        self.save_btn.pack(side=tk.RIGHT, padx=5)

    def update_ui_texts(self):
        """更新UI元素的文本,在语言切换时调用"""
        self.root.title(self.translate("title"))
        
        # 更新框架标题
        self.path_frame.configure(text=self.translate("file_paths"))
        self.list_frame.configure(text=self.translate("user_list"))
        
        # 更新按钮文本
        self.browse_store_btn.configure(text=self.translate("browse"))
        self.browse_friend_btn.configure(text=self.translate("browse"))
        self.refresh_btn.configure(text=self.translate("refresh"))
        self.import_selected_btn.configure(text=self.translate("import_selected"))
        self.import_all_btn.configure(text=self.translate("import_all"))
        self.save_btn.configure(text=self.translate("save"))
        
        # 更新标签文本
        self.lang_label.configure(text=self.translate("language"))
        self.filter_label.configure(text=self.translate("filter"))
        
        # 更新树状视图列标题
        self.tree.heading("id", text=self.translate("id"))
        self.tree.heading("current_name", text=self.translate("current_name"))
        self.tree.heading("nick_name", text=self.translate("nick_name"))
        self.tree.heading("used_names", text=self.translate("used_names"))
    
    def change_language(self, lang):
        """切换语言"""
        self.language = lang
        self.update_ui_texts()
        # 如果需要刷新数据视图
        self.update_treeview()
    
    def browse_file(self, file_type):
        filename = filedialog.askopenfilename(
            title=self.translate("browse"),
            filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
        )
        if filename:
            if file_type == "store":
                self.store_path_var.set(filename)
                self.store_names_path = filename
            else:
                self.friend_path_var.set(filename)
                self.friend_list_path = filename
            
            self.load_data()
    
    def load_data(self):
        try:
            # 读取 storeNames.json
            if os.path.exists(self.store_names_path):
                with open(self.store_names_path, 'r', encoding='utf-8-sig') as f:
                    self.store_data = json.load(f)
            else:
                self.store_data = {}
                messagebox.showwarning(self.translate("warning"), self.translate("file_not_exist") + self.store_names_path)
            
            # 读取 FriendListRemarks.json
            if os.path.exists(self.friend_list_path):
                with open(self.friend_list_path, 'r', encoding='utf-8') as f:
                    self.friend_data = json.load(f)
                if "PlayerInfos" not in self.friend_data:
                    self.friend_data["PlayerInfos"] = {}
            else:
                self.friend_data = {"PlayerInfos": {}}
                # 检查是否需要创建路径
                dir_path = os.path.dirname(self.friend_list_path)
                if not os.path.exists(dir_path):
                    try:
                        os.makedirs(dir_path)
                    except Exception as e:
                        messagebox.showwarning(self.translate("warning"), self.translate("cannot_create_dir") + dir_path + "\n" + str(e))
                
                messagebox.showinfo(self.translate("notice"), self.translate("will_create_file") + self.friend_list_path)
            
            # 更新树视图
            self.update_treeview()
        except Exception as e:
            messagebox.showerror(self.translate("error"), self.translate("error_loading") + str(e))
    
    def update_treeview(self):
        # 清空当前树视图
        for item in self.tree.get_children():
            self.tree.delete(item)
        
        # 填充树视图
        for player_id, data in self.store_data.items():
            current_name = data.get("currentName", "")
            nick_name = data.get("nickName", "")
            used_names = ", ".join(filter(None, data.get("usedNames", [])))
            
            # 检查是否已经在 FriendListRemarks.json 中
            in_friend_list = player_id in self.friend_data.get("PlayerInfos", {})
            
            # 添加到树视图
            item_id = self.tree.insert("", tk.END, values=(player_id, current_name, nick_name, used_names))
            
            # 已导入的项目使用不同颜色标记
            if in_friend_list:
                self.tree.item(item_id, tags=("imported",))
        
        # 设置已导入项的标签颜色
        self.tree.tag_configure("imported", background="#e6f7ff")
    
    def filter_treeview(self, event=None):
        search_term = self.filter_var.get().lower()
        
        # 清空当前树视图
        for item in self.tree.get_children():
            self.tree.delete(item)
        
        # 填充树视图,应用筛选
        for player_id, data in self.store_data.items():
            current_name = data.get("currentName", "")
            nick_name = data.get("nickName", "")
            used_names = ", ".join(filter(None, data.get("usedNames", [])))
            
            # 检查是否匹配筛选条件
            if (search_term in player_id.lower() or
                search_term in current_name.lower() or
                search_term in nick_name.lower() or
                search_term in used_names.lower()):
                
                # 检查是否已经在 FriendListRemarks.json 中
                in_friend_list = player_id in self.friend_data.get("PlayerInfos", {})
                
                # 添加到树视图
                item_id = self.tree.insert("", tk.END, values=(player_id, current_name, nick_name, used_names))
                
                # 已导入的项目使用不同颜色标记
                if in_friend_list:
                    self.tree.item(item_id, tags=("imported",))
    
    def format_used_names_to_remark(self, used_names):
        """将曾用名格式化为备注内容,按照A, B, C...的格式"""
        if not used_names:
            return ""
            
        # 过滤掉空字符串
        used_names = list(filter(None, used_names))
        
        # 如果没有曾用名,返回空字符串
        if not used_names:
            return ""
            
        # 合并成一个字符串
        return ", ".join(used_names)
    
    def import_selected(self):
        selected_items = self.tree.selection()
        if not selected_items:
            messagebox.showinfo(self.translate("notice"), self.translate("select_users"))
            return
        
        imported_count = 0
        for item in selected_items:
            values = self.tree.item(item, "values")
            player_id = values[0]
            
            if player_id in self.store_data:
                data = self.store_data[player_id]
                
                # 格式化曾用名到备注
                used_names_remark = self.format_used_names_to_remark(data.get("usedNames", []))
                
                # 创建导入数据
                import_data = {
                    "ContentID": int(player_id),
                    "Name": data.get("currentName", ""),
                    "Nickname": data.get("nickName", ""),
                    "Remark": used_names_remark
                }
                
                # 更新数据
                self.friend_data["PlayerInfos"][player_id] = import_data
                imported_count += 1
        
        if imported_count > 0:
            messagebox.showinfo(self.translate("notice"), self.translate("imported_count", imported_count))
            self.update_treeview()
        else:
            messagebox.showinfo(self.translate("notice"), self.translate("no_import"))
    
    def import_all(self):
        imported_count = 0
        for player_id, data in self.store_data.items():
            # 格式化曾用名到备注
            used_names_remark = self.format_used_names_to_remark(data.get("usedNames", []))
            
            # 创建导入数据
            import_data = {
                "ContentID": int(player_id),
                "Name": data.get("currentName", ""),
                "Nickname": data.get("nickName", ""),
                "Remark": used_names_remark
            }
            
            # 更新数据
            self.friend_data["PlayerInfos"][player_id] = import_data
            imported_count += 1
        
        if imported_count > 0:
            messagebox.showinfo(self.translate("notice"), self.translate("imported_all", imported_count))
            self.update_treeview()
        else:
            messagebox.showinfo(self.translate("notice"), self.translate("no_import"))
    
    def save_data(self):
        try:
            # 确保目录存在
            dir_path = os.path.dirname(self.friend_list_path)
            if not os.path.exists(dir_path):
                os.makedirs(dir_path)
                
            with open(self.friend_list_path, 'w', encoding='utf-8') as f:
                json.dump(self.friend_data, f, ensure_ascii=False, indent=2)
            messagebox.showinfo(self.translate("notice"), self.translate("save_success") + self.friend_list_path)
        except Exception as e:
            messagebox.showerror(self.translate("error"), self.translate("save_error") + str(e))


if __name__ == "__main__":
    root = tk.Tk()
    app = UsernameImportTool(root)
    root.mainloop()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions