Convert to native Tkinter GUI: add mac_native.py and update setup.py/README

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Pat TeruelandCopilot committed 2026-03-01 10:31:46 +08:00
1 parent c77c1a6f6f
commit 4de3064dd1
3 files changed
+166 -3

No files matched your search

+163
View File
@@ -0,0 +1,163 @@
#!/usr/bin/env python3
"""
Native GUI using tkinter to generate One Piece progress reports without a web server.
"""
import os
import threading
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
from datetime import datetime
# Reuse helper logic from app.py
from app import current_week, format_date_range, get_latest_sunday, get_previous_sunday
from clients import jellyfin
def generate_report(ui):
def task():
ui['status_var'].set("Fetching...")
jellyfin.clear_debug_logs()
try:
latest = get_latest_sunday()
previous = get_previous_sunday()
latest_dt = datetime.fromisoformat(latest.replace("Z", "+00:00"))
previous_dt = datetime.fromisoformat(previous.replace("Z", "+00:00"))
user_id = os.environ.get("JELLYFIN_USER_ID")
if not user_id:
raise RuntimeError("JELLYFIN_USER_ID not set in environment")
ep_info = jellyfin.find_last_one_piece_episode(latest_dt, user_id)
prev_ep_info = jellyfin.find_last_one_piece_before_previous_sunday(previous_dt, user_id)
previous_episode = prev_ep_info["episode_number"]
total_episodes = int(os.environ.get("TOTAL_EPISODES", 1155))
watched_this_week = ep_info["episode_number"] - previous_episode
week_start, week_end, _ = current_week()
date_range = format_date_range(week_start, week_end)
report_text = (
f"ONE PIECE PROGRESS!!! ({date_range})\n"
f"Episode {ep_info['episode_number']}/{total_episodes}\n"
f"Around {watched_this_week} episodes watched this week!"
)
debug_output = jellyfin.get_debug_logs()
ui['root'].after(0, lambda: update_ui(ui, ep_info, previous_episode, watched_this_week, total_episodes, report_text, debug_output))
except Exception as e:
debug_output = jellyfin.get_debug_logs()
ui['root'].after(0, lambda: ui['status_var'].set(f"Error: {e}\n{debug_output}"))
threading.Thread(target=task, daemon=True).start()
def update_ui(ui, ep_info, previous_episode, watched_this_week, total_episodes, report_text, debug_output):
ui['latest_var'].set(str(ep_info.get("episode_number", "—")))
ui['previous_var'].set(str(previous_episode))
ui['watched_var'].set(str(watched_this_week))
ui['total_var'].set(str(total_episodes))
ui['report_box'].config(state='normal')
ui['report_box'].delete('1.0', tk.END)
ui['report_box'].insert(tk.END, report_text)
ui['report_box'].config(state='disabled')
ui['debug_box'].config(state='normal')
ui['debug_box'].delete('1.0', tk.END)
ui['debug_box'].insert(tk.END, debug_output)
ui['debug_box'].config(state='disabled')
ui['status_var'].set("Done")
def copy_report(ui):
report = ui['report_box'].get('1.0', tk.END).strip()
if not report:
messagebox.showinfo("Copy", "No report to copy.")
return
ui['root'].clipboard_clear()
ui['root'].clipboard_append(report)
messagebox.showinfo("Copy", "Report copied to clipboard.")
def build_ui(root):
root.title("One Piece Progress Reporter")
root.geometry("900x650")
main = ttk.Frame(root, padding=12)
main.pack(fill='both', expand=True)
week_frame = ttk.Frame(main)
week_frame.pack(fill='x', pady=(0,8))
week_start, week_end, _ = current_week()
date_range = format_date_range(week_start, week_end)
week_label = ttk.Label(week_frame, text=f"Week: {date_range} ({week_start.isoformat()} → {week_end.isoformat()})", font=("Helvetica", 12))
week_label.pack(anchor='w')
info_frame = ttk.Frame(main)
info_frame.pack(fill='x', pady=(6,6))
ttk.Label(info_frame, text="Last episode watched:").grid(row=0, column=0, sticky='w', padx=4, pady=2)
latest_var = tk.StringVar(value="—")
ttk.Label(info_frame, textvariable=latest_var).grid(row=0, column=1, sticky='w', padx=4)
ttk.Label(info_frame, text="Previous week ended at:").grid(row=1, column=0, sticky='w', padx=4, pady=2)
previous_var = tk.StringVar(value="—")
ttk.Label(info_frame, textvariable=previous_var).grid(row=1, column=1, sticky='w', padx=4)
ttk.Label(info_frame, text="Watched this week:").grid(row=2, column=0, sticky='w', padx=4, pady=2)
watched_var = tk.StringVar(value="—")
ttk.Label(info_frame, textvariable=watched_var).grid(row=2, column=1, sticky='w', padx=4)
ttk.Label(info_frame, text="Total in series:").grid(row=3, column=0, sticky='w', padx=4, pady=2)
total_var = tk.StringVar(value="—")
ttk.Label(info_frame, textvariable=total_var).grid(row=3, column=1, sticky='w', padx=4)
btn_frame = ttk.Frame(info_frame)
btn_frame.grid(row=0, column=2, rowspan=4, padx=18)
status_var = tk.StringVar(value="")
generate_btn = ttk.Button(btn_frame, text="Generate Report")
generate_btn.pack(fill='x', pady=(0,6))
copy_btn = ttk.Button(btn_frame, text="Copy to Clipboard")
copy_btn.pack(fill='x')
report_label = ttk.Label(main, text="Report:")
report_label.pack(anchor='w', pady=(8,0))
report_box = scrolledtext.ScrolledText(main, height=6, wrap='word', state='disabled')
report_box.pack(fill='both', expand=False, pady=(4,6))
debug_label = ttk.Label(main, text="Debug:")
debug_label.pack(anchor='w')
debug_box = scrolledtext.ScrolledText(main, height=12, wrap='word', state='disabled')
debug_box.pack(fill='both', expand=True, pady=(4,0))
status = ttk.Label(root, textvariable=status_var, relief='sunken', anchor='w')
status.pack(side='bottom', fill='x')
ui = {
'root': root,
'latest_var': latest_var,
'previous_var': previous_var,
'watched_var': watched_var,
'total_var': total_var,
'status_var': status_var,
'report_box': report_box,
'debug_box': debug_box,
}
generate_btn.config(command=lambda: generate_report(ui))
copy_btn.config(command=lambda: copy_report(ui))
debug_box.config(state='normal')
debug_box.insert(tk.END, f"LATEST_SUNDAY={get_latest_sunday()}\nPREVIOUS_SUNDAY={get_previous_sunday()}\n")
debug_box.config(state='disabled')
return ui
def main():
root = tk.Tk()
ui = build_ui(root)
root.mainloop()
if __name__ == '__main__':
main()