Compare commits
5 Commits
970b9b5fc5
...
33519acf7a
Author | SHA1 | Date | |
---|---|---|---|
|
33519acf7a | ||
|
553585e275 | ||
|
c5393484c2 | ||
|
330393667e | ||
|
4ce8a75d9d |
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -30,6 +30,7 @@ Makefile*
|
||||||
*build-*
|
*build-*
|
||||||
/build
|
/build
|
||||||
/app_dir
|
/app_dir
|
||||||
|
/installers
|
||||||
/release
|
/release
|
||||||
/debug
|
/debug
|
||||||
|
|
||||||
|
@ -54,7 +55,3 @@ compile_commands.json
|
||||||
|
|
||||||
# VSCode
|
# VSCode
|
||||||
/.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
|
74
README.md
74
README.md
|
@ -1,21 +1,38 @@
|
||||||
# Motion Watch #
|
# JustMotion #
|
||||||
|
|
||||||
Motion Watch is a video surveillance application that monitors the video feeds
|
JustMotion is a video surveillance application that monitors the video feeds
|
||||||
of an IP camera and records only footage that contains motion. The main
|
of an IP or local camera and records only footage that contains motion. The
|
||||||
advantage of this is reduced storage requirements as opposed to continuous
|
main advantage of this is reduced storage requirements as opposed to
|
||||||
recording because only video footage of interest is recorded to storage.
|
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 ###
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage: mow <argument>
|
Usage: jmotion <argument>
|
||||||
|
|
||||||
-h : display usage information about this application.
|
-h : display usage information about this application.
|
||||||
-c : path to the config file used to run a single camera instance.
|
-c : path to the config file used to run a single main loop instance.
|
||||||
-d : path to a directory that can contain multiple config files.
|
-i : all valid config files found in /etc/jmotion will be used to create
|
||||||
each file found in the directory will be used to run a
|
a full instance; full meaning main and vid loop systemd services
|
||||||
camera instance.
|
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.
|
-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 ###
|
### Config File ###
|
||||||
|
@ -78,14 +95,20 @@ img_comp_out = stderr
|
||||||
# use to output the comparison score. this can only be stderr or stdout,
|
# use to output the comparison score. this can only be stderr or stdout,
|
||||||
# any other stream name is considered invalid.
|
# any other stream name is considered invalid.
|
||||||
#
|
#
|
||||||
stream_codec = copy
|
vid_codec = copy
|
||||||
# this is the encoding codec to use when recording footage from the camera.
|
# this is the encoding codec to use when recording footage from the camera.
|
||||||
# the list of supported codecs entirely depend on the hosts' ffmpeg
|
# the list of supported codecs entirely depend on the hosts' ffmpeg
|
||||||
# installation. run 'ffmpeg -codecs' to determine this list. if not
|
# installation. run 'ffmpeg -codecs' to determine this list. if not
|
||||||
# defined, 'copy' will be used as in it will just copy the codec format
|
# defined, 'copy' will be used as in it will just copy the codec format
|
||||||
# from camera itself.
|
# from the camera itself.
|
||||||
#
|
#
|
||||||
stream_ext = .avi
|
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
|
||||||
# this is the file extension that will be used to when recording footage
|
# 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
|
# from the camera in buffer_path. ffmpeg will also use this to determine
|
||||||
# what format container to use for the video clips.
|
# what format container to use for the video clips.
|
||||||
|
@ -94,7 +117,7 @@ thumbnail_ext = .jpg
|
||||||
# this the image format that will be used when creating the thumbnails
|
# this the image format that will be used when creating the thumbnails
|
||||||
# for the videos clips recorded to rec_path.
|
# for the videos clips recorded to rec_path.
|
||||||
#
|
#
|
||||||
rec_ext = .avi
|
rec_ext = .mkv
|
||||||
# this the the file extension that will be used when storing motion footage
|
# 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 rec_path. ffmpeg will also use this to determine what format container
|
||||||
# to use for the video clips.
|
# to use for the video clips.
|
||||||
|
@ -121,13 +144,12 @@ post_cmd = move_the_ptz_camera.py
|
||||||
#
|
#
|
||||||
rec_fps = 30
|
rec_fps = 30
|
||||||
# this sets the recording frames per second for the footage recorded
|
# this sets the recording frames per second for the footage recorded
|
||||||
# from the camera. this has no affect if using 'copy' as the
|
# from the camera. this has no affect if using 'copy' as the vid_codec.
|
||||||
# stream_codec.
|
|
||||||
#
|
#
|
||||||
rec_scale = 1280:720
|
rec_scale = 1280:720
|
||||||
# this sets the pixel scale of the recorded footage from the camera. it
|
# 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
|
# uses width, height numeric strings seperated by a colon, eg W:H. this
|
||||||
# has no affect of using 'copy' as the stream_codec.
|
# has no affect of using 'copy' as the vid_codec.
|
||||||
#
|
#
|
||||||
img_scale = 320:240
|
img_scale = 320:240
|
||||||
# this sets the pixel size of the thumbnails for recorded stored in
|
# this sets the pixel size of the thumbnails for recorded stored in
|
||||||
|
@ -146,18 +168,20 @@ service_group = mow
|
||||||
# service_user will be used.
|
# service_user will be used.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Setup/Build/Install ###
|
### Build/Install ###
|
||||||
|
|
||||||
This application is currently only compatible with a Linux based operating
|
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
|
./build.py <--run this first
|
||||||
sh ./build.sh time or if upgrading from the ground up.
|
./install.py <--run this next
|
||||||
sh ./install.sh
|
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
note 1: be sure to run setup.sh and install.sh as root (or use sudo).
|
note 1: the build script will search for the QT api installed in your
|
||||||
note 2: if building from scratch the following scripts will need to
|
system. if not found, it will ask you where it is. either way
|
||||||
be run in this order - setup.sh -> build.sh -> install.sh.
|
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 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
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", 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
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
|
|
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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (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
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
#ifndef CAMERA_H
|
#ifndef CAMERA_H
|
||||||
#define 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (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
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (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
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
|
@ -214,8 +214,6 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
share->recPath.clear();
|
share->recPath.clear();
|
||||||
share->servGroup.clear();
|
share->servGroup.clear();
|
||||||
|
|
||||||
auto thrCount = QThread::idealThreadCount() / 2;
|
|
||||||
|
|
||||||
share->retCode = 0;
|
share->retCode = 0;
|
||||||
share->imgThresh = 8000;
|
share->imgThresh = 8000;
|
||||||
share->maxEvents = 30;
|
share->maxEvents = 30;
|
||||||
|
@ -225,15 +223,16 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
share->conf = filePath;
|
share->conf = filePath;
|
||||||
share->outputType = "stderr";
|
share->outputType = "stderr";
|
||||||
share->compCmd = "magick compare -metric FUZZ " + QString(PREV_IMG) + " " + QString(NEXT_IMG) + " /dev/null";
|
share->compCmd = "magick compare -metric FUZZ " + QString(PREV_IMG) + " " + QString(NEXT_IMG) + " /dev/null";
|
||||||
share->streamCodec = "copy";
|
share->vidCodec = "copy";
|
||||||
share->streamExt = ".avi";
|
share->audCodec = "copy";
|
||||||
share->recExt = ".avi";
|
share->streamExt = ".mkv";
|
||||||
|
share->recExt = ".mkv";
|
||||||
share->thumbExt = ".jpg";
|
share->thumbExt = ".jpg";
|
||||||
share->recFps = 30;
|
share->recFps = 30;
|
||||||
share->liveSecs = 80;
|
share->liveSecs = 80;
|
||||||
share->recScale = "1280:720";
|
share->recScale = "1280:720";
|
||||||
share->imgScale = "320:240";
|
share->imgScale = "320:240";
|
||||||
share->servUser = APP_BIN;
|
share->servUser = APP_TARGET;
|
||||||
|
|
||||||
QString line;
|
QString line;
|
||||||
|
|
||||||
|
@ -254,7 +253,8 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
rdLine("max_events = ", line, &share->maxEvents);
|
rdLine("max_events = ", line, &share->maxEvents);
|
||||||
rdLine("img_comp_out = ", line, &share->outputType);
|
rdLine("img_comp_out = ", line, &share->outputType);
|
||||||
rdLine("img_comp_cmd = ", line, &share->compCmd);
|
rdLine("img_comp_cmd = ", line, &share->compCmd);
|
||||||
rdLine("stream_codec = ", line, &share->streamCodec);
|
rdLine("vid_codec = ", line, &share->vidCodec);
|
||||||
|
rdLine("aud_codec = ", line, &share->audCodec);
|
||||||
rdLine("stream_ext = ", line, &share->streamExt);
|
rdLine("stream_ext = ", line, &share->streamExt);
|
||||||
rdLine("rec_ext = ", line, &share->recExt);
|
rdLine("rec_ext = ", line, &share->recExt);
|
||||||
rdLine("thumbnail_ext = ", line, &share->thumbExt);
|
rdLine("thumbnail_ext = ", line, &share->thumbExt);
|
||||||
|
@ -315,8 +315,8 @@ bool rdConf(const QString &filePath, shared_t *share)
|
||||||
share->servGroup = share->servUser;
|
share->servGroup = share->servUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
share->servMainLoop = QString(APP_BIN) + ".main_loop." + share->camName;
|
share->servMainLoop = QString(APP_TARGET) + ".main_loop." + share->camName;
|
||||||
share->servVidLoop = QString(APP_BIN) + ".vid_loop." + share->camName;
|
share->servVidLoop = QString(APP_TARGET) + ".vid_loop." + share->camName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return share->retCode == 0;
|
return share->retCode == 0;
|
||||||
|
|
15
src/common.h
15
src/common.h
|
@ -1,14 +1,14 @@
|
||||||
#ifndef COMMON_H
|
#ifndef COMMON_H
|
||||||
#define 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (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
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
|
@ -30,9 +30,9 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#define APP_VER "3.3"
|
#define APP_VERSION "3.4"
|
||||||
#define APP_NAME "Motion Watch"
|
#define APP_NAME "JustMotion"
|
||||||
#define APP_BIN "mow"
|
#define APP_TARGET "jmotion"
|
||||||
#define DATETIME_FMT "yyyyMMddhhmmss"
|
#define DATETIME_FMT "yyyyMMddhhmmss"
|
||||||
#define STRFTIME_FMT "%Y%m%d%H%M%S"
|
#define STRFTIME_FMT "%Y%m%d%H%M%S"
|
||||||
#define PREV_IMG "&prev&"
|
#define PREV_IMG "&prev&"
|
||||||
|
@ -65,7 +65,8 @@ struct shared_t
|
||||||
QString recPath;
|
QString recPath;
|
||||||
QString outputType;
|
QString outputType;
|
||||||
QString compCmd;
|
QString compCmd;
|
||||||
QString streamCodec;
|
QString vidCodec;
|
||||||
|
QString audCodec;
|
||||||
QString streamExt;
|
QString streamExt;
|
||||||
QString recExt;
|
QString recExt;
|
||||||
QString thumbExt;
|
QString thumbExt;
|
||||||
|
|
43
src/main.cpp
43
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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (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
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
|
@ -14,13 +14,13 @@
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "services.h"
|
#include "services.h"
|
||||||
|
|
||||||
void showHelp()
|
void showHelp(const QString etcDir)
|
||||||
{
|
{
|
||||||
QTextStream(stdout) << APP_NAME << " " << APP_VER << Qt::endl << Qt::endl;
|
QTextStream(stdout) << APP_NAME << " " << APP_VERSION << Qt::endl << Qt::endl;
|
||||||
QTextStream(stdout) << "Usage: mow <argument>" << 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) << "-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) << "-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) << " 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) << " 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;
|
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) << "-u : uninstall the entire app from your system, including all" << Qt::endl;
|
||||||
QTextStream(stdout) << " systemd services related to it." << 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) << "-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) << "-l : list all attached services to this application along with statuses." << Qt::endl;
|
||||||
QTextStream(stdout) << "-r : remove all mow services." << Qt::endl;
|
QTextStream(stdout) << "-r : remove all attached services." << Qt::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
|
@ -37,34 +37,35 @@ int main(int argc, char** argv)
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
QCoreApplication::setApplicationName(APP_NAME);
|
QCoreApplication::setApplicationName(APP_NAME);
|
||||||
QCoreApplication::setApplicationVersion(APP_VER);
|
QCoreApplication::setApplicationVersion(APP_VERSION);
|
||||||
|
|
||||||
auto args = QCoreApplication::arguments();
|
auto args = QCoreApplication::arguments();
|
||||||
|
auto etcDir = "/etc/" + QString(APP_TARGET);
|
||||||
auto ret = 0;
|
auto ret = 0;
|
||||||
|
|
||||||
if (args.contains("-h"))
|
if (args.contains("-h"))
|
||||||
{
|
{
|
||||||
showHelp();
|
showHelp(etcDir);
|
||||||
}
|
}
|
||||||
else if (args.contains("-v"))
|
else if (args.contains("-v"))
|
||||||
{
|
{
|
||||||
QTextStream(stdout) << APP_VER << Qt::endl;
|
QTextStream(stdout) << APP_VERSION << Qt::endl;
|
||||||
}
|
}
|
||||||
else if (args.contains("-l"))
|
else if (args.contains("-l"))
|
||||||
{
|
{
|
||||||
servStatByDir("/etc/mow");
|
servStatByDir(etcDir);
|
||||||
}
|
}
|
||||||
else if (args.contains("-i") || args.contains("-d"))
|
else if (args.contains("-i") || args.contains("-d"))
|
||||||
{
|
{
|
||||||
ret = rmServiceByDir("/etc/mow");
|
ret = rmServiceByDir(etcDir);
|
||||||
|
|
||||||
if ((ret == 0) && args.contains("-i"))
|
if ((ret == 0) && args.contains("-i"))
|
||||||
{
|
{
|
||||||
ret = loadServiceByDir("/etc/mow", true);
|
ret = loadServiceByDir(etcDir, true);
|
||||||
}
|
}
|
||||||
else if (ret == 0)
|
else if (ret == 0)
|
||||||
{
|
{
|
||||||
ret = loadServiceByDir("/etc/mow", false);
|
ret = loadServiceByDir(etcDir, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (args.contains("-c"))
|
else if (args.contains("-c"))
|
||||||
|
@ -82,11 +83,11 @@ int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (args.contains("-f"))
|
if (args.contains("-f"))
|
||||||
{
|
{
|
||||||
ret = rmServiceByDir("/etc/mow");
|
ret = rmServiceByDir(etcDir);
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
ret = QProcess::execute("/opt/mow/uninst", QStringList());
|
ret = QProcess::execute("/opt/" + QString(APP_TARGET) + "/uninstall.sh", QStringList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -102,18 +103,18 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
if (ret == 0)
|
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"))
|
else if (args.contains("-r"))
|
||||||
{
|
{
|
||||||
ret = rmServiceByDir("/etc/mow");
|
ret = rmServiceByDir(etcDir);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
showHelp();
|
showHelp(etcDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (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
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
int loadService(const QString &desc, const QString &user, const QString &servName, const QString &workDir, const QString &recPath, bool start)
|
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");
|
QFile file("/lib/systemd/system/" + servName + ".service");
|
||||||
|
|
||||||
auto ret = 0;
|
auto ret = 0;
|
||||||
|
@ -43,7 +45,12 @@ int loadService(const QString &desc, const QString &user, const QString &servNam
|
||||||
file.write("Type=simple\n");
|
file.write("Type=simple\n");
|
||||||
file.write("User=" + user.toUtf8() + "\n");
|
file.write("User=" + user.toUtf8() + "\n");
|
||||||
file.write("Restart=always\n");
|
file.write("Restart=always\n");
|
||||||
file.write("TimeoutStopSec=infinity\n");
|
|
||||||
|
if (servName.contains("vid_loop"))
|
||||||
|
{
|
||||||
|
file.write("RuntimeMaxSec=61\n");
|
||||||
|
}
|
||||||
|
|
||||||
file.write("ExecStart=/usr/bin/env " + servName.toUtf8() + "\n\n");
|
file.write("ExecStart=/usr/bin/env " + servName.toUtf8() + "\n\n");
|
||||||
file.write("[Install]\n");
|
file.write("[Install]\n");
|
||||||
file.write("WantedBy=multi-user.target\n");
|
file.write("WantedBy=multi-user.target\n");
|
||||||
|
@ -90,7 +97,6 @@ int loadSh(const QString &name, const QString &exeCmd, const QString &buffDir, c
|
||||||
|
|
||||||
QProcess::execute("chmod", {"-v", "+x", file.fileName()});
|
QProcess::execute("chmod", {"-v", "+x", file.fileName()});
|
||||||
QProcess::execute("chown", {servUser + ":" + servGroup, "-R", buffDir});
|
QProcess::execute("chown", {servUser + ":" + servGroup, "-R", buffDir});
|
||||||
QProcess::execute("chown", {servUser + ":" + servGroup, "-R", outDir});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
@ -104,7 +110,7 @@ QString camCmdFromConf(shared_t *conf, CmdExeType type)
|
||||||
|
|
||||||
if (type == MAIN_LOOP)
|
if (type == MAIN_LOOP)
|
||||||
{
|
{
|
||||||
ret += QString(APP_BIN) + " -c " + conf->conf;
|
ret += QString(APP_TARGET) + " -c " + conf->conf;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -112,15 +118,16 @@ QString camCmdFromConf(shared_t *conf, CmdExeType type)
|
||||||
|
|
||||||
if (conf->recordUri.contains("rtsp"))
|
if (conf->recordUri.contains("rtsp"))
|
||||||
{
|
{
|
||||||
ret += "-rtsp_transport tcp ";
|
ret += "-rtsp_transport udp ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conf->streamCodec != "copy")
|
if (conf->vidCodec != "copy")
|
||||||
{
|
{
|
||||||
ret += "-vf fps=" + QString::number(conf->recFps) + ",scale=" + conf->recScale + " ";
|
ret += "-vf fps=" + QString::number(conf->recFps) + ",scale=" + conf->recScale + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
ret += "-vcodec " + conf->streamCodec + " ";
|
ret += "-vcodec " + conf->vidCodec + " ";
|
||||||
|
ret += "-acodec " + conf->audCodec + " ";
|
||||||
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 += "-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;
|
ret += conf->buffPath + "/live/" + QString(STRFTIME_FMT) + conf->streamExt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
#ifndef SERVICES_H
|
#ifndef SERVICES_H
|
||||||
#define 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (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
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// 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