JustMotion/client/build.py
zii e72feb40ee server 1.2:
- removed -r command line option and renamed it to -s. then
   added -l as what -s used to be.

 - added detect_uri option to camera parameters so stream
   snapshots can now run a secondary stream that differs
   from the recording stream. also added sync logic so the
   recording loop and detection loop will run synchronously
   while still running in seperate threads.

 - live_secs renamed to live_mins. the amount of live
   footage to buffer in buffer_path is now based on minutes
   instead of seconds.

 - added live_clip_secs parameter so the amount of seconds
   each hls video clip should have is now adjustable.

 - remove max_event_secs. events are now transfered to
   rec_path based on minute increments.

 - added sec_per_image to make the amount of seconds
   between stream snapshots pulled from detect_uri
   adjustable.

 - added img_ext so the image snapshot format from
   detect_uri and then ultimately to img_comp_cmd can now
   be configured instead of being hardcoded to bmp.

 - added gray_image as a boolean parameter so the snapshots
   from detect_uri can be pulled in grayscale format or
   remain in color if desired.

 - removed img_scale since this parameter was not doing
   anything.

overview:

   use of QFileSystemWatcher on the detection loop has
   been eliminated and thus eliminated use of functions
   such as listFacingFiles(), backwardFacingFiles() and
   forwardFacingFiles(). Instead, the detection and
   recording loops will now run synchronously and use
   predictable up-to-the-minute file naming scheme.

client v1.1:

 - the build script will now include all imageformat
   plugins from the QT lib directory, adding support for
   jpg, svg, ico and gif image formats.

 - switched to 2 number versioning.

 - this client app will now support the server's new
   up-to-the-minute directory structure for live and
   recorded footage.

 - the version number is now be displayed on the main
   interface.
2025-10-11 08:05:51 -04:00

267 lines
8.9 KiB
Python
Executable File

