initial commit

This commit is contained in:
2026-01-05 10:36:19 -08:00
parent 1572e857d5
commit 31a3f9c539
28 changed files with 3600 additions and 32 deletions

31
LICENSE
View File

@@ -200,33 +200,4 @@ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY C
17. Interpretation of Sections 15 and 16. 17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
sparks
Copyright (C) 2026 tyler
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
sparks Copyright (C) 2026 tyler
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”.
You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <https://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@@ -1,3 +1,44 @@
# sparks SPARKS (Spacial Patterns Analysis, Research, & Knowledge Suite)
=================================================================
Spatial Patterns, Analysis, Research, & Knowledge Suite SPARKS is a standalone application to extract meaningful data out of video files.
SPARKS is free and open-source software that runs on Windows, MacOS, and Linux. Please read the information regarding each operating system below.
Visit the official [SPARKS web site](https://research.dezeeuw.ca/sparks).
[![Python web site](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org)
# For MacOS Users
Due to the cost of an Apple Developer account, the application is not certified by Apple. Once the application is extracted and attempted to be launched for the first time you will get a popup stating:
"Apple could not verify sparks.app is free of malware that may harm your Mac or compromise your privacy.", with the options of "Done" or "Move to Trash".
The solution around this is to use finder and navigate to the sparks-darwin folder. Once the folder has been located, right click the folder and click the option "New Terminal at Folder". Once the terminal opens, run the following command (you can copy + paste):
```xattr -dr com.apple.quarantine sparks.app & pid1=$!; xattr -dr com.apple.quarantine sparks_updater.app & pid2=$!; wait $pid1 $pid2; exit```
Once the command has been executed and the text "[Process completed]" appears, you may close the terminal window and attempt to open the application again. If you choose to unrestrict the app through Settings > Privacy & Security, the app may not be able to update correctly in the future.
This only applies for the first time you attempt to run SPARKS. Subsequent times, including after updates, will function correctly as-is.
# For Windows Users
Due to the cost of a code signing certificate, the application is not digitally signed. Once the application is extracted and attempted to be launched for the first time you will get a popup stating:
"Windows protected your PC - Microsoft Defender SmartScreen prevented an unrecognized app from starting. Running this app might put your PC at risk.", with the options of" More info" or "Don't run".
The solution around this is to click "More info" and then select "Run anyway".
This only applies for the first time you attempt to run SPARKS. Subsequent times, including after updates, will function correctly as-is.
# For Linux Users
There are no conditions for Linux users at this time.
# Licence
SPARKS is distributed under the GPL-3.0 license.
Copyright (C) 2025-2026 Tyler de Zeeuw

3
changelog.md Normal file
View File

@@ -0,0 +1,3 @@
# Version 0.1.0
- Initial preview release.

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z"/></svg>

After

Width:  |  Height:  |  Size: 278 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>

After

Width:  |  Height:  |  Size: 284 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><path d="M0 0h24v24H0z" fill="none"/><circle cx="6" cy="18" fill="none" r="2"/><circle cx="12" cy="12" fill="none" r=".5"/><circle cx="6" cy="6" fill="none" r="2"/><path d="M9.64 7.64c.23-.5.36-1.05.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c.59 0 1.14-.13 1.64-.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-.59-.13-1.14-.36-1.64L12 14l7 7h3v-1L9.64 7.64zM6 8c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm0 12c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm6-7.5c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5-.22.5-.5.5zM19 3l-6 6 2 2 7-7V3z"/></svg>

After

Width:  |  Height:  |  Size: 694 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z"/></svg>

After

Width:  |  Height:  |  Size: 357 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><path d="M0 0h24v24H0z" fill="none"/><path d="M10.09 15.59L11.5 17l5-5-5-5-1.41 1.41L12.67 11H3v2h9.67l-2.58 2.59zM19 3H5c-1.11 0-2 .9-2 2v4h2V5h14v14H5v-4H3v4c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/></svg>

After

Width:  |  Height:  |  Size: 321 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><g><rect fill="none" height="24" width="24"/></g><g><path d="M14,2H6C4.9,2,4,2.9,4,4v16c0,1.1,0.89,2,1.99,2H15v-8h5V8L14,2z M13,9V3.5L18.5,9H13z M17,21.66V16h5.66v2h-2.24 l2.95,2.95l-1.41,1.41L19,19.41l0,2.24H17z"/></g></svg>

After

Width:  |  Height:  |  Size: 361 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><path d="M0 0h24v24H0z" fill="none"/><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>

After

Width:  |  Height:  |  Size: 248 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><g><rect fill="none" height="24" width="24"/></g><g><g><path d="M3,6H1v13c0,1.1,0.9,2,2,2h17v-2H3V6z"/><path d="M21,4h-7l-2-2H7C5.9,2,5.01,2.9,5.01,4L5,15c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V6C23,4.9,22.1,4,21,4z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 364 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640v242q-18-14-38-23t-42-19v-200H447l-80-80H160v480h120v80H160ZM640-40q-91 0-168-48T360-220q35-84 112-132t168-48q91 0 168 48t112 132q-35 84-112 132T640-40Zm0-80q57 0 107.5-26t82.5-74q-32-48-82.5-74T640-320q-57 0-107.5 26T450-220q32 48 82.5 74T640-120Zm0-40q-25 0-42.5-17.5T580-220q0-25 17.5-42.5T640-280q25 0 42.5 17.5T700-220q0 25-17.5 42.5T640-160Zm-480-80v-480 277-37 240Z"/></svg>

After

Width:  |  Height:  |  Size: 593 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/></svg>

After

Width:  |  Height:  |  Size: 425 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>

After

Width:  |  Height:  |  Size: 253 B

BIN
icons/main.icns Normal file

Binary file not shown.

BIN
icons/main.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M200-440v-80h560v80H200Z"/></svg>

After

Width:  |  Height:  |  Size: 149 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><path d="M0 0h24v24H0z" fill="none"/><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/></svg>

After

Width:  |  Height:  |  Size: 299 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><g><rect fill="none" height="24" width="24"/></g><g><path d="M21,12.4V7l-4-4H5C3.89,3,3,3.9,3,5v14c0,1.1,0.89,2,2,2h7.4L21,12.4z M15,15c0,1.66-1.34,3-3,3s-3-1.34-3-3s1.34-3,3-3 S15,13.34,15,15z M6,6h9v4H6V6z M19.99,16.25l1.77,1.77L16.77,23H15v-1.77L19.99,16.25z M23.25,16.51l-0.85,0.85l-1.77-1.77 l0.85-0.85c0.2-0.2,0.51-0.2,0.71,0l1.06,1.06C23.45,16,23.45,16.32,23.25,16.51z"/></g></svg>

After

Width:  |  Height:  |  Size: 524 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm0-80h640v-400H160v400Zm140-40-56-56 103-104-104-104 57-56 160 160-160 160Zm180 0v-80h240v80H480Z"/></svg>

After

Width:  |  Height:  |  Size: 340 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#1f1f1f"><g><rect fill="none" height="24" width="24" x="0"/></g><g><g><g><path d="M21,10.12h-6.78l2.74-2.82c-2.73-2.7-7.15-2.8-9.88-0.1c-2.73,2.71-2.73,7.08,0,9.79s7.15,2.71,9.88,0 C18.32,15.65,19,14.08,19,12.1h2c0,1.98-0.88,4.55-2.64,6.29c-3.51,3.48-9.21,3.48-12.72,0c-3.5-3.47-3.53-9.11-0.02-12.58 s9.14-3.47,12.65,0L21,3V10.12z M12.5,8v4.25l3.5,2.08l-0.72,1.21L11,13V8H12.5z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 525 B

BIN
icons/updater.icns Normal file

Binary file not shown.

BIN
icons/updater.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M280-160v-80h400v80H280Zm160-160v-327L336-544l-56-56 200-200 200 200-56 56-104-103v327h-80Z"/></svg>

After

Width:  |  Height:  |  Size: 216 B

3223
main.py Normal file

File diff suppressed because it is too large Load Diff

255
sparks_updater.py Normal file
View File

@@ -0,0 +1,255 @@
"""
Filename: sparks_updater.py
Description: SPARKS updater executable
Author: Tyler de Zeeuw
License: GPL-3.0
"""
# Built-in imports
import os
import sys
import time
import shlex
import psutil
import shutil
import platform
import subprocess
from datetime import datetime
PLATFORM_NAME = platform.system().lower()
if PLATFORM_NAME == 'darwin':
LOG_FILE = os.path.join(os.path.dirname(sys.executable), "../../../sparks_updater.log")
else:
LOG_FILE = os.path.join(os.getcwd(), "sparks_updater.log")
def log(msg):
with open(LOG_FILE, "a", encoding="utf-8") as f:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
f.write(f"{timestamp} - {msg}\n")
def kill_all_processes_by_executable(exe_path):
terminated_any = False
exe_path = os.path.realpath(exe_path)
if PLATFORM_NAME == 'windows':
for proc in psutil.process_iter(['pid', 'exe']):
try:
proc_exe = proc.info.get('exe')
if proc_exe and os.path.samefile(os.path.realpath(proc_exe), exe_path):
log(f"Terminating process: PID {proc.pid}")
_terminate_process(proc)
terminated_any = True
except Exception as e:
log(f"Error terminating process (Windows): {e}")
elif PLATFORM_NAME == 'linux':
for proc in psutil.process_iter(['pid', 'cmdline']):
try:
cmdline = proc.info.get('cmdline', [])
if cmdline:
proc_cmd = os.path.realpath(cmdline[0])
if os.path.samefile(proc_cmd, exe_path):
log(f"Terminating process: PID {proc.pid}")
_terminate_process(proc)
terminated_any = True
except Exception as e:
log(f"Error terminating process (Linux): {e}")
if not terminated_any:
log(f"No running processes found for {exe_path}")
return terminated_any
def _terminate_process(proc):
try:
proc.terminate()
proc.wait(timeout=10)
log(f"Process {proc.pid} terminated gracefully.")
except psutil.TimeoutExpired:
log(f"Process {proc.pid} did not terminate in time. Killing forcefully.")
proc.kill()
proc.wait(timeout=5)
log(f"Process {proc.pid} killed.")
def wait_for_unlock(path, timeout=100):
start_time = time.time()
while time.time() - start_time < timeout:
try:
if os.path.isdir(path):
shutil.rmtree(path)
else:
os.remove(path)
log(f"Deleted (after wait): {path}")
return
except Exception as e:
log(f"Still locked: {path} - {e}")
time.sleep(1)
log(f"Failed to delete after wait: {path}")
def delete_path(path):
if os.path.exists(path):
try:
if os.path.isdir(path):
shutil.rmtree(path)
log(f"Deleted directory: {path}")
else:
os.remove(path)
log(f"Deleted file: {path}")
except Exception as e:
log(f"Error deleting {path}: {e}")
def copy_update_files(src_folder, dest_folder, updater_name):
for item in os.listdir(src_folder):
if item.lower() == updater_name.lower():
log(f"Skipping updater executable: {item}")
continue
s = os.path.join(src_folder, item)
d = os.path.join(dest_folder, item)
delete_path(d)
try:
if os.path.isdir(s):
shutil.copytree(s, d)
log(f"Copied folder: {s} -> {d}")
else:
shutil.copy2(s, d)
log(f"Copied file: {s} -> {d}")
except Exception as e:
log(f"Error copying {s} -> {d}: {e}")
def copy_update_files_darwin(src_folder, dest_folder, updater_name):
updater_name = updater_name + ".app"
for item in os.listdir(src_folder):
if item.lower() == updater_name.lower():
log(f"Skipping updater executable: {item}")
continue
s = os.path.join(src_folder, item)
d = os.path.join(dest_folder, item)
delete_path(d)
try:
if os.path.isdir(s):
subprocess.check_call(["ditto", s, d])
log(f"Copied folder with ditto: {s} -> {d}")
else:
shutil.copy2(s, d)
log(f"Copied file: {s} -> {d}")
except Exception as e:
log(f"Error copying {s} -> {d}: {e}")
def remove_quarantine(app_path):
script = f'''
do shell script "xattr -d -r com.apple.quarantine {shlex.quote(app_path)}" with administrator privileges with prompt "SPARKS needs privileges to finish the update. (1/2)"
'''
try:
subprocess.run(['osascript', '-e', script], check=True)
print("✅ Quarantine attribute removed.")
except subprocess.CalledProcessError as e:
print("❌ Failed to remove quarantine attribute.")
print(e)
def main():
try:
log(f"[Updater] sys.argv: {sys.argv}")
if len(sys.argv) != 3:
log("Invalid arguments. Usage: sparks_updater <update_folder> <main_app_executable>")
sys.exit(1)
update_folder = sys.argv[1]
main_exe = sys.argv[2]
# Interesting naming convention
parent_dir = os.path.dirname(os.path.abspath(main_exe))
pparent_dir = os.path.dirname(parent_dir)
ppparent_dir = os.path.dirname(pparent_dir)
pppparent_dir = os.path.dirname(ppparent_dir)
updater_name = os.path.basename(sys.argv[0])
log("Updater started.")
log(f"Update folder: {update_folder}")
log(f"Main EXE: {main_exe}")
log(f"Updater EXE: {updater_name}")
if PLATFORM_NAME == 'darwin':
log(f"Main App Folder: {ppparent_dir}")
# Kill all instances of main app
kill_all_processes_by_executable(main_exe)
# Wait until main_exe process is fully gone (polling)
for _ in range(20): # wait max 10 seconds
running = False
for proc in psutil.process_iter(['exe', 'cmdline']):
try:
if PLATFORM_NAME == 'windows':
proc_exe = proc.info.get('exe')
if proc_exe and os.path.samefile(os.path.realpath(proc_exe), os.path.realpath(main_exe)):
running = True
break
elif PLATFORM_NAME == 'linux':
cmdline = proc.info.get('cmdline', [])
if cmdline:
proc_cmd = os.path.realpath(cmdline[0])
if os.path.samefile(proc_cmd, os.path.realpath(main_exe)):
running = True
break
except Exception as e:
log(f"Polling error: {e}")
if not running:
break
time.sleep(0.5)
else:
log("Warning: main executable still running after wait timeout.")
# Delete old version files
if PLATFORM_NAME == 'darwin':
log(f'Attempting to delete {ppparent_dir}')
delete_path(ppparent_dir)
update_folder = os.path.join(sys.argv[1], "sparks-darwin")
copy_update_files_darwin(update_folder, pppparent_dir, updater_name)
else:
delete_path(main_exe)
wait_for_unlock(os.path.join(parent_dir, "_internal"))
# Copy new files excluding the updater itself
copy_update_files(update_folder, parent_dir, updater_name)
except Exception as e:
log(f"Something went wrong: {e}")
# Relaunch main app
try:
if PLATFORM_NAME == 'linux':
os.chmod(main_exe, 0o755)
log("Added executable bit")
if PLATFORM_NAME == 'darwin':
os.chmod(ppparent_dir, 0o755)
log("Added executable bit")
remove_quarantine(ppparent_dir)
log(f"Removed the quarantine flag on {ppparent_dir}")
subprocess.Popen(['open', ppparent_dir, "--args", "--finish-update"])
else:
subprocess.Popen([main_exe, "--finish-update"], cwd=parent_dir)
log("Relaunched main app.")
except Exception as e:
log(f"Failed to relaunch main app: {e}")
log("Updater completed. Exiting.")
sys.exit(0)
if __name__ == "__main__":
main()

29
version_main.txt Normal file
View File

@@ -0,0 +1,29 @@
VSVersionInfo(
ffi=FixedFileInfo(
filevers=(1, 0, 0, 0),
prodvers=(1, 0, 0, 0),
mask=0x3f,
flags=0x0,
OS=0x4,
fileType=0x1,
subtype=0x0,
date=(0, 0)
),
kids=[
StringFileInfo(
[
StringTable(
'040904B0',
[StringStruct('CompanyName', 'Tyler de Zeeuw'),
StringStruct('FileDescription', 'SPARKS main application'),
StringStruct('FileVersion', '1.0.0.0'),
StringStruct('InternalName', 'sparks.exe'),
StringStruct('LegalCopyright', '© 2025 Tyler de Zeeuw'),
StringStruct('OriginalFilename', 'sparks.exe'),
StringStruct('ProductName', 'SPARKS'),
StringStruct('ProductVersion', '1.0.0.0')])
]
),
VarFileInfo([VarStruct('Translation', [1033, 1200])])
]
)

29
version_updater.txt Normal file
View File

@@ -0,0 +1,29 @@
VSVersionInfo(
ffi=FixedFileInfo(
filevers=(1, 0, 0, 0),
prodvers=(1, 0, 0, 0),
mask=0x3f,
flags=0x0,
OS=0x4,
fileType=0x1,
subtype=0x0,
date=(0, 0)
),
kids=[
StringFileInfo(
[
StringTable(
'040904B0',
[StringStruct('CompanyName', 'Tyler de Zeeuw'),
StringStruct('FileDescription', 'SPARKS updater application'),
StringStruct('FileVersion', '1.0.0.0'),
StringStruct('InternalName', 'main.exe'),
StringStruct('LegalCopyright', '© 2025 Tyler de Zeeuw'),
StringStruct('OriginalFilename', 'sparks_updater.exe'),
StringStruct('ProductName', 'SPARKS Updater'),
StringStruct('ProductVersion', '1.0.0.0')])
]
),
VarFileInfo([VarStruct('Translation', [1033, 1200])])
]
)