Ver código fonte

Working version

Nikola Kotur 3 anos atrás
pai
commit
7b08b77a15
2 arquivos alterados com 162 adições e 73 exclusões
  1. 0 73
      README-boilerplate.md
  2. 162 0
      run.py

+ 0 - 73
README-boilerplate.md

@@ -1,73 +0,0 @@
-# python3-boilerplate ![CI](https://github.com/BastiTee/python3-boilerplate/workflows/CI/badge.svg)
-
-> A best-practices template project for Python3 modules
-
---------------------------------------------------------------------------------
-
-**Disclaimer**: If you see this on [pypi.org](https://pypi.org/project/python3-boilerplate/) please note that the project is only published here for testing purposes. Please visit [GitHub](https://github.com/BastiTee/python3-boilerplate) for the related template project.
-
---------------------------------------------------------------------------------
-
-## Setup
-
-- Make sure that `python3` and `pip3` is installed and available on the path (on Ubuntu: `sudo apt-get install python3 python3-pip`)
-- On first checkout run `make venv` to initialize the project configuration
-- Refer to [the Makefile](Makefile) to learn about the various operations available
-- To rename the project you can use [the provided script](__rename__.sh)
-
-## Features
-
-- Basic project/module organization according to <https://packaging.python.org>
-- `Makefile` management script
-- [pipenv](https://github.com/pypa/pipenv) and virtual environments
-- [distutils](https://docs.python.org/3/library/distutils.html)-based installer script
-- Unit testing with [pytest](https://docs.pytest.org/en/latest/)
-- Multicore/-interpreter testing with [tox](https://tox.readthedocs.io/en/latest/)
-- Linting ([flake8](http://flake8.pycqa.org)) and code formatting ([autopep8](https://github.com/hhatto/autopep8)) support
-- [isort](https://pypi.org/project/isort/) support for automated import sorting
-- [mpyp](https://pypi.org/project/mypy/) support for type checking
-- Publishing to PyPi.org at <https://pypi.org/project/python3-boilerplate/>
-- [vscode](https://code.visualstudio.com/) editor configuration including plugin recommendations, debugging support, unit test discovery and on-save formatting
-- [Github actions](https://github.com/BastiTee/python3-boilerplate/actions) continuous integration
-
-## How to use in existing project
-
-To use it in an existing project you can merge the remote changes to your project.
-
-- Add remote to access the template project
-
-```shell
-git remote add py3template git@github.com:BastiTee/python3-boilerplate.git
-git pull py3template master --allow-unrelated-histories
-```
-
-- Solve all merge conflicts and commit. Most likely there will be a lot
-- Do whatever is necessary to remove the boilerplate you don't need, e.g.
-
-```shell
-rm -rf my_module tests/test_utils.py __rename__.sh
-```
-
-- Commit your changes and push to your project
-
-## Resources
-
-- <http://packaging.python.org/>
-- <https://packaging.python.org/en/latest/distributing.html>
-- <https://pypi.org/>
-- <https://github.com/pypa/sampleproject>
-- <https://www.python.org/dev/peps/>
-- <https://www.python.org/dev/peps/pep-0008/>
-- <https://www.kennethreitz.org/essays/why-you-should-use-vs-code-if-youre-a-python-developer>
-- <https://code.visualstudio.com/docs/python/python-tutorial>
-
-## Future ideas and todos
-
-- Optional static type hints ([PEP 484](https://www.python.org/dev/peps/pep-0484/)) with [mypy](https://github.com/python/mypy)
-- Make sure [tox](https://tox.readthedocs.io/) is working as expected for supported Python versions
-- Introduce [black](https://github.com/psf/black) in favour of other linters
-- Introduce [pyproject.toml](https://www.python.org/dev/peps/pep-0518/) along with bleeding edge build tools like [flit](https://flit.readthedocs.io/en/latest/rationale.html) or [poetry](https://python-poetry.org/)
-
-## Licensing
-
-This project is licensed under [Apache License 2.0](LICENSE.txt).

+ 162 - 0
run.py

@@ -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)