Compare commits
No commits in common. "33519acf7a4880aa8173b0037301c5caef2c60c2" and "970b9b5fc5647b6762a7f14636c447cce05b2911" have entirely different histories.
33519acf7a
...
970b9b5fc5
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -30,7 +30,6 @@ Makefile*
|
|||
*build-*
|
||||
/build
|
||||
/app_dir
|
||||
/installers
|
||||
/release
|
||||
/debug
|
||||
|
||||
|
@ -55,3 +54,7 @@ compile_commands.json
|
|||
|
||||
# VSCode
|
||||
/.vscode
|
||||
|
||||
# Build folders
|
||||
/.build-mow
|
||||
/.build-imagemagick
|
||||
|
|
29
CMakeLists.txt
Normal file
29
CMakeLists.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
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})
|
|
@ -1,20 +0,0 @@
|
|||
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
|
74
README.md
74
README.md
|
@ -1,38 +1,21 @@
|
|||
# JustMotion #
|
||||
# Motion Watch #
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
### Usage ###
|
||||
|
||||
```
|
||||
Usage: jmotion <argument>
|
||||
Usage: mow <argument>
|
||||
|
||||
-h : display usage information about this application.
|
||||
-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.
|
||||
-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.
|
||||
-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 ###
|
||||
|
@ -95,20 +78,14 @@ img_comp_out = stderr
|
|||
# use to output the comparison score. this can only be stderr or stdout,
|
||||
# any other stream name is considered invalid.
|
||||
#
|
||||
vid_codec = copy
|
||||
stream_codec = copy
|
||||
# this is the encoding codec to use when recording footage from the camera.
|
||||
# the list of supported codecs entirely depend on the hosts' ffmpeg
|
||||
# installation. run 'ffmpeg -codecs' to determine this list. if not
|
||||
# defined, 'copy' will be used as in it will just copy the codec format
|
||||
# from the camera itself.
|
||||
# from camera itself.
|
||||
#
|
||||
aud_codec = copy
|
||||
# this is the audio encoding codec to use when recording from the camera.
|
||||
# the list of supported audio codes can be determined by running 'ffmpeg
|
||||
# -codecs' on the host machine. if not defined, 'copy' will be used as in
|
||||
# it will directly copy the audio stream from the camera if present.
|
||||
#
|
||||
stream_ext = .mkv
|
||||
stream_ext = .avi
|
||||
# this is the file extension that will be used to when recording footage
|
||||
# from the camera in buffer_path. ffmpeg will also use this to determine
|
||||
# what format container to use for the video clips.
|
||||
|
@ -117,7 +94,7 @@ thumbnail_ext = .jpg
|
|||
# this the image format that will be used when creating the thumbnails
|
||||
# for the videos clips recorded to rec_path.
|
||||
#
|
||||
rec_ext = .mkv
|
||||
rec_ext = .avi
|
||||
# this the the file extension that will be used when storing motion footage
|
||||
# to rec_path. ffmpeg will also use this to determine what format container
|
||||
# to use for the video clips.
|
||||
|
@ -144,12 +121,13 @@ post_cmd = move_the_ptz_camera.py
|
|||
#
|
||||
rec_fps = 30
|
||||
# this sets the recording frames per second for the footage recorded
|
||||
# from the camera. this has no affect if using 'copy' as the vid_codec.
|
||||
# from the camera. this has no affect if using 'copy' as the
|
||||
# stream_codec.
|
||||
#
|
||||
rec_scale = 1280:720
|
||||
# this sets the pixel scale of the recorded footage from the camera. it
|
||||
# uses width, height numeric strings seperated by a colon, eg W:H. this
|
||||
# has no affect of using 'copy' as the vid_codec.
|
||||
# has no affect of using 'copy' as the stream_codec.
|
||||
#
|
||||
img_scale = 320:240
|
||||
# this sets the pixel size of the thumbnails for recorded stored in
|
||||
|
@ -168,20 +146,18 @@ service_group = mow
|
|||
# service_user will be used.
|
||||
```
|
||||
|
||||
### Build/Install ###
|
||||
### Setup/Build/Install ###
|
||||
|
||||
This application is currently only compatible with a Linux based operating
|
||||
systems that are capable of installing python3 and the QT API (QT6.X.X or
|
||||
better).
|
||||
systems that are capable of installing the QT API.
|
||||
|
||||
```
|
||||
./build.py <--run this first
|
||||
./install.py <--run this next
|
||||
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
|
||||
```
|
||||
```
|
||||
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.
|
||||
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.
|
||||
```
|
||||
|
|
233
build.py
233
build.py
|
@ -1,233 +0,0 @@
|
|||
#!/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 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")
|
||||
|
||||
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
Normal file
5
build.sh
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
mkdir -p ./.build-mow
|
||||
cd ./.build-mow
|
||||
cmake ..
|
||||
make -j4
|
13
imgmagick_build.sh
Normal file
13
imgmagick_build.sh
Normal file
|
@ -0,0 +1,13 @@
|
|||
#!/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
336
install.py
|
@ -1,336 +0,0 @@
|
|||
#!/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", 10322)
|
||||
|
||||
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
Normal file
54
install.sh
Normal file
|
@ -0,0 +1,54 @@
|
|||
#!/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
|
26
setup.sh
Normal file
26
setup.sh
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/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 JustMotion.
|
||||
// This file is part of Motion Watch.
|
||||
|
||||
// JustMotion is free software: you can redistribute it and/or modify
|
||||
// Motion Watch 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.
|
||||
|
||||
// JustMotion is distributed in the hope that it will be useful,
|
||||
// Motion Watch 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 JustMotion.
|
||||
// This file is part of Motion Watch.
|
||||
|
||||
// JustMotion is free software: you can redistribute it and/or modify
|
||||
// Motion Watch 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.
|
||||
|
||||
// JustMotion is distributed in the hope that it will be useful,
|
||||
// Motion Watch 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 JustMotion.
|
||||
// This file is part of Motion Watch.
|
||||
|
||||
// JustMotion is free software: you can redistribute it and/or modify
|
||||
// Motion Watch 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.
|
||||
|
||||
// JustMotion is distributed in the hope that it will be useful,
|
||||
// Motion Watch 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.
|
||||
|
@ -214,25 +214,26 @@ bool rdConf(const QString &filePath, shared_t *share)
|
|||
share->recPath.clear();
|
||||
share->servGroup.clear();
|
||||
|
||||
share->retCode = 0;
|
||||
share->imgThresh = 8000;
|
||||
share->maxEvents = 30;
|
||||
share->skipCmd = false;
|
||||
share->postSecs = 60;
|
||||
share->evMaxSecs = 30;
|
||||
share->conf = filePath;
|
||||
share->outputType = "stderr";
|
||||
share->compCmd = "magick compare -metric FUZZ " + QString(PREV_IMG) + " " + QString(NEXT_IMG) + " /dev/null";
|
||||
share->vidCodec = "copy";
|
||||
share->audCodec = "copy";
|
||||
share->streamExt = ".mkv";
|
||||
share->recExt = ".mkv";
|
||||
share->thumbExt = ".jpg";
|
||||
share->recFps = 30;
|
||||
share->liveSecs = 80;
|
||||
share->recScale = "1280:720";
|
||||
share->imgScale = "320:240";
|
||||
share->servUser = APP_TARGET;
|
||||
auto thrCount = QThread::idealThreadCount() / 2;
|
||||
|
||||
share->retCode = 0;
|
||||
share->imgThresh = 8000;
|
||||
share->maxEvents = 30;
|
||||
share->skipCmd = false;
|
||||
share->postSecs = 60;
|
||||
share->evMaxSecs = 30;
|
||||
share->conf = filePath;
|
||||
share->outputType = "stderr";
|
||||
share->compCmd = "magick compare -metric FUZZ " + QString(PREV_IMG) + " " + QString(NEXT_IMG) + " /dev/null";
|
||||
share->streamCodec = "copy";
|
||||
share->streamExt = ".avi";
|
||||
share->recExt = ".avi";
|
||||
share->thumbExt = ".jpg";
|
||||
share->recFps = 30;
|
||||
share->liveSecs = 80;
|
||||
share->recScale = "1280:720";
|
||||
share->imgScale = "320:240";
|
||||
share->servUser = APP_BIN;
|
||||
|
||||
QString line;
|
||||
|
||||
|
@ -253,8 +254,7 @@ bool rdConf(const QString &filePath, shared_t *share)
|
|||
rdLine("max_events = ", line, &share->maxEvents);
|
||||
rdLine("img_comp_out = ", line, &share->outputType);
|
||||
rdLine("img_comp_cmd = ", line, &share->compCmd);
|
||||
rdLine("vid_codec = ", line, &share->vidCodec);
|
||||
rdLine("aud_codec = ", line, &share->audCodec);
|
||||
rdLine("stream_codec = ", line, &share->streamCodec);
|
||||
rdLine("stream_ext = ", line, &share->streamExt);
|
||||
rdLine("rec_ext = ", line, &share->recExt);
|
||||
rdLine("thumbnail_ext = ", line, &share->thumbExt);
|
||||
|
@ -315,8 +315,8 @@ bool rdConf(const QString &filePath, shared_t *share)
|
|||
share->servGroup = share->servUser;
|
||||
}
|
||||
|
||||
share->servMainLoop = QString(APP_TARGET) + ".main_loop." + share->camName;
|
||||
share->servVidLoop = QString(APP_TARGET) + ".vid_loop." + share->camName;
|
||||
share->servMainLoop = QString(APP_BIN) + ".main_loop." + share->camName;
|
||||
share->servVidLoop = QString(APP_BIN) + ".vid_loop." + share->camName;
|
||||
}
|
||||
|
||||
return share->retCode == 0;
|
||||
|
|
15
src/common.h
15
src/common.h
|
@ -1,14 +1,14 @@
|
|||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
// This file is part of JustMotion.
|
||||
// This file is part of Motion Watch.
|
||||
|
||||
// JustMotion is free software: you can redistribute it and/or modify
|
||||
// Motion Watch 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.
|
||||
|
||||
// JustMotion is distributed in the hope that it will be useful,
|
||||
// Motion Watch 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_VERSION "3.4"
|
||||
#define APP_NAME "JustMotion"
|
||||
#define APP_TARGET "jmotion"
|
||||
#define APP_VER "3.3"
|
||||
#define APP_NAME "Motion Watch"
|
||||
#define APP_BIN "mow"
|
||||
#define DATETIME_FMT "yyyyMMddhhmmss"
|
||||
#define STRFTIME_FMT "%Y%m%d%H%M%S"
|
||||
#define PREV_IMG "&prev&"
|
||||
|
@ -65,8 +65,7 @@ struct shared_t
|
|||
QString recPath;
|
||||
QString outputType;
|
||||
QString compCmd;
|
||||
QString vidCodec;
|
||||
QString audCodec;
|
||||
QString streamCodec;
|
||||
QString streamExt;
|
||||
QString recExt;
|
||||
QString thumbExt;
|
||||
|
|
47
src/main.cpp
47
src/main.cpp
|
@ -1,11 +1,11 @@
|
|||
// This file is part of JustMotion.
|
||||
// This file is part of Motion Watch.
|
||||
|
||||
// JustMotion is free software: you can redistribute it and/or modify
|
||||
// Motion Watch 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.
|
||||
|
||||
// JustMotion is distributed in the hope that it will be useful,
|
||||
// Motion Watch 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(const QString etcDir)
|
||||
void showHelp()
|
||||
{
|
||||
QTextStream(stdout) << APP_NAME << " " << APP_VERSION << Qt::endl << Qt::endl;
|
||||
QTextStream(stdout) << "Usage: " << APP_TARGET << " <argument>" << Qt::endl << Qt::endl;
|
||||
QTextStream(stdout) << APP_NAME << " " << APP_VER << Qt::endl << Qt::endl;
|
||||
QTextStream(stdout) << "Usage: mow <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 " << etcDir << " will be used to create" << Qt::endl;
|
||||
QTextStream(stdout) << "-i : all valid config files found in /etc/mow 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(const QString etcDir)
|
|||
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 attached services to this application along with statuses." << Qt::endl;
|
||||
QTextStream(stdout) << "-r : remove all attached services." << Qt::endl;
|
||||
QTextStream(stdout) << "-l : list all mow services along with statuses." << Qt::endl;
|
||||
QTextStream(stdout) << "-r : remove all mow services." << Qt::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
@ -37,35 +37,34 @@ int main(int argc, char** argv)
|
|||
QCoreApplication app(argc, argv);
|
||||
|
||||
QCoreApplication::setApplicationName(APP_NAME);
|
||||
QCoreApplication::setApplicationVersion(APP_VERSION);
|
||||
QCoreApplication::setApplicationVersion(APP_VER);
|
||||
|
||||
auto args = QCoreApplication::arguments();
|
||||
auto etcDir = "/etc/" + QString(APP_TARGET);
|
||||
auto ret = 0;
|
||||
auto args = QCoreApplication::arguments();
|
||||
auto ret = 0;
|
||||
|
||||
if (args.contains("-h"))
|
||||
{
|
||||
showHelp(etcDir);
|
||||
showHelp();
|
||||
}
|
||||
else if (args.contains("-v"))
|
||||
{
|
||||
QTextStream(stdout) << APP_VERSION << Qt::endl;
|
||||
QTextStream(stdout) << APP_VER << Qt::endl;
|
||||
}
|
||||
else if (args.contains("-l"))
|
||||
{
|
||||
servStatByDir(etcDir);
|
||||
servStatByDir("/etc/mow");
|
||||
}
|
||||
else if (args.contains("-i") || args.contains("-d"))
|
||||
{
|
||||
ret = rmServiceByDir(etcDir);
|
||||
ret = rmServiceByDir("/etc/mow");
|
||||
|
||||
if ((ret == 0) && args.contains("-i"))
|
||||
{
|
||||
ret = loadServiceByDir(etcDir, true);
|
||||
ret = loadServiceByDir("/etc/mow", true);
|
||||
}
|
||||
else if (ret == 0)
|
||||
{
|
||||
ret = loadServiceByDir(etcDir, false);
|
||||
ret = loadServiceByDir("/etc/mow", false);
|
||||
}
|
||||
}
|
||||
else if (args.contains("-c"))
|
||||
|
@ -83,11 +82,11 @@ int main(int argc, char** argv)
|
|||
{
|
||||
if (args.contains("-f"))
|
||||
{
|
||||
ret = rmServiceByDir(etcDir);
|
||||
ret = rmServiceByDir("/etc/mow");
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = QProcess::execute("/opt/" + QString(APP_TARGET) + "/uninstall.sh", QStringList());
|
||||
ret = QProcess::execute("/opt/mow/uninst", QStringList());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -103,18 +102,18 @@ int main(int argc, char** argv)
|
|||
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = QProcess::execute("/opt/" + QString(APP_TARGET) + "/uninstall.sh", QStringList());
|
||||
ret = QProcess::execute("/opt/mow/uninst", QStringList());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (args.contains("-r"))
|
||||
{
|
||||
ret = rmServiceByDir(etcDir);
|
||||
ret = rmServiceByDir("/etc/mow");
|
||||
}
|
||||
else
|
||||
{
|
||||
showHelp(etcDir);
|
||||
showHelp();
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// This file is part of JustMotion.
|
||||
// This file is part of Motion Watch.
|
||||
|
||||
// JustMotion is free software: you can redistribute it and/or modify
|
||||
// Motion Watch 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.
|
||||
|
||||
// JustMotion is distributed in the hope that it will be useful,
|
||||
// Motion Watch 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,8 +14,6 @@
|
|||
|
||||
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;
|
||||
|
@ -45,12 +43,7 @@ int loadService(const QString &desc, const QString &user, const QString &servNam
|
|||
file.write("Type=simple\n");
|
||||
file.write("User=" + user.toUtf8() + "\n");
|
||||
file.write("Restart=always\n");
|
||||
|
||||
if (servName.contains("vid_loop"))
|
||||
{
|
||||
file.write("RuntimeMaxSec=61\n");
|
||||
}
|
||||
|
||||
file.write("TimeoutStopSec=infinity\n");
|
||||
file.write("ExecStart=/usr/bin/env " + servName.toUtf8() + "\n\n");
|
||||
file.write("[Install]\n");
|
||||
file.write("WantedBy=multi-user.target\n");
|
||||
|
@ -97,6 +90,7 @@ int loadSh(const QString &name, const QString &exeCmd, const QString &buffDir, c
|
|||
|
||||
QProcess::execute("chmod", {"-v", "+x", file.fileName()});
|
||||
QProcess::execute("chown", {servUser + ":" + servGroup, "-R", buffDir});
|
||||
QProcess::execute("chown", {servUser + ":" + servGroup, "-R", outDir});
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
@ -110,7 +104,7 @@ QString camCmdFromConf(shared_t *conf, CmdExeType type)
|
|||
|
||||
if (type == MAIN_LOOP)
|
||||
{
|
||||
ret += QString(APP_TARGET) + " -c " + conf->conf;
|
||||
ret += QString(APP_BIN) + " -c " + conf->conf;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -118,16 +112,15 @@ QString camCmdFromConf(shared_t *conf, CmdExeType type)
|
|||
|
||||
if (conf->recordUri.contains("rtsp"))
|
||||
{
|
||||
ret += "-rtsp_transport udp ";
|
||||
ret += "-rtsp_transport tcp ";
|
||||
}
|
||||
|
||||
if (conf->vidCodec != "copy")
|
||||
if (conf->streamCodec != "copy")
|
||||
{
|
||||
ret += "-vf fps=" + QString::number(conf->recFps) + ",scale=" + conf->recScale + " ";
|
||||
}
|
||||
|
||||
ret += "-vcodec " + conf->vidCodec + " ";
|
||||
ret += "-acodec " + conf->audCodec + " ";
|
||||
ret += "-vcodec " + conf->streamCodec + " ";
|
||||
ret += "-reset_timestamps 1 -sc_threshold 0 -g 2 -force_key_frames \"expr:gte(t, n_forced * 2)\" -t 60 -segment_time 2 -f segment ";
|
||||
ret += conf->buffPath + "/live/" + QString(STRFTIME_FMT) + conf->streamExt;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#ifndef SERVICES_H
|
||||
#define SERVICES_H
|
||||
|
||||
// This file is part of JustMotion.
|
||||
// This file is part of Motion Watch.
|
||||
|
||||
// JustMotion is free software: you can redistribute it and/or modify
|
||||
// Motion Watch 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.
|
||||
|
||||
// JustMotion is distributed in the hope that it will be useful,
|
||||
// Motion Watch 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,5 +0,0 @@
|
|||
#!/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
|
|
@ -1,6 +0,0 @@
|
|||
#!/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