htm-engine 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: htm-engine
3
+ Version: 1.0.0
4
+ Summary: Advanced HTML to EXE packaging suite
5
+ Author: YAVUX STUDIOS
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: pywebview
9
+ Requires-Dist: requests
10
+ Requires-Dist: pyinstaller
11
+ Dynamic: author
File without changes
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: htm-engine
3
+ Version: 1.0.0
4
+ Summary: Advanced HTML to EXE packaging suite
5
+ Author: YAVUX STUDIOS
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: pywebview
9
+ Requires-Dist: requests
10
+ Requires-Dist: pyinstaller
11
+ Dynamic: author
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ htm_engine.egg-info/PKG-INFO
5
+ htm_engine.egg-info/SOURCES.txt
6
+ htm_engine.egg-info/dependency_links.txt
7
+ htm_engine.egg-info/entry_points.txt
8
+ htm_engine.egg-info/requires.txt
9
+ htm_engine.egg-info/top_level.txt
10
+ htmengine/__init__.py
11
+ htmengine/__main__.py
12
+ htmengine/mainapp.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ htm-engine = htm_engine.main:main_func
@@ -0,0 +1,3 @@
1
+ pywebview
2
+ requests
3
+ pyinstaller
@@ -0,0 +1 @@
1
+ htmengine
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
@@ -0,0 +1,4 @@
1
+ from .mainapp import launch
2
+
3
+ if __name__ == "__main__":
4
+ launch()
@@ -0,0 +1,375 @@
1
+ import os
2
+ import sys
3
+ import subprocess
4
+ import threading
5
+ import json
6
+ import tkinter as tk
7
+ from tkinter import filedialog, messagebox, ttk
8
+ import re
9
+ import requests
10
+ import time
11
+ from urllib.parse import urljoin, urlparse
12
+
13
+ def get_path(rel):
14
+ if hasattr(sys, '_MEIPASS'):
15
+ return os.path.join(sys._MEIPASS, rel)
16
+ return os.path.join(os.path.abspath("."), rel)
17
+
18
+ class PackerGUI:
19
+ def __init__(self, root):
20
+ self.root = root
21
+ self.root.title("Htm Engine - Advanced Packaging Center")
22
+ self.root.geometry("700x950")
23
+ self.root.configure(bg="#0f0f0f")
24
+
25
+ # --- Variables ---
26
+ self.exe_name = tk.StringVar(value="HtmApp")
27
+ self.app_title = tk.StringVar(value="New Htm Engine Project")
28
+ self.src_dir = tk.StringVar(value="src")
29
+ self.icon_path = tk.StringVar()
30
+
31
+ self.version = tk.StringVar(value="1.0.0.0")
32
+ self.company_name = tk.StringVar(value="Htm Engine")
33
+ self.copyright = tk.StringVar(value="© 2024 Htm Engine")
34
+
35
+ self.screen_mode = tk.StringVar(value="resizable")
36
+ self.width = tk.StringVar(value="1280")
37
+ self.height = tk.StringVar(value="720")
38
+ self.topmost = tk.BooleanVar(value=False)
39
+ self.disable_right_click = tk.BooleanVar(value=True)
40
+
41
+ # --- Splash Screen Variables ---
42
+ self.use_splash = tk.BooleanVar(value=False)
43
+ self.splash_path = tk.StringVar()
44
+ self.splash_time = tk.StringVar(value="3") # Seconds
45
+
46
+ self.open_folder = tk.BooleanVar(value=True)
47
+ self.test_after_build = tk.BooleanVar(value=False)
48
+
49
+ self.create_widgets()
50
+
51
+ def create_widgets(self):
52
+ tk.Label(self.root, text="🛠️ HTM ENGINE PACKER", font=("Impact", 26), fg="#00ffcc", bg="#0f0f0f").pack(pady=10)
53
+
54
+ main_canvas = tk.Canvas(self.root, bg="#0f0f0f", highlightthickness=0)
55
+ scrollbar = ttk.Scrollbar(self.root, orient="vertical", command=main_canvas.yview)
56
+ self.scrollable_frame = tk.Frame(main_canvas, bg="#0f0f0f")
57
+
58
+ self.scrollable_frame.bind(
59
+ "<Configure>",
60
+ lambda e: main_canvas.configure(scrollregion=main_canvas.bbox("all"))
61
+ )
62
+
63
+ main_canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
64
+ main_canvas.configure(yscrollcommand=scrollbar.set)
65
+
66
+ main_canvas.pack(side="left", fill="both", expand=True, padx=20)
67
+ scrollbar.pack(side="right", fill="y")
68
+
69
+ # --- Section 1: Project Essentials ---
70
+ section1 = tk.LabelFrame(self.scrollable_frame, text=" Project Essentials ", fg="#00ffcc", bg="#0f0f0f", padx=10, pady=10)
71
+ section1.pack(fill="x", pady=5)
72
+
73
+ tk.Label(section1, text="Source Folder:", fg="#aaa", bg="#0f0f0f").grid(row=0, column=0, sticky="w")
74
+ tk.Entry(section1, textvariable=self.src_dir, bg="#1a1a1a", fg="white", width=35).grid(row=0, column=1, pady=5)
75
+ tk.Button(section1, text="Browse", command=self.select_src, bg="#333", fg="white").grid(row=0, column=2, padx=5)
76
+
77
+ tk.Label(section1, text="EXE Name:", fg="#aaa", bg="#0f0f0f").grid(row=1, column=0, sticky="w")
78
+ tk.Entry(section1, textvariable=self.exe_name, bg="#1a1a1a", fg="white", width=35).grid(row=1, column=1, pady=5)
79
+
80
+ tk.Label(section1, text="Game Title:", fg="#aaa", bg="#0f0f0f").grid(row=2, column=0, sticky="w")
81
+ tk.Entry(section1, textvariable=self.app_title, bg="#1a1a1a", fg="#00ff00", width=35).grid(row=2, column=1, pady=5)
82
+
83
+ # --- Section: Metadata ---
84
+ section_meta = tk.LabelFrame(self.scrollable_frame, text=" Metadata (EXE Details) ", fg="#00ffcc", bg="#0f0f0f", padx=10, pady=10)
85
+ section_meta.pack(fill="x", pady=5)
86
+
87
+ tk.Label(section_meta, text="Version (Ex: 1.0.0.0):", fg="#aaa", bg="#0f0f0f").grid(row=0, column=0, sticky="w")
88
+ tk.Entry(section_meta, textvariable=self.version, bg="#1a1a1a", fg="white").grid(row=0, column=1, sticky="w", pady=2)
89
+
90
+ tk.Label(section_meta, text="Company:", fg="#aaa", bg="#0f0f0f").grid(row=1, column=0, sticky="w")
91
+ tk.Entry(section_meta, textvariable=self.company_name, bg="#1a1a1a", fg="white").grid(row=1, column=1, sticky="w", pady=2)
92
+
93
+ tk.Label(section_meta, text="Copyright:", fg="#aaa", bg="#0f0f0f").grid(row=2, column=0, sticky="w")
94
+ tk.Entry(section_meta, textvariable=self.copyright, bg="#1a1a1a", fg="white").grid(row=2, column=1, sticky="w", pady=2)
95
+
96
+ # --- Section 2: Screen & Interface ---
97
+ section2 = tk.LabelFrame(self.scrollable_frame, text=" Display & Interface ", fg="#00ffcc", bg="#0f0f0f", padx=10, pady=10)
98
+ section2.pack(fill="x", pady=5)
99
+
100
+ modes = [("Fullscreen", "full"), ("Resizable", "resizable"), ("Fixed", "fixed")]
101
+ for i, (text, mode) in enumerate(modes):
102
+ tk.Radiobutton(section2, text=text, variable=self.screen_mode, value=mode, bg="#0f0f0f", fg="white", selectcolor="#444").grid(row=0, column=i, padx=5)
103
+
104
+ size_frame = tk.Frame(section2, bg="#0f0f0f")
105
+ size_frame.grid(row=1, column=0, columnspan=3, pady=10)
106
+ tk.Label(size_frame, text="Width:", fg="#888", bg="#0f0f0f").pack(side="left")
107
+ tk.Entry(size_frame, textvariable=self.width, width=6, bg="#1a1a1a", fg="white").pack(side="left", padx=5)
108
+ tk.Label(size_frame, text="Height:", fg="#888", bg="#0f0f0f").pack(side="left", padx=(10,0))
109
+ tk.Entry(size_frame, textvariable=self.height, width=6, bg="#1a1a1a", fg="white").pack(side="left", padx=5)
110
+
111
+ tk.Checkbutton(section2, text="Always on Top", variable=self.topmost, bg="#0f0f0f", fg="#aaa", selectcolor="#222").grid(row=2, column=0)
112
+ tk.Checkbutton(section2, text="Disable Right Click", variable=self.disable_right_click, bg="#0f0f0f", fg="#aaa", selectcolor="#222").grid(row=2, column=1)
113
+
114
+ # Splash Screen Settings
115
+ splash_frame = tk.LabelFrame(section2, text=" Splash Screen (Loading) ", fg="#ffa500", bg="#0f0f0f", padx=5, pady=5)
116
+ splash_frame.grid(row=3, column=0, columnspan=3, pady=10, sticky="ew")
117
+
118
+ tk.Checkbutton(splash_frame, text="Enable Splash", variable=self.use_splash, bg="#0f0f0f", fg="white", selectcolor="#222").grid(row=0, column=0, sticky="w")
119
+ tk.Button(splash_frame, text="Select Image", command=self.select_splash, bg="#333", fg="white", font=("Arial", 8)).grid(row=0, column=1, padx=5)
120
+ tk.Label(splash_frame, text="Duration(s):", fg="#aaa", bg="#0f0f0f").grid(row=0, column=2)
121
+ tk.Entry(splash_frame, textvariable=self.splash_time, width=3, bg="#1a1a1a", fg="white").grid(row=0, column=3, padx=5)
122
+
123
+ # --- Section 3: Build & Actions ---
124
+ section3 = tk.LabelFrame(self.scrollable_frame, text=" Build & Operations ", fg="#00ffcc", bg="#0f0f0f", padx=10, pady=10)
125
+ section3.pack(fill="x", pady=5)
126
+
127
+ tk.Checkbutton(section3, text="Open folder when finished", variable=self.open_folder, bg="#0f0f0f", fg="#aaa", selectcolor="#222").pack(anchor="w")
128
+ tk.Checkbutton(section3, text="Start test when finished", variable=self.test_after_build, bg="#0f0f0f", fg="#aaa", selectcolor="#222").pack(anchor="w")
129
+
130
+ btn_frame = tk.Frame(section3, bg="#0f0f0f")
131
+ btn_frame.pack(fill="x", pady=10)
132
+ tk.Button(btn_frame, text="Select Icon (.ico)", command=self.select_icon, bg="#333", fg="white").pack(side="left", fill="x", expand=True)
133
+ self.btn_build = tk.Button(btn_frame, text="🔥 COMPILE PROJECT (OFFLINE) 🔥", bg="#00ffcc", fg="black", font=("Arial", 12, "bold"), command=self.run_thread)
134
+ self.btn_build.pack(side="left", fill="x", expand=True, padx=5)
135
+
136
+ self.log_area = tk.Text(self.scrollable_frame, height=8, bg="black", fg="#00ff00", font=("Consolas", 9))
137
+ self.log_area.pack(fill="x", pady=5)
138
+
139
+ def select_src(self):
140
+ path = filedialog.askdirectory()
141
+ if path: self.src_dir.set(path)
142
+
143
+ def select_icon(self):
144
+ path = filedialog.askopenfilename(filetypes=[("Icon", "*.ico")])
145
+ if path: self.icon_path.set(path)
146
+
147
+ def select_splash(self):
148
+ path = filedialog.askopenfilename(filetypes=[("Image", "*.png *.jpg *.jpeg *.gif")])
149
+ if path: self.splash_path.set(path)
150
+
151
+ def log(self, msg):
152
+ self.log_area.insert("end", f">>> {msg}\n")
153
+ self.log_area.see("end")
154
+
155
+ def run_thread(self):
156
+ self.btn_build.config(state="disabled")
157
+ threading.Thread(target=self.start_build, daemon=True).start()
158
+
159
+ def make_assets_offline(self, src_dir):
160
+ index_path = os.path.join(src_dir, "index.html")
161
+ if not os.path.exists(index_path): return
162
+ libs_dir = os.path.join(src_dir, "offline_libs")
163
+ if not os.path.exists(libs_dir): os.makedirs(libs_dir)
164
+ with open(index_path, "r", encoding="utf-8") as f:
165
+ content = f.read()
166
+ urls = re.findall(r'src=["\'](http[s]?://.*?)["\']|href=["\'](http[s]?://.*?)["\']', content)
167
+ for src_url, href_url in urls:
168
+ url = src_url if src_url else href_url
169
+ if not url: continue
170
+ try:
171
+ filename = os.path.basename(urlparse(url).path)
172
+ if not filename: filename = "lib_" + str(hash(url))
173
+ local_path = os.path.join(libs_dir, filename)
174
+ self.log(f"Downloading: {filename}")
175
+ response = requests.get(url, timeout=10)
176
+ if response.status_code == 200:
177
+ with open(local_path, "wb") as f:
178
+ f.write(response.content)
179
+ rel_path = "offline_libs/" + filename
180
+ content = content.replace(url, rel_path)
181
+ except Exception as e:
182
+ self.log(f"Error (Download): {url} -> {e}")
183
+ with open(index_path, "w", encoding="utf-8") as f:
184
+ f.write(content)
185
+ self.log("All dependencies localized (Offline Mode).")
186
+
187
+ def create_version_file(self):
188
+ raw_version = self.version.get()
189
+ numbers = re.findall(r'\d+', raw_version)
190
+ while len(numbers) < 4: numbers.append('0')
191
+ ver_tuple = f"({','.join(numbers[:4])})"
192
+ content = f"""
193
+ VSVersionInfo(
194
+ ffi=FixedFileInfo(filevers={ver_tuple}, prodvers={ver_tuple}, mask=0x3f, flags=0x0, OS=0x40004, fileType=0x1, subtype=0x0, date=(0, 0)),
195
+ kids=[
196
+ StringFileInfo([
197
+ StringTable(u'040904B0',
198
+ [StringStruct(u'CompanyName', u'{self.company_name.get()}'),
199
+ StringStruct(u'FileDescription', u'{self.app_title.get()}'),
200
+ StringStruct(u'FileVersion', u'{raw_version}'),
201
+ StringStruct(u'InternalName', u'HtmEngine'),
202
+ StringStruct(u'LegalCopyright', u'{self.copyright.get()}'),
203
+ StringStruct(u'OriginalFilename', u'{self.exe_name.get()}.exe'),
204
+ StringStruct(u'ProductName', u'{self.app_title.get()}'),
205
+ StringStruct(u'ProductVersion', u'{raw_version}')])
206
+ ]), VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
207
+ ]
208
+ )"""
209
+ with open("version_info.txt", "w", encoding="utf-8") as f:
210
+ f.write(content)
211
+
212
+ def start_build(self):
213
+ try:
214
+ src = self.src_dir.get()
215
+ if not os.path.exists(os.path.join(src, "index.html")):
216
+ messagebox.showwarning("Warning", "index.html not found!")
217
+ return
218
+
219
+ self.log("Preparing offline assets...")
220
+ self.make_assets_offline(src)
221
+
222
+ splash_filename = ""
223
+ if self.use_splash.get() and self.splash_path.get():
224
+ import shutil
225
+ ext = os.path.splitext(self.splash_path.get())[1]
226
+ splash_filename = "splash" + ext
227
+ shutil.copy2(self.splash_path.get(), os.path.join(src, splash_filename))
228
+
229
+ config = {
230
+ "title": self.app_title.get(),
231
+ "mode": self.screen_mode.get(),
232
+ "width": int(self.width.get()),
233
+ "height": int(self.height.get()),
234
+ "topmost": self.topmost.get(),
235
+ "no_right_click": self.disable_right_click.get(),
236
+ "use_splash": self.use_splash.get(),
237
+ "splash_img": splash_filename,
238
+ "splash_time": int(self.splash_time.get())
239
+ }
240
+
241
+ with open(os.path.join(src, "config.json"), "w", encoding="utf-8") as f:
242
+ json.dump(config, f)
243
+
244
+ self.create_version_file()
245
+ self.log("Build process started...")
246
+
247
+ sep = ';' if os.name == 'nt' else ':'
248
+ cmd = ["pyinstaller", "--noconsole", "--onefile", "--clean",
249
+ f"--add-data={src}{sep}.",
250
+ "--name", self.exe_name.get(),
251
+ "--version-file", "version_info.txt"]
252
+
253
+ if self.icon_path.get(): cmd.extend(["--icon", self.icon_path.get()])
254
+ cmd.append(sys.argv[0])
255
+
256
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
257
+ for line in proc.stdout:
258
+ self.log(line.strip())
259
+ self.root.update_idletasks()
260
+
261
+ proc.wait()
262
+
263
+ if proc.returncode == 0:
264
+ self.log("✅ Build completed successfully!")
265
+ if self.open_folder.get(): os.startfile("dist")
266
+ if self.test_after_build.get():
267
+ subprocess.Popen([os.path.join("dist", f"{self.exe_name.get()}.exe")])
268
+ messagebox.showinfo("Htm Engine", "Packaging Successful!")
269
+ else:
270
+ self.log("❌ Compilation error!")
271
+
272
+ except Exception as e:
273
+ self.log(f"ERROR: {str(e)}")
274
+ finally:
275
+ self.btn_build.config(state="normal")
276
+
277
+ if __name__ == "__main__":
278
+ if hasattr(sys, '_MEIPASS'):
279
+ import webview
280
+ # --- TÜM SİSTEMİ KAPATAN FONKSİYON ---
281
+ def kill_everything():
282
+ print("System shutting down...")
283
+ os._exit(0) # Bu komut tüm threadleri ve ana süreci anında öldürür.
284
+
285
+ try:
286
+ conf_path = get_path("config.json")
287
+ index_path = get_path("index.html")
288
+
289
+ if not os.path.exists(conf_path) or not os.path.exists(index_path):
290
+ conf_path = get_path(os.path.join("src", "config.json"))
291
+ index_path = get_path(os.path.join("src", "index.html"))
292
+
293
+ with open(conf_path, "r", encoding="utf-8") as f:
294
+ c = json.load(f)
295
+
296
+ index_url = "file:///" + os.path.abspath(index_path).replace("\\", "/")
297
+
298
+ if c.get("use_splash") and c.get("splash_img"):
299
+ splash_img_path = get_path(c["splash_img"])
300
+ if os.path.exists(splash_img_path):
301
+ splash_html = f"""
302
+ <html>
303
+ <body style="margin:0; padding:0; overflow:hidden; background-color:black; display:flex; align-items:center; justify-content:center; height:100vh;">
304
+ <img src="file:///{os.path.abspath(splash_img_path).replace('\\', '/')}" style="max-width:100%; max-height:100%; object-fit:contain;">
305
+ </body>
306
+ </html>
307
+ """
308
+ splash_window = webview.create_window(
309
+ "Loading...",
310
+ html=splash_html,
311
+ width=c.get("width", 1280),
312
+ height=c.get("height", 720),
313
+ frameless=True,
314
+ on_top=True
315
+ )
316
+
317
+ def close_splash(sw, main_conf, main_url):
318
+ time.sleep(main_conf.get("splash_time", 3))
319
+ main_window = webview.create_window(
320
+ main_conf["title"],
321
+ url=main_url,
322
+ width=main_conf.get("width", 1280),
323
+ height=main_conf.get("height", 720),
324
+ fullscreen=(main_conf.get("mode") == "full"),
325
+ resizable=(main_conf.get("mode") == "resizable"),
326
+ on_top=main_conf.get("topmost", False)
327
+ )
328
+
329
+ # Pencere kapandığında kill_everything'i çağır
330
+ main_window.events.closed += kill_everything
331
+
332
+ if main_conf.get("no_right_click", False):
333
+ main_window.evaluate_js("document.addEventListener('contextmenu', e => e.preventDefault());")
334
+
335
+ sw.destroy()
336
+
337
+ threading.Thread(target=close_splash, args=(splash_window, c, index_url), daemon=True).start()
338
+ webview.start()
339
+ kill_everything() # webview döngüsü kırılırsa (pencere kapanırsa)
340
+ sys.exit()
341
+
342
+ # Splash yoksa normal başla
343
+ window = webview.create_window(
344
+ c["title"],
345
+ url=index_url,
346
+ width=c.get("width", 1280),
347
+ height=c.get("height", 720),
348
+ fullscreen=(c.get("mode") == "full"),
349
+ resizable=(c.get("mode") == "resizable"),
350
+ on_top=c.get("topmost", False)
351
+ )
352
+
353
+ window.events.closed += kill_everything
354
+
355
+ def disable_click(w):
356
+ if c.get("no_right_click", False):
357
+ w.evaluate_js("document.addEventListener('contextmenu', e => e.preventDefault());")
358
+
359
+ webview.start(disable_click, window)
360
+ kill_everything()
361
+
362
+ except Exception as e:
363
+ root = tk.Tk(); root.withdraw()
364
+ messagebox.showerror("Error", f"An error occurred:\n{str(e)}")
365
+ root.destroy()
366
+ kill_everything()
367
+ else:
368
+ root = tk.Tk()
369
+ app = PackerGUI(root)
370
+ root.mainloop()
371
+
372
+ def launch():
373
+ root = tk.Tk()
374
+ app = PackerGUI(root)
375
+ root.mainloop()
@@ -0,0 +1,18 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "htm-engine"
7
+ version = "1.0.0"
8
+ description = "Advanced HTML to EXE packaging suite"
9
+ readme = "README.md"
10
+ requires-python = ">=3.7"
11
+ dependencies = [
12
+ "pywebview",
13
+ "requests",
14
+ "pyinstaller"
15
+ ]
16
+
17
+ [project.scripts]
18
+ htm-engine = "htm_engine.main:main_func" # Terminalden 'htm-engine' yazınca çalışması için
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,18 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="htmengine",
5
+ version="1.0.1",
6
+ author="YAVUX STUDIOS",
7
+ packages=find_packages(),
8
+ install_requires=[
9
+ "pyinstaller",
10
+ "pywebview",
11
+ "requests",
12
+ ],
13
+ entry_points={
14
+ 'console_scripts': [
15
+ 'htmengine=htmengine.mainapp:launch', # htmengine yazınca launch() çalışır
16
+ ],
17
+ },
18
+ )