Repository navigation
Expand file tree
/
Copy pathApp.py
More file actions
251 lines (209 loc) · 12.3 KB
/
Copy pathApp.py
File metadata and controls
251 lines (209 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
import tkinter as tk
from tkinter import ttk
import os
import zipfile
import logging
import xml.etree.ElementTree as ET
from wand.image import Image as MagickImage
# Configure logging utilities to write system report maps natively
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s [%(levelname)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
handlers=[
logging.FileHandler("manager.log", mode="w", encoding="utf-8"),
logging.StreamHandler()
]
)
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
from pygame import mixer
from grabTitles import fetch_title_database
def ensure_gametdb_database():
"""
Checks if wiitdb.xml exists locally.
If missing, it automatically downloads and extracts the archive from GameTDB.
"""
import requests
xml_path = "wiitdb.xml"
zip_path = "wiitdb.zip"
download_url = "https://gametdb.com"
if os.path.exists(xml_path):
logging.info("GameTDB XML database found locally. Skipping download check.")
return True
logging.info("Database file 'wiitdb.xml' is missing. Initiating automatic setup pipeline...")
try:
response = requests.get(download_url, timeout=10, stream=True)
if response.status_code == 200:
with open(zip_path, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
logging.info("Download completed successfully. Extracting workspace assets...")
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(".")
if os.path.exists(zip_path):
os.remove(zip_path)
logging.info("Database initialized! 'wiitdb.xml' is now ready for parsing operations.")
return True
else:
logging.error(f"Server rejected database download connection request. Status code: {response.status_code}")
except Exception as e:
logging.error(f"Critical failure while executing GameTDB setup pipeline: {e}", exc_info=True)
return False
def lookup_id6_from_xml(target_title_id):
"""
Parses wiitdb.xml to flawlessly translate a 16-character Title ID
into its exact corresponding GameTDB ID6 identifier code.
"""
xml_path = "wiitdb.xml"
if not os.path.exists(xml_path):
logging.warning("Warning: wiitdb.xml database missing from root directory.")
return None
try:
target_title_id = str(target_title_id).strip().upper()
tree = ET.parse(xml_path)
root = tree.getroot()
for game in root.findall('game'):
xml_title_id = game.find('titleid')
if xml_title_id is not None and xml_title_id.text.upper() == target_title_id:
id6_code = game.find('id')
if id6_code is not None:
return id6_code.text
except Exception as e:
logging.error(f"Error parsing GameTDB XML document file: {e}")
return None
class DesktopManagerApp:
def __init__(self, root):
logging.info("Application starting up...")
self.root = root
self.root.title("Generic Media & Library Manager")
self.root.geometry("950x650")
self.root.configure(bg="#141419")
# Pull GameTDB reference data before mapping interface tables
ensure_gametdb_database()
self.current_tab = "library"
self.current_cover = None
self.all_titles = []
self.init_audio_loop()
self.apply_retro_styling()
self.create_layout_skeleton()
self.load_initial_data()
def init_audio_loop(self):
try:
mixer.init()
audio_path = os.path.join("Images", "loop.ogg")
if os.path.exists(audio_path):
mixer.music.load(audio_path)
mixer.music.play(-1)
mixer.music.set_volume(0.4)
logging.info(f"Audio system initialized. Loaded loop asset: {audio_path}")
else:
logging.warning(f"Audio asset missing during track handoff at path: {audio_path}")
except Exception as e:
logging.error(f"Failed to initialize audio mixer engine hardware: {e}", exc_info=True)
def apply_retro_styling(self):
logging.debug("Applying interface grid style matrix variants...")
self.style = ttk.Style()
self.theme_name = "default"
self.style.theme_use(self.theme_name)
self.style.configure("Treeview", background="#1e1e24", foreground="#e0e0e0", rowheight=26, fieldbackground="#1e1e24", font=("Helvetica", 10))
self.style.map("Treeview", background=[('selected', '#e67e22')], foreground=[('selected', '#ffffff')])
self.style.configure("Treeview.Heading", background="#2c2c35", foreground="#ffffff", relief="flat", font=("Helvetica", 10, "bold"))
self.style.map("Treeview.Heading", background=[('active', '#3a3a45')])
def create_layout_skeleton(self):
logging.debug("Assembling structural geometry wrappers...")
self.master_frame = tk.Frame(self.root, bg="#141419")
self.master_frame.pack(fill="both", expand=True)
self.nav_frame = tk.Frame(self.master_frame, bg="#1a1a22", width=220)
self.nav_frame.pack(side="left", fill="y", padx=5, pady=5)
self.nav_frame.pack_propagate(False)
self.content_container = tk.Frame(self.master_frame, bg="#141419")
self.content_container.pack(side="right", fill="both", expand=True, padx=5, pady=5)
self.top_bar = tk.Frame(self.content_container, bg="#1a1a22", height=50)
self.top_bar.pack(side="top", fill="x", pady=(0, 5))
self.top_bar.pack_propagate(False)
search_lbl = tk.Label(self.top_bar, text="Search Library:", font=("Helvetica", 10, "bold"), fg="#e0e0e0", bg="#1a1a22")
search_lbl.pack(side="left", padx=(15, 10))
self.search_var = tk.StringVar()
self.search_var.trace_add("write", self.on_search_query_changed)
self.search_entry = tk.Entry(self.top_bar, textvariable=self.search_var, font=("Helvetica", 10), bg="#2e2e3a", fg="#ffffff", insertbackground="#ffffff", relief="flat", width=45)
self.search_entry.pack(side="left", padx=5, pady=12)
self.content_frame = tk.Frame(self.content_container, bg="#141419")
self.content_frame.pack(side="bottom", fill="both", expand=True)
self.cover_label = tk.Label(self.nav_frame, text="Select a Game", fg="#888899", bg="#111115", width=24, height=12, relief="solid", bd=1)
self.cover_label.pack(padx=15, pady=15, fill="x")
btn_config = {"font": ("Helvetica", 10, "bold"), "fg": "#ffffff", "bg": "#2e2e3a", "activebackground": "#e67e22", "activeforeground": "#ffffff", "relief": "flat"}
self.lib_btn = tk.Button(self.nav_frame, text="Library", command=self.show_library, **btn_config)
self.lib_btn.pack(fill="x", padx=15, pady=5)
self.settings_btn = tk.Button(self.nav_frame, text="Settings", command=self.show_settings, **btn_config)
self.settings_btn.pack(fill="x", padx=15, pady=5)
self.tree = ttk.Treeview(self.content_frame, columns=("Name", "ID", "Region", "Status"), show="headings")
self.tree.pack(fill="both", expand=True)
self.tree.bind("<<TreeviewSelect>>", self.on_game_selected)
self.tree.heading("Name", text="Game Name")
self.tree.heading("ID", text="Title ID")
self.tree.heading("Region", text="Region")
self.tree.heading("Status", text="Status")
self.tree.column("Name", width=380, anchor="w")
self.tree.column("ID", width=160, anchor="center")
self.tree.column("Region", width=70, anchor="center")
self.tree.column("Status", width=90, anchor="center")
def load_initial_data(self):
logging.info("Requesting dataset transfer from core data module...")
self.all_titles = fetch_title_database()
self.update_content_table(self.all_titles)
def update_content_table(self, title_dataset):
for i in self.tree.get_children():
self.tree.delete(i)
for item in title_dataset:
title_id = item.get("id", "")
name = item.get("name", "")
region = item.get("region", "")
status = "Ready"
self.tree.insert("", "end", values=(name, title_id, region, status))
logging.debug(f"Render engine processed and mapped {len(title_dataset)} visible entries.")
def on_search_query_changed(self, *args):
query = self.search_var.get().strip().lower()
if not query:
self.update_content_table(self.all_titles)
return
filtered_results = [
item for item in self.all_titles
if query in item.get("name", "").lower() or query in item.get("id", "").lower()
]
self.update_content_table(filtered_results)
def on_game_selected(self, event):
selected_items = self.tree.selection()
if not selected_items:
return
item_data = self.tree.item(selected_items)['values']
if len(item_data) < 3:
logging.warning("Row activation intercept dropped due to short data tuple columns length.")
return
game_name = str(item_data[0]).strip()
title_id = str(item_data[1]).strip().upper()
region = str(item_data[2]).strip().upper()
logging.info(f"User highlighted game row: '{game_name}' [ID: {title_id}]")
is_wiiu = title_id.startswith("00050000")
sub_folder = "wiiu" if is_wiiu else "3ds"
base_dir = os.path.join("Images", sub_folder)
os.makedirs(base_dir, exist_ok=True)
image_path = os.path.join(base_dir, f"{title_id}.png")
# --- GameTDB XML Hot-Download Pipeline ---
if not os.path.exists(image_path) and len(title_id) == 16:
import requests
id6_code = lookup_id6_from_xml(title_id)
if id6_code:
platform_path = "wiiu" if is_wiiu else "3ds"
url = f"gametdb.com{platform_path}/cover/3D/{id6_code}.png"
logging.info(f"Asset missing locally. Launching GameTDB XML fetch pipeline: {url}")
try:
response = requests.get(url, timeout=3)
if response.status_code == 200:
with open(image_path, "wb") as f:
f.write(response.content)
logging.info(f"Successfully cached downloaded GameTDB asset to storage:{image_path}")
else:
logging.warning(f"GameTDB network mirror rejected resource fetch request. Status:
{response.status_code}")
except Exception as e:
logging.error(f"Failed handling background dynamic asset conversion pipeline downloads: {e}")# --- UI Image Display Routing ---try:if os.path.exists(image_path):logging.info(f"Magick Target Hit! Streaming image payload from storage: '{image_path}'")with MagickImage(filename=image_path) as img:img.resize(160, 200)img.format = 'png'img_bytes = img.make_blob()self.current_cover = tk.PhotoImage(data=img_bytes)self.cover_label.config(image=self.current_cover, text="")else:logging.warning(f"File system lookup completed. File match missing for TitleID key: {title_id}")self.cover_label.config(image="", text=f"Local Asset Missing\nID: {title_id}")except Exception as e:logging.error(f"ImageMagick engine encountered a structural data processing layout crash: {e}", exc_info=True)self.cover_label.config(image="", text="Image Render Error")def show_library(self):logging.debug("Switching active viewport canvas: 'Library'")self.current_tab = "library"self.top_bar.pack(side="top", fill="x", pady=(0, 5))self.content_frame.pack(fill="both", expand=True)self.update_content_table(self.all_titles)def show_settings(self):logging.debug("Switching active viewport canvas: 'Settings'")self.current_tab = "settings"self.top_bar.pack_forget()self.content_frame.pack_forget()lbl = tk.Label(self.content_container, text="System Configurations Coming Soon", font=("Helvetica", 12), fg="#ffffff", bg="#141419")lbl.pack(pady=40)if name == "main":root = tk.Tk()app = DesktopManagerApp(root)root.mainloop()