|
@@ -0,0 +1,162 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+"""
|
|
|
+Module Docstring
|
|
|
+"""
|
|
|
+
|
|
|
+__author__ = "Nikola Kotur"
|
|
|
+__version__ = "0.1"
|
|
|
+__license__ = "MIT"
|
|
|
+
|
|
|
+import os
|
|
|
+import sys
|
|
|
+import argparse
|
|
|
+import threading
|
|
|
+import tempfile
|
|
|
+import subprocess
|
|
|
+from pprint import pprint as pp
|
|
|
+
|
|
|
+import pyudev
|
|
|
+from logzero import logger
|
|
|
+
|
|
|
+
|
|
|
+class USBDetector():
|
|
|
+ ''' Monitor udev for detection of usb '''
|
|
|
+
|
|
|
+ def __init__(self, handler, source_dir, dest_dir, ftp_args):
|
|
|
+ ''' Initiate the object '''
|
|
|
+ self.usb_plugged_in = handler
|
|
|
+ self.source_dir = source_dir
|
|
|
+ self.dest_dir = dest_dir
|
|
|
+ self.ftp_args = ftp_args
|
|
|
+ thread = threading.Thread(target=self._work)
|
|
|
+ thread.daemon = True
|
|
|
+ thread.start()
|
|
|
+ thread.join()
|
|
|
+
|
|
|
+ def _work(self):
|
|
|
+ ''' Runs the actual loop to detect the events '''
|
|
|
+ self.context = pyudev.Context()
|
|
|
+ self.monitor = pyudev.Monitor.from_netlink(self.context)
|
|
|
+ self.monitor.filter_by(subsystem='block')
|
|
|
+ # this is module level logger, can be ignored
|
|
|
+ logger.info("Starting to monitor for usb")
|
|
|
+ self.monitor.start()
|
|
|
+ for device in iter(self.monitor.poll, None):
|
|
|
+ logger.debug("Got USB event: %s", device.action)
|
|
|
+ if device.action == 'add' and device.device_type == 'partition':
|
|
|
+ # some function to run on insertion of usb
|
|
|
+ self.usb_plugged_in(device, self.source_dir, self.dest_dir, self.ftp_args)
|
|
|
+
|
|
|
+
|
|
|
+def execute(shell_command):
|
|
|
+ shell_command = shell_command.split(' ')
|
|
|
+ try:
|
|
|
+ res = subprocess.check_output(shell_command, stderr=subprocess.PIPE)
|
|
|
+ return res.decode('utf-8')
|
|
|
+ except subprocess.CalledProcessError as e:
|
|
|
+ print('exit code: {}'.format(e.returncode))
|
|
|
+ print('stdout: {}'.format(e.output.decode(sys.getfilesystemencoding())))
|
|
|
+ print('stderr: {}'.format(e.stderr.decode(sys.getfilesystemencoding())))
|
|
|
+ return None
|
|
|
+
|
|
|
+
|
|
|
+def parse_video_files(usb_dir, base_dir, extension):
|
|
|
+ files = {}
|
|
|
+ for file in os.listdir(os.path.join(usb_dir, base_dir)):
|
|
|
+ if file.endswith(extension):
|
|
|
+ full_path = (os.path.join(usb_dir, base_dir, file))
|
|
|
+ file_parts = file.split('_')
|
|
|
+ part_date = file_parts[0]
|
|
|
+ if part_date not in files:
|
|
|
+ files[part_date] = []
|
|
|
+ files[part_date].append(full_path)
|
|
|
+ return files
|
|
|
+
|
|
|
+
|
|
|
+def join_video_files(dest, sources):
|
|
|
+ with tempfile.TemporaryDirectory() as tmpdirname:
|
|
|
+ list_file = os.path.join(tmpdirname, 'list')
|
|
|
+ with open(list_file, 'w') as lfh:
|
|
|
+ for source_line in sources:
|
|
|
+ lfh.write("file '%s'\n" % source_line)
|
|
|
+
|
|
|
+ ffmpeg_line = 'ffmpeg -safe 0 -f concat -i %s -c copy %s' % (list_file, dest)
|
|
|
+ output = execute(ffmpeg_line)
|
|
|
+ if output != None:
|
|
|
+ logger.info('Removing source files')
|
|
|
+ for s_file in sources:
|
|
|
+ os.remove(s_file)
|
|
|
+ return True
|
|
|
+ return False
|
|
|
+
|
|
|
+
|
|
|
+def plugged_in(dev, source_dir, dest_dir, ftp):
|
|
|
+ logger.info('Detected %s (%s)', dev.device_node, dev.device_type)
|
|
|
+ with tempfile.TemporaryDirectory() as tmpdirname:
|
|
|
+ logger.info('Mounting %s on %s', dev.device_node, tmpdirname)
|
|
|
+ output = execute('mount %s %s' % (dev.device_node, tmpdirname))
|
|
|
+
|
|
|
+ # Join files
|
|
|
+ joined_files = []
|
|
|
+ for file_group, files in parse_video_files(tmpdirname, source_dir, 'MP4').items():
|
|
|
+ logger.info('Joining video group %s', file_group)
|
|
|
+ outfile_name = '%s.mp4' % file_group
|
|
|
+ outfile_path = os.path.join(dest_dir, outfile_name)
|
|
|
+ if join_video_files(outfile_path, files):
|
|
|
+ joined_files.append(outfile_path)
|
|
|
+
|
|
|
+ logger.info('Umounting %s from %s', dev.device_node, tmpdirname)
|
|
|
+ output = execute('umount %s' % (dev.device_node))
|
|
|
+
|
|
|
+ # Beam files to FTP
|
|
|
+ for up_file in joined_files:
|
|
|
+ logger.info('Uploading %s to %s', up_file, ftp['host'])
|
|
|
+ execute('ncftpput -u %s -p %s %s %s %s' % (ftp['user'], ftp['password'], ftp['host'], ftp['path'], up_file))
|
|
|
+
|
|
|
+ logger.info('Done')
|
|
|
+
|
|
|
+
|
|
|
+def main(args):
|
|
|
+ """ Main entry point of the app """
|
|
|
+ ftp_args = {
|
|
|
+ 'host': args.ftp_host,
|
|
|
+ 'path': args.ftp_path,
|
|
|
+ 'user': args.ftp_user,
|
|
|
+ 'password': args.ftp_password,
|
|
|
+ }
|
|
|
+ usb_detect = USBDetector(plugged_in, args.source, args.destination, ftp_args)
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ """ This is executed when run from the command line """
|
|
|
+ parser = argparse.ArgumentParser()
|
|
|
+
|
|
|
+ # Optional argument flag which defaults to False
|
|
|
+ parser.add_argument("-f", "--flag", action="store_true", default=False)
|
|
|
+
|
|
|
+ # Directory options
|
|
|
+ parser.add_argument("-s", "--source", help="source directory with dashcam videos", action="store", dest="source", default="DCIM/Movie")
|
|
|
+ parser.add_argument("-d", "--destination", help="destination directory for joined dashcam videos", action="store", dest="destination", default="/var/dashcam")
|
|
|
+
|
|
|
+ # FTP options
|
|
|
+ parser.add_argument("--ftp-host", help="ftp host (server)", action="store", default="192.168.88.242")
|
|
|
+ parser.add_argument("--ftp-path", help="ftp path", action="store", default="/dashcam")
|
|
|
+ parser.add_argument("--ftp-user", help="ftp user", action="store", default="dashcam")
|
|
|
+ parser.add_argument("--ftp-password", help="ftp user", action="store", default="")
|
|
|
+
|
|
|
+ # Optional verbosity counter (eg. -v, -vv, -vvv, etc.)
|
|
|
+ parser.add_argument(
|
|
|
+ "-v",
|
|
|
+ "--verbose",
|
|
|
+ action="count",
|
|
|
+ default=0,
|
|
|
+ help="Verbosity (-v, -vv, etc)")
|
|
|
+
|
|
|
+ # Specify output of "--version"
|
|
|
+ parser.add_argument(
|
|
|
+ "--version",
|
|
|
+ action="version",
|
|
|
+ version="%(prog)s (version {version})".format(version=__version__))
|
|
|
+
|
|
|
+ args = parser.parse_args()
|
|
|
+ main(args)
|