JustMotion/client/install.py

344 lines
11 KiB
Python
Raw Normal View History

#!/usr/bin/python3
import os
import subprocess
import shutil
import platform
import sys
import zipfile
import binascii
import tempfile
def cd():
current_dir = os.path.dirname(__file__)
if current_dir != "":
os.chdir(current_dir)
def get_default_installer_path(app_ver, app_name):
if not os.path.exists("../installers"):
os.makedirs("../installers")
return "../installers/" + app_name + "-" + app_ver + "-" + platform.system() + "-" + platform.machine() + ".run"
def make_install_dir(path):
try:
if not os.path.exists(path):
os.makedirs(path)
return True
except:
print("Failed to create the install directory, please make sure you are runnning this script with admin rights.")
return False
def replace_bin(binary, old_bin, new_bin, offs):
while(True):
try:
index = binary.index(old_bin, offs)
binary = binary[:index] + new_bin + binary[index + len(old_bin):]
except ValueError:
break
return binary
def bin_sub_copy_file(src, dst, old_bin, new_bin, offs):
binary = bytearray()
with open(src, "rb") as rd_file:
binary = rd_file.read()
binary = replace_bin(binary, old_bin, new_bin, offs)
with open(dst, "wb") as wr_file:
wr_file.write(binary)
def text_sub_copy_file(src, dst, old_text, new_text, offs):
bin_sub_copy_file(src, dst, old_text.encode("utf-8"), new_text.encode("utf-8"), offs)
def text_template_deploy(src, dst, install_dir, app_name, app_target):
print("dep: " + dst)
text_sub_copy_file(src, dst, "$install_dir", install_dir, 0)
text_sub_copy_file(dst, dst, "$app_name", app_name, 0)
text_sub_copy_file(dst, dst, "$app_target", app_target, 0)
def verbose_copy(src, dst):
print("cpy: " + src + " --> " + dst)
if os.path.isdir(src):
files = os.listdir(src)
if not os.path.exists(dst):
os.makedirs(dst)
for file in files:
tree_src = src + os.path.sep + file
tree_dst = dst + os.path.sep + file
if os.path.isdir(tree_src):
if not os.path.exists(tree_dst):
os.makedirs(tree_dst)
verbose_copy(tree_src, tree_dst)
else:
shutil.copyfile(src, dst)
def verbose_create_symmlink(src, dst):
print("lnk: " + src + " --> " + dst)
if os.path.exists(dst):
os.remove(dst)
os.symlink(src, dst)
def local_install(app_target, app_name):
install_dir = "/opt/" + app_target
if os.path.exists(install_dir + "/uninstall.sh"):
subprocess.run([install_dir + "/uninstall.sh"])
if make_install_dir(install_dir):
text_template_deploy("app_dir/" + app_target + ".sh", install_dir + "/" + app_target + ".sh", install_dir, app_name, app_target)
text_template_deploy("app_dir/uninstall.sh", install_dir + "/uninstall.sh", install_dir, app_name, app_target)
text_template_deploy("app_dir/" + app_target + ".desktop", "/usr/share/applications/" + app_target + ".desktop", install_dir, app_name, app_target)
img_sizes = [8, 16, 22, 24, 28, 32, 36, 42, 48, 64, 72, 96, 128, 192, 256, 512, 1024]
for i in img_sizes:
dst_img = "/usr/share/icons/hicolor/" + str(i) + "x" + str(i) + "/apps/jmc.png"
verbose_copy("app_dir/icons/" + str(i) + ".png", dst_img)
subprocess.run(["chmod", "644", dst_img])
verbose_copy("app_dir/icons/scalable.svg", "/usr/share/icons/hicolor/scalable/apps/jmc.svg")
verbose_copy("app_dir/" + app_target, install_dir + "/" + app_target)
verbose_copy("app_dir/lib", install_dir + "/lib")
verbose_copy("app_dir/platforms", install_dir + "/platforms")
verbose_copy("app_dir/xcbglintegrations", install_dir + "/xcbglintegrations")
verbose_copy("app_dir/multimedia", install_dir + "/multimedia")
verbose_copy("app_dir/platformthemes", install_dir + "/platformthemes")
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
verbose_copy("app_dir/imageformats", install_dir + "/imageformats")
verbose_create_symmlink(install_dir + "/" + app_target + ".sh", "/usr/bin/" + app_target)
subprocess.run(["chmod", "644", "/usr/share/icons/hicolor/scalable/apps/jmc.svg"])
subprocess.run(["chmod", "755", install_dir + "/" + app_target + ".sh"])
subprocess.run(["chmod", "755", install_dir + "/" + app_target])
subprocess.run(["chmod", "755", install_dir + "/uninstall.sh"])
print("Installation finished. If you ever need to uninstall this application, run this command with root rights:")
print(" sh " + install_dir + "/uninstall.sh\n")
def dir_tree(path):
ret = []
if os.path.isdir(path):
for entry in os.listdir(path):
full_path = os.path.join(path, entry)
if os.path.isdir(full_path):
for sub_dir_file in dir_tree(full_path):
ret.append(sub_dir_file)
else:
ret.append(full_path)
return ret
def to_hex(data):
return str(binascii.hexlify(data))[2:-1]
def from_hex(text_line):
return binascii.unhexlify(text_line)
def make_install(app_ver, app_name):
path = get_default_installer_path(app_ver, app_name)
with zipfile.ZipFile("app_dir.zip", "w", compression=zipfile.ZIP_DEFLATED) as zip_file:
print("Compressing app_dir --")
for file in dir_tree("app_dir"):
print("adding file: " + file)
zip_file.write(file)
text_sub_copy_file(__file__, path, "main(is_sfx=False)", "main(is_sfx=True)\n\n\n", 10728)
with open(path, "a") as dst_file, open("app_dir.zip", "rb") as src_file:
print("Packing the compressed app_dir into the sfx script file --")
dst_file.write("# APP_DIR\n")
stat = os.stat("app_dir.zip")
while(True):
buffer = src_file.read(4000000)
if len(buffer) != 0:
dst_file.write("# " + to_hex(buffer) + "\n")
print(str(src_file.tell()) + "/" + str(stat.st_size))
if len(buffer) < 4000000:
break
os.remove("app_dir.zip")
subprocess.run(["chmod", "+x", path])
print("Finished packing the app.")
print("Installer: " + path)
def sfx():
abs_sfx_path = os.path.abspath(__file__)
mark_found = False
os.chdir(tempfile.gettempdir())
with open(abs_sfx_path) as packed_file, open("app_dir.zip", "wb") as zip_file:
stat = os.stat(abs_sfx_path)
print("Unpacking the app_dir compressed file from the sfx script.")
while(True):
line = packed_file.readline()
if not line:
break
elif mark_found:
zip_file.write(from_hex(line[2:-1]))
print(str(packed_file.tell()) + "/" + str(stat.st_size))
else:
if line == "# APP_DIR\n":
mark_found = True
print("Done.")
if not mark_found:
print("The app_dir mark was not found, unable to continue.")
else:
with zipfile.ZipFile("app_dir.zip", "r", compression=zipfile.ZIP_DEFLATED) as zip_file:
print("De-compressing app_dir --")
zip_file.extractall()
print("Preparing for installation.")
os.remove("app_dir.zip")
with open("app_dir" + os.sep + "info.txt") as info_file:
info = info_file.read().split("\n")
local_install(info[0], info[2])
shutil.rmtree("app_dir")
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"]
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(is_sfx):
cd()
app_target = ""
app_ver = ""
app_name = ""
if not is_sfx:
with open("app_dir" + os.sep + "info.txt") as info_file:
info = info_file.read().split("\n")
app_target = info[0]
app_ver = info[1]
app_name = info[2]
if is_sfx:
sfx()
elif "--local" in sys.argv:
platform_setup()
local_install(app_target, app_name)
elif "--installer" in sys.argv:
make_install(app_ver, app_name)
else:
print("Do you want to install onto this machine or create an installer?")
print("[1] local machine")
print("[2] create installer")
print("[3] exit")
while(True):
opt = input("select an option: ")
if opt == "1":
subprocess.run(["sudo", "python3", "install.py", "--local"])
break
elif opt == "2":
subprocess.run(["python3", "install.py", "--installer"])
break
elif opt == "3":
break
if __name__ == "__main__":
main(is_sfx=False)