#!/usr/bin/env python3 """ Module Docstring """ __author__ = "Nikola Kotur" __version__ = "0.1" __license__ = "Apache License 2.0" 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): 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): self.context = pyudev.Context() self.monitor = pyudev.Monitor.from_netlink(self.context) self.monitor.filter_by(subsystem='block') 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': 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']) output = execute('ncftpput -u %s -p %s %s %s %s' % (ftp['user'], ftp['password'], ftp['host'], ftp['path'], up_file)) if output != None: logger.info('Removing %s', up_file) os.remove(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__": parser = argparse.ArgumentParser() # 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)