#!/usr/bin/python3
import os
import re
import subprocess
import shutil
import sys
import platform
def get_app_target(text):
return re.search(r'(APP_TARGET) +(\"(.*?)\")', text).group(3)
def get_app_ver(text):
return re.search(r'(APP_VERSION) +(\"(.*?)\")', text).group(3)
def get_app_name(text):
return re.search(r'(APP_NAME) +(\"(.*?)\")', text).group(3)
def get_qt_path():
try:
return str(subprocess.check_output(["qtpaths6", "--binaries-dir"]), 'utf-8').strip()
except:
print("A direct call to 'qtpaths6' has failed so automatic retrieval of the QT bin folder is not possible.")
return input("Please enter the QT bin path (leave blank to cancel the build): ")
def get_qt_from_cli():
for arg in sys.argv:
if arg == "-qt_dir":
index = sys.argv.index(arg)
try:
return sys.argv[index + 1]
except:
return ""
return ""
def get_ver_header():
current_dir = os.path.dirname(__file__)
if current_dir == "":
return "src" + os.sep + "common.h"
else:
return current_dir + os.sep + "src" + os.sep + "common.h"
def get_nearest_subdir(path, sub_name):
dir_list = os.listdir(path)
ret = ""
for entry in dir_list:
if sub_name in entry:
ret = entry
break
return ret
def cd():
current_dir = os.path.dirname(__file__)
if current_dir != "":
os.chdir(current_dir)
def verbose_copy(src, dst):
print("cpy: " + src + " --> " + dst)
if os.path.isdir(src):
if os.path.exists(dst) and os.path.isdir(dst):
shutil.rmtree(dst)
try:
# ignore errors thrown by shutil.copytree()
# it's likely not actually failing to copy
# the directory but still throws errors if
# it fails to apply the same file stats as
# the source. this type of error can be
# ignored.
shutil.copytree(src, dst)
except:
pass
elif os.path.exists(src):
shutil.copyfile(src, dst)
else:
print("wrn: " + src + " does not exists. skipping.")
def linux_build_app_dir(app_ver, app_name, app_target, qt_bin):
if not os.path.exists("app_dir/platforms"):
os.makedirs("app_dir/platforms")
if not os.path.exists("app_dir/xcbglintegrations"):
os.makedirs("app_dir/xcbglintegrations")
if not os.path.exists("app_dir/multimedia"):
os.makedirs("app_dir/multimedia")
if not os.path.exists("app_dir/platformthemes"):
os.makedirs("app_dir/platformthemes")
if not os.path.exists("app_dir/lib"):
os.makedirs("app_dir/lib")
if not os.path.exists("app_dir/icons"):
os.makedirs("app_dir/icons")
if not os.path.exists("app_dir/imageformats"):
os.makedirs("app_dir/imageformats")
verbose_copy(qt_bin + "/../plugins/platforms", "app_dir/platforms")
verbose_copy(qt_bin + "/../plugins/xcbglintegrations", "app_dir/xcbglintegrations")
verbose_copy(qt_bin + "/../plugins/multimedia", "app_dir/multimedia")
verbose_copy(qt_bin + "/../plugins/platformthemes", "app_dir/platformthemes")
verbose_copy(qt_bin + "/../plugins/imageformats", "app_dir/imageformats")
verbose_copy("build/" + app_target, "app_dir/" + app_target)
verbose_copy("icons/main.svg", "app_dir/icons/scalable.svg")
img_sizes = [8, 16, 22, 24, 28, 32, 36, 42, 48, 64, 72, 96, 128, 192, 256, 512, 1024]
for i in img_sizes:
subprocess.run(["inkscape", "-w", str(i), "-h", str(i), "icons/main.svg", "-o", "app_dir/icons/" + str(i) + ".png"])
shutil.copyfile("build/" + app_target, "/tmp/" + app_target)
# copying the executable file from the build folder to
# temp bypasses any -noexe retrictions a linux file
# system may have. there is a chance temp is also
# restricted in this way but that kind of config is
# rare. ldd will not run correctly with -noexe
# enabled.
lines = str(subprocess.check_output(["ldd", "/tmp/" + app_target]), 'utf-8').split("\n")
os.remove("/tmp/" + app_target)
for line in lines:
if " => " in line and "libc" not in line:
#if ("libQt" in line) or ("libicu" in line) or ("libGL.so" in line) or ("libpcre16.so" in line) or ("libpcre.so" in line):
if " (0x0" in line:
start_index = line.index("> ") + 2
end_index = line.index(" (0x0")
src_file = line[start_index:end_index]
file_name = os.path.basename(src_file)
verbose_copy(src_file, "app_dir/lib/" + file_name)
if "/usr/lib/x86_64-linux-gnu/qt6/bin" == qt_bin:
verbose_copy(qt_bin + "/../../libQt6DBus.so.6", "app_dir/lib/libQt6DBus.so.6")
verbose_copy(qt_bin + "/../../libQt6XcbQpa.so.6", "app_dir/lib/libQt6XcbQpa.so.6")
else:
verbose_copy(qt_bin + "/../lib/libQt6DBus.so.6", "app_dir/lib/libQt6DBus.so.6")
verbose_copy(qt_bin + "/../lib/libQt6XcbQpa.so.6", "app_dir/lib/libQt6XcbQpa.so.6")
verbose_copy(qt_bin + "/../lib/libQt6OpenGL.so.6", "app_dir/lib/libQt6OpenGL.so.6")
verbose_copy("templates/linux_run_script.sh", "app_dir/" + app_target + ".sh")
verbose_copy("templates/linux_uninstall.sh", "app_dir/uninstall.sh")
verbose_copy("templates/linux_icon.desktop", "app_dir/" + app_target + ".desktop")
complete(app_ver, app_target)
def complete(app_ver, app_target):
print("Build complete for version: " + app_ver)
print("You can now run the install.py script to install onto this machine or create an installer.")
def get_like_distro():
info = platform.freedesktop_os_release()
ids = [info["ID"]]
if "ID_LIKE" in info:
# ids are space separated and ordered by precedence
ids.extend(info["ID_LIKE"].split())
return ids
def list_installed_packages():
like_distro = get_like_distro()
if ("ubuntu" in like_distro) or ("debian" in like_distro) or ("linuxmint" in like_distro):
return str(subprocess.check_output(["apt", "list", "--installed"]), 'utf-8')
elif ("fedora" in like_distro) or ("rhel" in like_distro):
return str(subprocess.check_output(["dnf", "list", "installed"]), 'utf-8')
elif ("arch" in like_distro):
return str(subprocess.check_output(["pacman", "-Q"]), 'utf-8')
else:
print("Warning: unable to determine a package manager for this platform.")
return []
def list_of_words_in_text(list_of_words, text_body):
for word in list_of_words:
if not word in text_body:
return False
return True
def platform_setup():
ins_packages = list_installed_packages()
like_distro = get_like_distro()
dep_pkgs_a = ["pkg-config"]
dep_pkgs_b = ["ffmpeg", "libavcodec-dev", "libavformat-dev", "libavfilter-dev", "libavdevice-dev", "libilmbase-dev", "libvdpau-dev", "libxkbcommon-dev", "libgl-dev", "libxcb-cursor0", "inkscape"]
if not list_of_words_in_text(dep_pkgs_a, ins_packages) or not list_of_words_in_text(dep_pkgs_b, ins_packages):
if ("ubuntu" in like_distro) or ("debian" in like_distro) or ("linuxmint" in like_distro):
subprocess.run(["sudo", "apt", "update", "-y"])
subprocess.run(["sudo", "apt", "install", "-y"] + dep_pkgs_a)
subprocess.run(["sudo", "apt", "install", "-y"] + dep_pkgs_b)
elif ("fedora" in like_distro) or ("rhel" in like_distro):
subprocess.run(["sudo", "dnf", "install", "-y"] + dep_pkgs_a)
subprocess.run(["sudo", "dnf", "install", "-y"] + dep_pkgs_b)
elif ("arch" in like_distro):
subprocess.run(["sudo", "pacman", "-S", "--noconfirm"] + dep_pkgs_a)
subprocess.run(["sudo", "pacman", "-S", "--noconfirm"] + dep_pkgs_b)
def main():
platform_setup()
with open(get_ver_header()) as file:
text = file.read()
app_target = get_app_target(text)
app_ver = get_app_ver(text)
app_name = get_app_name(text)
qt_bin = get_qt_from_cli()
if qt_bin == "":
qt_bin = get_qt_path()
if qt_bin != "":
print("app_target = " + app_target)
print("app_version = " + app_ver)
print("app_name = " + app_name)
print("qt_bin = " + qt_bin)
cd()
result = subprocess.run([qt_bin + os.sep + "qmake", "-config", "release"])
if result.returncode == 0:
result = subprocess.run(["make"])
if result.returncode == 0:
if os.path.exists("app_dir"):
shutil.rmtree("app_dir")
os.makedirs("app_dir")
with open("app_dir" + os.sep + "info.txt", "w") as info_file:
info_file.write(app_target + "\n")
info_file.write(app_ver + "\n")
info_file.write(app_name + "\n")
linux_build_app_dir(app_ver, app_name, app_target, qt_bin)
if __name__ == "__main__":
main()