ソースを参照

Created a tool for the correct naming and destination of all figures

Fabian Schöneck 10 ヶ月 前
コミット
05f0d03b8c
3 ファイル変更204 行追加1 行削除
  1. 4 1
      README.md
  2. 198 0
      figures_name_tool.pyw
  3. 2 0
      requirements.txt

+ 4 - 1
README.md

@@ -10,4 +10,7 @@ Pull requests welcome!
 
 **Pull Requests**
 
-If you want to add new Tonies, it will help to run `validation.sh`. If it detects something wrong with any of the files, it will tell you, and you can fix it before submitting the PR. Otherwise it will output no message at all. I will also run this from time to time and clean up files as needed.
+If you want to add new Tonies, it will help to run `validation.sh`. If it detects something wrong with any of the files, it will tell you, and you can fix it before submitting the PR. Otherwise it will output no message at all. I will also run this from time to time and clean up files as needed.
+
+You can use the `figures_name_tool.py` to generate the correct name for your file and place them in the correct directory.
+To use this tool you need to have python installed and install all requirements with `pip install -r requirements.txt`

+ 198 - 0
figures_name_tool.pyw

@@ -0,0 +1,198 @@
+import tkinter as tk
+from tkinter import ttk, filedialog, Canvas, Frame, Toplevel, messagebox
+from PIL import Image, ImageTk
+from collections import Counter
+import requests
+from io import BytesIO
+import re
+import os
+import shutil
+
+LANGUAGE_MAPPING = {
+    "de-de": "German",
+    "en-gb": "English",
+    "en-us": "English",
+    "fr-fr": "French"
+}
+
+# Max Image Size
+MAX_WIDTH = 100
+MAX_HEIGHT = 100
+COLS = 3  # Count of columns
+file_path = ""
+
+# GUI erstellen
+root = tk.Tk()
+root.title("Tonie Name Tool")
+root.geometry("400x500") 
+
+def get_language_name(language_code):
+    """returns the folder name for the selected language"""
+    return LANGUAGE_MAPPING.get(language_code, language_code)  # default to the lang_code if no mapping exists
+
+def generate_valid_filename(name, max_length=255):
+    # remove invalid characters
+    name = re.sub(r'[<>:"/\\|?*\x00-\x1F]', "", name)
+    name = name.replace("ä", "ae").replace("ö", "oe").replace("ü", "ue").replace("ß", "ss") # replace umlauts
+    # remove leading/trailing whitespaces
+    name = name.strip()
+
+    # truncate to max_length
+    if len(name) > max_length:
+        name = name[:max_length]
+
+    return name
+
+def load_new_file():
+    """ fuction to load a new file """
+    global file_path
+    file_path = filedialog.askopenfilename(filetypes=[("NFC-File", "*.nfc")])
+    if not file_path:
+        return  # No file selected
+
+    # Update the dropdowns
+    choose_language_show_series()
+
+def choose_language_show_series(*args):
+    """ function to show the series of the selected language """
+    lang = selected_language.get()
+    language_series = [entry for entry in data if entry.get("language") == lang and entry.get("hash")]
+    unique_series = sorted(set(entry.get("series") for entry in language_series), reverse=False)
+    dropdown_series["values"] = unique_series
+    dropdown_series["state"] = "readonly" if unique_series else "disabled"
+    selected_series.set("")  # reset the series
+
+def show_episode_details(entry):
+    """ shows the choosen episode in a detail window """
+    details_window = Toplevel(root)
+    details_window.title("Episode details")
+
+    # title
+    tk.Label(details_window, text=entry["episodes"], font=("Arial", 14, "bold")).pack(pady=10)
+
+    # load image from url
+    try:
+        response = requests.get(entry["pic"])
+        img_data = Image.open(BytesIO(response.content))
+
+        # scale image to fit into the window
+        img_data.thumbnail((200, 200))
+        img = ImageTk.PhotoImage(img_data)
+
+        img_label = tk.Label(details_window, image=img)
+        img_label.image = img
+        img_label.pack(pady=10)
+    except Exception as e:
+        print(f"Error while loading the image {e}")
+
+    def on_ok():
+        """ function to copy the file to the selected folder """
+        global file_path
+        lang = selected_language.get()
+        series = selected_series.get()
+        episode = entry["episodes"]
+        selected_data = [entry for entry in data if entry["language"] == lang and entry["series"] == series and entry["episodes"] == episode]
+        #audio_id = [entry["audio_id"] for entry in selected_data]
+        language_folder = generate_valid_filename(get_language_name(lang))
+        series_folder = generate_valid_filename(series)
+        file_name = generate_valid_filename(episode)
+        details_window.destroy()  # close the window
+        destination_path = os.path.join(language_folder, series_folder,)
+        os.makedirs(destination_path, exist_ok=True) # create the folder if it does not exist
+        shutil.copy(file_path, os.path.join(destination_path, file_name + ".nfc"))
+        messagebox.showinfo("Selection confirmed", f"The Tonie is stored under {language_folder}/{series_folder}/{file_name}")
+
+    # Add OK-Button
+    tk.Button(details_window, text="OK", command=on_ok).pack(pady=10)
+    # Add Close-Button
+    tk.Button(details_window, text="Close", command=details_window.destroy).pack(pady=5)
+
+def choose_series_show_episodes(*args):
+    """ function to show the episodes of the selected series """
+    lang = selected_language.get()
+    series = selected_series.get()
+    
+    # reset the scroll frame
+    for widget in scroll_frame.winfo_children():
+        widget.destroy()
+    
+    # filter the data
+    filtered_episodes = [entry for entry in data if entry["language"] == lang and entry["series"] == series and entry["hash"]] 
+    
+    # create the grid
+    row , col = 0, 0
+    for entry in filtered_episodes:
+        # load the image from the url
+        response = requests.get(entry["pic"])
+        img_data = Image.open(BytesIO(response.content))
+        
+        # scale image
+        img_data.thumbnail((MAX_WIDTH, MAX_HEIGHT))  # Automatically scale to fit the frame
+        img = ImageTk.PhotoImage(img_data)
+        
+        # Show Image
+        # Image-Button with function
+        img_button = tk.Button(scroll_frame, image=img, command=lambda e=entry: show_episode_details(e))
+        img_button.image = img
+        img_button.grid(row=row, column=col, padx=10, pady=10)
+        
+        # episode-title
+        episode_label = tk.Label(scroll_frame, text=entry["episodes"], wraplength=100, justify="center")
+        episode_label.grid(row=row + 1, column=col, padx=10, pady=5)
+        
+        col += 1
+        if col >= COLS:  # Max 3 Columns
+            col = 0
+            row += 2
+    
+    # Scrollbar update
+    scroll_frame.update_idletasks()
+    canvas.config(scrollregion=canvas.bbox("all"))
+
+# get the full data from the json file
+url="https://raw.githubusercontent.com/toniebox-reverse-engineering/tonies-json/release/tonies.json"
+try:
+    response = requests.get(url, timeout=10)
+    response.raise_for_status()
+    data = response.json()
+except requests.RequestException as e:
+    print(f"Error while loading the data: {e}")
+    data = []
+
+# filter the data by all languages containing figures
+languages = [entry.get("language") for entry in data if entry.get("hash")]
+filtered_languages = [lang for lang in languages if isinstance(lang, str)]
+language_counts = Counter(filtered_languages)
+sorted_languages = sorted(language_counts.keys(), key=lambda x: language_counts[x], reverse=True)
+
+# Buttons and Dropdowns
+tk.Button(root, text="Choose new file", command=load_new_file).pack(pady=10)
+
+# Dropdown language
+tk.Label(root, text="Choose a language:").pack(pady=5)
+selected_language = tk.StringVar()
+dropdown_language = ttk.Combobox(root, textvariable=selected_language, values=sorted_languages, state="readonly", width=50)
+dropdown_language.pack(pady=5)
+
+# Dropdown series
+tk.Label(root, text="Choose a series:").pack(pady=5)
+selected_series = tk.StringVar()
+dropdown_series = ttk.Combobox(root, textvariable=selected_series, state="disabled", width=50)
+dropdown_series.pack(pady=5)
+
+# Frame for Scrollbar
+canvas = Canvas(root)
+scroll_frame = Frame(canvas)
+scrollbar = ttk.Scrollbar(root, orient="vertical", command=canvas.yview)
+canvas.configure(yscrollcommand=scrollbar.set)
+
+scrollbar.pack(side="right", fill="y")
+canvas.pack(side="left", fill="both", expand=True)
+canvas.create_window((0, 0), window=scroll_frame, anchor="nw")
+
+# Event-listener for the language change
+selected_language.trace_add("write", choose_language_show_series)
+# Event-listener for the series change
+selected_series.trace_add("write", choose_series_show_episodes)
+
+root.mainloop()

+ 2 - 0
requirements.txt

@@ -0,0 +1,2 @@
+Pillow
+requests