sync.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #!/usr/bin/env python3
  2. """
  3. Module Docstring
  4. """
  5. __author__ = "Nikola Kotur"
  6. __version__ = "0.1"
  7. __license__ = "Apache License 2.0"
  8. import os
  9. import sys
  10. import argparse
  11. import threading
  12. import tempfile
  13. import subprocess
  14. from pprint import pprint as pp
  15. import pyudev
  16. from logzero import logger
  17. class USBDetector():
  18. ''' Monitor udev for detection of usb '''
  19. def __init__(self, handler, source_dir, dest_dir, ftp_args):
  20. self.usb_plugged_in = handler
  21. self.source_dir = source_dir
  22. self.dest_dir = dest_dir
  23. self.ftp_args = ftp_args
  24. thread = threading.Thread(target=self._work)
  25. thread.daemon = True
  26. thread.start()
  27. thread.join()
  28. def _work(self):
  29. self.context = pyudev.Context()
  30. self.monitor = pyudev.Monitor.from_netlink(self.context)
  31. self.monitor.filter_by(subsystem='block')
  32. logger.info("Starting to monitor for usb")
  33. self.monitor.start()
  34. for device in iter(self.monitor.poll, None):
  35. logger.debug("Got USB event: %s", device.action)
  36. if device.action == 'add' and device.device_type == 'partition':
  37. self.usb_plugged_in(device, self.source_dir, self.dest_dir, self.ftp_args)
  38. def execute(shell_command):
  39. shell_command = shell_command.split(' ')
  40. try:
  41. res = subprocess.check_output(shell_command, stderr=subprocess.PIPE)
  42. return res.decode('utf-8')
  43. except subprocess.CalledProcessError as e:
  44. print('exit code: {}'.format(e.returncode))
  45. print('stdout: {}'.format(e.output.decode(sys.getfilesystemencoding())))
  46. print('stderr: {}'.format(e.stderr.decode(sys.getfilesystemencoding())))
  47. return None
  48. def parse_video_files(usb_dir, base_dir, extension):
  49. files = {}
  50. for file in os.listdir(os.path.join(usb_dir, base_dir)):
  51. if file.endswith(extension):
  52. full_path = (os.path.join(usb_dir, base_dir, file))
  53. file_parts = file.split('_')
  54. part_date = file_parts[0]
  55. if part_date not in files:
  56. files[part_date] = []
  57. files[part_date].append(full_path)
  58. return files
  59. def join_video_files(dest, sources):
  60. with tempfile.TemporaryDirectory() as tmpdirname:
  61. list_file = os.path.join(tmpdirname, 'list')
  62. with open(list_file, 'w') as lfh:
  63. for source_line in sources:
  64. lfh.write("file '%s'\n" % source_line)
  65. ffmpeg_line = 'ffmpeg -safe 0 -f concat -i %s -c copy %s' % (list_file, dest)
  66. output = execute(ffmpeg_line)
  67. if output != None:
  68. logger.info('Removing source files')
  69. for s_file in sources:
  70. os.remove(s_file)
  71. return True
  72. return False
  73. def plugged_in(dev, source_dir, dest_dir, ftp):
  74. logger.info('Detected %s (%s)', dev.device_node, dev.device_type)
  75. with tempfile.TemporaryDirectory() as tmpdirname:
  76. logger.info('Mounting %s on %s', dev.device_node, tmpdirname)
  77. output = execute('mount %s %s' % (dev.device_node, tmpdirname))
  78. # Join files
  79. joined_files = []
  80. for file_group, files in parse_video_files(tmpdirname, source_dir, 'MP4').items():
  81. logger.info('Joining video group %s', file_group)
  82. outfile_name = '%s.mp4' % file_group
  83. outfile_path = os.path.join(dest_dir, outfile_name)
  84. if join_video_files(outfile_path, files):
  85. joined_files.append(outfile_path)
  86. logger.info('Umounting %s from %s', dev.device_node, tmpdirname)
  87. output = execute('umount %s' % (dev.device_node))
  88. # Beam files to FTP
  89. for up_file in joined_files:
  90. logger.info('Uploading %s to %s', up_file, ftp['host'])
  91. output = execute('ncftpput -u %s -p %s %s %s %s' % (ftp['user'], ftp['password'], ftp['host'], ftp['path'], up_file))
  92. if output != None:
  93. logger.info('Removing %s', up_file)
  94. os.remove(up_file)
  95. logger.info('Done')
  96. def main(args):
  97. """ Main entry point of the app """
  98. ftp_args = {
  99. 'host': args.ftp_host,
  100. 'path': args.ftp_path,
  101. 'user': args.ftp_user,
  102. 'password': args.ftp_password,
  103. }
  104. usb_detect = USBDetector(plugged_in, args.source, args.destination, ftp_args)
  105. if __name__ == "__main__":
  106. parser = argparse.ArgumentParser()
  107. # Directory options
  108. parser.add_argument("-s", "--source", help="source directory with dashcam videos", action="store", dest="source", default="DCIM/Movie")
  109. parser.add_argument("-d", "--destination", help="destination directory for joined dashcam videos", action="store", dest="destination", default="/var/dashcam")
  110. # FTP options
  111. parser.add_argument("--ftp-host", help="ftp host (server)", action="store", default="192.168.88.242")
  112. parser.add_argument("--ftp-path", help="ftp path", action="store", default="/dashcam")
  113. parser.add_argument("--ftp-user", help="ftp user", action="store", default="dashcam")
  114. parser.add_argument("--ftp-password", help="ftp user", action="store", default="")
  115. # Optional verbosity counter (eg. -v, -vv, -vvv, etc.)
  116. parser.add_argument(
  117. "-v",
  118. "--verbose",
  119. action="count",
  120. default=0,
  121. help="Verbosity (-v, -vv, etc)")
  122. # Specify output of "--version"
  123. parser.add_argument(
  124. "--version",
  125. action="version",
  126. version="%(prog)s (version {version})".format(version=__version__))
  127. args = parser.parse_args()
  128. main(args)