v3.4.t4
-changed the app name to JustMotion to match the git repository. -changed the build system away from cmake to qmake now that the app doesn't depend on openCV. -changed the build/install scripts to python based scripts which now includes the ability create an installer.
This commit is contained in:
parent
c5393484c2
commit
553585e275
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -54,7 +54,3 @@ compile_commands.json
|
|||
|
||||
# VSCode
|
||||
/.vscode
|
||||
|
||||
# Build folders
|
||||
/.build-mow
|
||||
/.build-imagemagick
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
project(MotionWatch LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)
|
||||
|
||||
add_executable(mow
|
||||
src/main.cpp
|
||||
src/common.h
|
||||
src/common.cpp
|
||||
src/camera.h
|
||||
src/camera.cpp
|
||||
src/services.h
|
||||
src/services.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(mow Qt${QT_VERSION_MAJOR}::Core ${OpenCV_LIBS})
|
20
JustMotion.pro
Normal file
20
JustMotion.pro
Normal file
|
@ -0,0 +1,20 @@
|
|||
QT -= gui
|
||||
|
||||
CONFIG += c++11 console
|
||||
CONFIG -= app_bundle
|
||||
|
||||
TARGET = build/jmotion
|
||||
OBJECTS_DIR = build
|
||||
MOC_DIR = build
|
||||
RCC_DIR = build
|
||||
|
||||
HEADERS += \
|
||||
src/common.h \
|
||||
src/camera.h \
|
||||
src/services.h
|
||||
|
||||
SOURCES += \
|
||||
src/common.cpp \
|
||||
src/camera.cpp \
|
||||
src/services.cpp \
|
||||
src/main.cpp
|
55
README.md
55
README.md
|
@ -1,21 +1,38 @@
|
|||
# Motion Watch #
|
||||
# JustMotion #
|
||||
|
||||
Motion Watch is a video surveillance application that monitors the video feeds
|
||||
of an IP camera and records only footage that contains motion. The main
|
||||
advantage of this is reduced storage requirements as opposed to continuous
|
||||
recording because only video footage of interest is recorded to storage.
|
||||
JustMotion is a video surveillance application that monitors the video feeds
|
||||
of an IP or local camera and records only footage that contains motion. The
|
||||
main advantage of this is reduced storage requirements as opposed to
|
||||
continuous recording because only video footage of interest is recorded to
|
||||
storage.
|
||||
|
||||
Sticking to the principle of doing the least as needed, this application is
|
||||
extremely lightweight with the fact it doesn't attempt to re-implement much
|
||||
of it's functions internally but will instead rely on external applications
|
||||
that already implement the functions very well.
|
||||
|
||||
No user interface is implemented instead exteral applications are more than
|
||||
welcome to interface with the buffer/footage directories to implement a
|
||||
user interface of any flavor.
|
||||
|
||||
### Usage ###
|
||||
|
||||
```
|
||||
Usage: mow <argument>
|
||||
Usage: jmotion <argument>
|
||||
|
||||
-h : display usage information about this application.
|
||||
-c : path to the config file used to run a single camera instance.
|
||||
-d : path to a directory that can contain multiple config files.
|
||||
each file found in the directory will be used to run a
|
||||
camera instance.
|
||||
-c : path to the config file used to run a single main loop instance.
|
||||
-i : all valid config files found in /etc/jmotion will be used to create
|
||||
a full instance; full meaning main and vid loop systemd services
|
||||
will be created for each config file.
|
||||
-d : this is the same as -i except it will not auto start the services.
|
||||
-v : display the current version.
|
||||
-u : uninstall the entire app from your system, including all
|
||||
systemd services related to it.
|
||||
-f : force an action without pausing for user confirmation.
|
||||
-l : list all jmotion services along with statuses.
|
||||
-r : remove all jmotion services.
|
||||
|
||||
```
|
||||
|
||||
### Config File ###
|
||||
|
@ -151,18 +168,20 @@ service_group = mow
|
|||
# service_user will be used.
|
||||
```
|
||||
|
||||
### Setup/Build/Install ###
|
||||
### Build/Install ###
|
||||
|
||||
This application is currently only compatible with a Linux based operating
|
||||
systems that are capable of installing the QT API.
|
||||
systems that are capable of installing python3 and the QT API (QT6.X.X or
|
||||
better).
|
||||
|
||||
```
|
||||
sh ./setup.sh <--- only need to run this once if compiling for the first
|
||||
sh ./build.sh time or if upgrading from the ground up.
|
||||
sh ./install.sh
|
||||
./build.py <--run this first
|
||||
./install.py <--run this next
|
||||
```
|
||||
```
|
||||
note 1: be sure to run setup.sh and install.sh as root (or use sudo).
|
||||
note 2: if building from scratch the following scripts will need to
|
||||
be run in this order - setup.sh -> build.sh -> install.sh.
|
||||
note 1: the build script will search for the QT api installed in your
|
||||
system. if not found, it will ask you where it is. either way
|
||||
it is recommended to install the QT API before running this
|
||||
script.
|
||||
note 2: both scripts assume python3 is already installed.
|
||||
```
|
||||
|
|
233
build.py
Executable file
233
build.py
Executable file
|
@ -0,0 +1,233 @@
|
|||
#!/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(["qtpaths", "--binaries-dir"]), 'utf-8').strip()
|
||||
|
||||
except:
|
||||
print("A direct call to 'qtpaths' 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/lib"):
|
||||
os.makedirs("app_dir/lib")
|
||||
|
||||
verbose_copy("build/" + app_target, "app_dir/" + app_target)
|
||||
|
||||
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:
|
||||
#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")
|
||||
|
||||
else:
|
||||
verbose_copy(qt_bin + "/../lib/libQt6DBus.so.6", "app_dir/lib/libQt6DBus.so.6")
|
||||
|
||||
verbose_copy("templates/linux_run_script.sh", "app_dir/" + app_target + ".sh")
|
||||
verbose_copy("templates/linux_uninstall.sh", "app_dir/uninstall.sh")
|
||||
|
||||
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", "make", "g++"]
|
||||
dep_pkgs_b = ["ffmpeg", "libfuse-dev", "fuse3", "imagemagick"]
|
||||
|
||||
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()
|
5
build.sh
5
build.sh
|
@ -1,5 +0,0 @@
|
|||
#!/bin/sh
|
||||
mkdir -p ./.build-mow
|
||||
cd ./.build-mow
|
||||
cmake ..
|
||||
make -j4
|
|
@ -1,13 +0,0 @@
|
|||
#!/bin/sh
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
if [ ! -f "/usr/local/bin/magick" ]
|
||||
then
|
||||
apt install -y git
|
||||
git clone https://github.com/ImageMagick/ImageMagick.git .build-imagemagick
|
||||
cd .build-imagemagick
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
ldconfig /usr/local/lib
|
||||
fi
|
336
install.py
Executable file
336
install.py
Executable file
|
@ -0,0 +1,336 @@
|
|||
#!/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, false_on_fail):
|
||||
try:
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
return True
|
||||
|
||||
except:
|
||||
if false_on_fail:
|
||||
print("Failed to create directory: " + path + ", please make sure you are runnning this script with admin rights.")
|
||||
|
||||
return False
|
||||
|
||||
else:
|
||||
return True
|
||||
|
||||
def make_app_dirs(app_target):
|
||||
return make_install_dir("/etc/" + app_target, True) and make_install_dir("/opt/" + app_target, True) and make_install_dir("/var/buffer", False) and make_install_dir("/var/footage", 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_app_dirs(app_target):
|
||||
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)
|
||||
|
||||
verbose_copy("app_dir/" + app_target, install_dir + "/" + app_target)
|
||||
verbose_copy("app_dir/lib", install_dir + "/lib")
|
||||
|
||||
verbose_create_symmlink(install_dir + "/" + app_target + ".sh", "/usr/bin/" + app_target)
|
||||
|
||||
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"])
|
||||
|
||||
subprocess.run(["chmod", "777", "/var/buffer"])
|
||||
subprocess.run(["chmod", "777", "/var/footage"])
|
||||
|
||||
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", "libfuse-dev", "fuse3", "imagemagick"]
|
||||
|
||||
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)
|
54
install.sh
54
install.sh
|
@ -1,54 +0,0 @@
|
|||
#!/bin/sh
|
||||
if [ -f "/opt/mow/uninst" ]; then
|
||||
mow -u -f
|
||||
fi
|
||||
|
||||
if [ ! -d "/opt" ]; then
|
||||
mkdir /opt
|
||||
fi
|
||||
|
||||
if [ ! -d "/opt/mow" ]; then
|
||||
mkdir /opt/mow
|
||||
fi
|
||||
|
||||
if [ ! -d "/var/footage" ]; then
|
||||
mkdir /var/footage
|
||||
fi
|
||||
|
||||
if [ ! -d "/etc/mow" ]; then
|
||||
mkdir /etc/mow
|
||||
fi
|
||||
|
||||
if [ ! -d "/var/buffer" ]; then
|
||||
mkdir /var/buffer
|
||||
fi
|
||||
|
||||
cp -v ./.build-mow/mow /opt/mow/bin
|
||||
|
||||
echo "writing /opt/mow/run"
|
||||
printf "#!/bin/sh\n" > /opt/mow/run
|
||||
printf "/opt/mow/bin \$1 \$2 \$3\n" >> /opt/mow/run
|
||||
|
||||
echo "writing /opt/mow/uninst"
|
||||
printf "#!/bin/sh\n" > /opt/mow/uninst
|
||||
printf "mow -r\n" >> /opt/mow/uninst
|
||||
printf "rm -v /opt/mow/bin\n" >> /opt/mow/uninst
|
||||
printf "rm -v /opt/mow/run\n" >> /opt/mow/uninst
|
||||
printf "rm -v /opt/mow/uninst\n" >> /opt/mow/uninst
|
||||
printf "rm -v /usr/bin/mow\n" >> /opt/mow/uninst
|
||||
printf "rm -rv /opt/mow\n" >> /opt/mow/uninst
|
||||
printf "deluser mow\n" >> /opt/mow/uninst
|
||||
|
||||
useradd -r mow
|
||||
usermod -aG video mow
|
||||
|
||||
chown -R mow:mow /var/footage
|
||||
chown -R mow:mow /var/buffer
|
||||
|
||||
chmod -v +x /opt/mow/run
|
||||
chmod -v +x /opt/mow/bin
|
||||
chmod -v +x /opt/mow/uninst
|
||||
|
||||
ln -sv /opt/mow/run /usr/bin/mow
|
||||
|
||||
sh imgmagick_build.sh
|
342
installers/JustMotion-3.4.t4-Linux-x86_64.run
Executable file
342
installers/JustMotion-3.4.t4-Linux-x86_64.run
Executable file
File diff suppressed because one or more lines are too long
26
setup.sh
26
setup.sh
|
@ -1,26 +0,0 @@
|
|||
#!/bin/sh
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt update -y
|
||||
apt install -y pkg-config cmake make g++
|
||||
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
apt install -y ffmpeg
|
||||
apt install -y libavcodec-dev
|
||||
apt install -y libavformat-dev
|
||||
apt install -y libavutil-dev
|
||||
apt install -y libswscale-dev
|
||||
apt install -y x264
|
||||
apt install -y libx264-dev
|
||||
apt install -y libilmbase-dev
|
||||
apt install -y qt6-base-dev
|
||||
apt install -y qtchooser
|
||||
apt install -y qmake6
|
||||
apt install -y qt6-base-dev-tools
|
||||
apt install -y libxkbcommon-dev
|
||||
apt install -y libfuse-dev
|
||||
apt install -y fuse3
|
||||
sh imgmagick_build.sh
|
||||
fi
|
||||
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
// This file is part of Motion Watch.
|
||||
// This file is part of JustMotion.
|
||||
|
||||
// Motion Watch is free software: you can redistribute it and/or modify
|
||||
// JustMotion 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.
|
||||
|
||||
// Motion Watch is distributed in the hope that it will be useful,
|
||||
// JustMotion 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.
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#ifndef CAMERA_H
|
||||
#define CAMERA_H
|
||||
|
||||
// This file is part of Motion Watch.
|
||||
// This file is part of JustMotion.
|
||||
|
||||
// Motion Watch is free software: you can redistribute it and/or modify
|
||||
// JustMotion 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.
|
||||
|
||||
// Motion Watch is distributed in the hope that it will be useful,
|
||||
// JustMotion 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.
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// This file is part of Motion Watch.
|
||||
// This file is part of JustMotion.
|
||||
|
||||
// Motion Watch is free software: you can redistribute it and/or modify
|
||||
// JustMotion 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.
|
||||
|
||||
// Motion Watch is distributed in the hope that it will be useful,
|
||||
// JustMotion 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.
|
||||
|
@ -213,8 +213,6 @@ bool rdConf(const QString &filePath, shared_t *share)
|
|||
share->buffPath.clear();
|
||||
share->recPath.clear();
|
||||
share->servGroup.clear();
|
||||
|
||||
auto thrCount = QThread::idealThreadCount() / 2;
|
||||
|
||||
share->retCode = 0;
|
||||
share->imgThresh = 8000;
|
||||
|
@ -234,7 +232,7 @@ bool rdConf(const QString &filePath, shared_t *share)
|
|||
share->liveSecs = 80;
|
||||
share->recScale = "1280:720";
|
||||
share->imgScale = "320:240";
|
||||
share->servUser = APP_BIN;
|
||||
share->servUser = APP_TARGET;
|
||||
|
||||
QString line;
|
||||
|
||||
|
@ -317,8 +315,8 @@ bool rdConf(const QString &filePath, shared_t *share)
|
|||
share->servGroup = share->servUser;
|
||||
}
|
||||
|
||||
share->servMainLoop = QString(APP_BIN) + ".main_loop." + share->camName;
|
||||
share->servVidLoop = QString(APP_BIN) + ".vid_loop." + share->camName;
|
||||
share->servMainLoop = QString(APP_TARGET) + ".main_loop." + share->camName;
|
||||
share->servVidLoop = QString(APP_TARGET) + ".vid_loop." + share->camName;
|
||||
}
|
||||
|
||||
return share->retCode == 0;
|
||||
|
|
12
src/common.h
12
src/common.h
|
@ -1,14 +1,14 @@
|
|||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
// This file is part of Motion Watch.
|
||||
// This file is part of JustMotion.
|
||||
|
||||
// Motion Watch is free software: you can redistribute it and/or modify
|
||||
// JustMotion 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.
|
||||
|
||||
// Motion Watch is distributed in the hope that it will be useful,
|
||||
// JustMotion 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.
|
||||
|
@ -30,9 +30,9 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
#define APP_VER "3.4.t3"
|
||||
#define APP_NAME "Motion Watch"
|
||||
#define APP_BIN "mow"
|
||||
#define APP_VERSION "3.4.t4"
|
||||
#define APP_NAME "JustMotion"
|
||||
#define APP_TARGET "jmotion"
|
||||
#define DATETIME_FMT "yyyyMMddhhmmss"
|
||||
#define STRFTIME_FMT "%Y%m%d%H%M%S"
|
||||
#define PREV_IMG "&prev&"
|
||||
|
|
47
src/main.cpp
47
src/main.cpp
|
@ -1,11 +1,11 @@
|
|||
// This file is part of Motion Watch.
|
||||
// This file is part of JustMotion.
|
||||
|
||||
// Motion Watch is free software: you can redistribute it and/or modify
|
||||
// JustMotion 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.
|
||||
|
||||
// Motion Watch is distributed in the hope that it will be useful,
|
||||
// JustMotion 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.
|
||||
|
@ -14,13 +14,13 @@
|
|||
#include "camera.h"
|
||||
#include "services.h"
|
||||
|
||||
void showHelp()
|
||||
void showHelp(const QString etcDir)
|
||||
{
|
||||
QTextStream(stdout) << APP_NAME << " " << APP_VER << Qt::endl << Qt::endl;
|
||||
QTextStream(stdout) << "Usage: mow <argument>" << Qt::endl << Qt::endl;
|
||||
QTextStream(stdout) << APP_NAME << " " << APP_VERSION << Qt::endl << Qt::endl;
|
||||
QTextStream(stdout) << "Usage: " << APP_TARGET << " <argument>" << Qt::endl << Qt::endl;
|
||||
QTextStream(stdout) << "-h : display usage information about this application." << Qt::endl;
|
||||
QTextStream(stdout) << "-c : path to the config file used to run a single main loop instance." << Qt::endl;
|
||||
QTextStream(stdout) << "-i : all valid config files found in /etc/mow will be used to create" << Qt::endl;
|
||||
QTextStream(stdout) << "-i : all valid config files found in " << etcDir << " will be used to create" << Qt::endl;
|
||||
QTextStream(stdout) << " a full instance; full meaning main and vid loop systemd services" << Qt::endl;
|
||||
QTextStream(stdout) << " will be created for each config file." << Qt::endl;
|
||||
QTextStream(stdout) << "-d : this is the same as -i except it will not auto start the services." << Qt::endl;
|
||||
|
@ -28,8 +28,8 @@ void showHelp()
|
|||
QTextStream(stdout) << "-u : uninstall the entire app from your system, including all" << Qt::endl;
|
||||
QTextStream(stdout) << " systemd services related to it." << Qt::endl;
|
||||
QTextStream(stdout) << "-f : force an action without pausing for user confirmation." << Qt::endl;
|
||||
QTextStream(stdout) << "-l : list all mow services along with statuses." << Qt::endl;
|
||||
QTextStream(stdout) << "-r : remove all mow services." << Qt::endl;
|
||||
QTextStream(stdout) << "-l : list all attached services to this application along with statuses." << Qt::endl;
|
||||
QTextStream(stdout) << "-r : remove all attached services." << Qt::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
@ -37,34 +37,35 @@ int main(int argc, char** argv)
|
|||
QCoreApplication app(argc, argv);
|
||||
|
||||
QCoreApplication::setApplicationName(APP_NAME);
|
||||
QCoreApplication::setApplicationVersion(APP_VER);
|
||||
QCoreApplication::setApplicationVersion(APP_VERSION);
|
||||
|
||||
auto args = QCoreApplication::arguments();
|
||||
auto ret = 0;
|
||||
auto args = QCoreApplication::arguments();
|
||||
auto etcDir = "/etc/" + QString(APP_TARGET);
|
||||
auto ret = 0;
|
||||
|
||||
if (args.contains("-h"))
|
||||
{
|
||||
showHelp();
|
||||
showHelp(etcDir);
|
||||
}
|
||||
else if (args.contains("-v"))
|
||||
{
|
||||
QTextStream(stdout) << APP_VER << Qt::endl;
|
||||
QTextStream(stdout) << APP_VERSION << Qt::endl;
|
||||
}
|
||||
else if (args.contains("-l"))
|
||||
{
|
||||
servStatByDir("/etc/mow");
|
||||
servStatByDir(etcDir);
|
||||
}
|
||||
else if (args.contains("-i") || args.contains("-d"))
|
||||
{
|
||||
ret = rmServiceByDir("/etc/mow");
|
||||
ret = rmServiceByDir(etcDir);
|
||||
|
||||
if ((ret == 0) && args.contains("-i"))
|
||||
{
|
||||
ret = loadServiceByDir("/etc/mow", true);
|
||||
ret = loadServiceByDir(etcDir, true);
|
||||
}
|
||||
else if (ret == 0)
|
||||
{
|
||||
ret = loadServiceByDir("/etc/mow", false);
|
||||
ret = loadServiceByDir(etcDir, false);
|
||||
}
|
||||
}
|
||||
else if (args.contains("-c"))
|
||||
|
@ -82,11 +83,11 @@ int main(int argc, char** argv)
|
|||
{
|
||||
if (args.contains("-f"))
|
||||
{
|
||||
ret = rmServiceByDir("/etc/mow");
|
||||
ret = rmServiceByDir(etcDir);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = QProcess::execute("/opt/mow/uninst", QStringList());
|
||||
ret = QProcess::execute("/opt/" + QString(APP_TARGET) + "/uninstall.sh", QStringList());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -102,18 +103,18 @@ int main(int argc, char** argv)
|
|||
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = QProcess::execute("/opt/mow/uninst", QStringList());
|
||||
ret = QProcess::execute("/opt/" + QString(APP_TARGET) + "/uninstall.sh", QStringList());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (args.contains("-r"))
|
||||
{
|
||||
ret = rmServiceByDir("/etc/mow");
|
||||
ret = rmServiceByDir(etcDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
showHelp();
|
||||
showHelp(etcDir);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// This file is part of Motion Watch.
|
||||
// This file is part of JustMotion.
|
||||
|
||||
// Motion Watch is free software: you can redistribute it and/or modify
|
||||
// JustMotion 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.
|
||||
|
||||
// Motion Watch is distributed in the hope that it will be useful,
|
||||
// JustMotion 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.
|
||||
|
@ -13,7 +13,9 @@
|
|||
#include "services.h"
|
||||
|
||||
int loadService(const QString &desc, const QString &user, const QString &servName, const QString &workDir, const QString &recPath, bool start)
|
||||
{
|
||||
{
|
||||
Q_UNUSED(recPath);
|
||||
|
||||
QFile file("/lib/systemd/system/" + servName + ".service");
|
||||
|
||||
auto ret = 0;
|
||||
|
@ -108,7 +110,7 @@ QString camCmdFromConf(shared_t *conf, CmdExeType type)
|
|||
|
||||
if (type == MAIN_LOOP)
|
||||
{
|
||||
ret += QString(APP_BIN) + " -c " + conf->conf;
|
||||
ret += QString(APP_TARGET) + " -c " + conf->conf;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#ifndef SERVICES_H
|
||||
#define SERVICES_H
|
||||
|
||||
// This file is part of Motion Watch.
|
||||
// This file is part of JustMotion.
|
||||
|
||||
// Motion Watch is free software: you can redistribute it and/or modify
|
||||
// JustMotion 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.
|
||||
|
||||
// Motion Watch is distributed in the hope that it will be useful,
|
||||
// JustMotion 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.
|
||||
|
|
5
templates/linux_run_script.sh
Normal file
5
templates/linux_run_script.sh
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
export QTDIR=$install_dir
|
||||
export QT_PLUGIN_PATH=$install_dir
|
||||
export LD_LIBRARY_PATH=$install_dir/lib
|
||||
$install_dir/$app_target $1 $2 $3
|
6
templates/linux_uninstall.sh
Normal file
6
templates/linux_uninstall.sh
Normal file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
$app_target -r
|
||||
rm -v /usr/bin/$app_target
|
||||
rm -rv $install_dir
|
||||
deluser $app_target
|
||||
echo "Uninstallation Complete"
|
Loading…
Reference in New Issue
Block a user