commit a5afc96316600f74a3de754e32065f7cfd2bfd98
Author: David Sorber <david.sorber@gmail.com>
Date:   Tue Jan 8 20:02:16 2019 -0500

    Add remove command and helper scripts.

diff --git a/software/hbkcd/hbkcd/command_server.py b/software/hbkcd/hbkcd/command_server.py
index 04e12d3..7d7b5a7 100755
--- a/software/hbkcd/hbkcd/command_server.py
+++ b/software/hbkcd/hbkcd/command_server.py
@@ -91,11 +91,15 @@ class CommandHandler(BaseHTTPRequestHandler):
                 response_dict['message'] = 'Command format invalid'
                 
                 if 'type' in content:
-                    # Delegate potentiall valid commands for additional handling
+                    # Delegate potentially valid commands for additional handling
                     if content['type'] == 'add':
                         response = self.handle_add_cmd(content)
                         response_dict['success'] = response[0]
                         response_dict['message'] = response[1]
+                    elif content['type'] == 'remove':
+                        response = self.handle_remove_cmd(content)
+                        response_dict['success'] = response[0]
+                        response_dict['message'] = response[1]
                     
             except Exception as exp:
                 response_dict['success'] = False
@@ -114,7 +118,7 @@ class CommandHandler(BaseHTTPRequestHandler):
     def handle_add_cmd(self, command_data):
         """ Handler for the "add" command
         """
-        if 'path' not in command_data:
+        if 'path' not in command_data or 'output' not in command_data:
             return False, 'Command format invalid'
         
         # Check if the path actually exists:
@@ -131,10 +135,30 @@ class CommandHandler(BaseHTTPRequestHandler):
             msg = msg.format(path, mimetype)
             logging.error(msg)
             return False, msg
+            
+        # Check output default assume that the path is a direct file name
+        output_path = command_data['output']
+        if output_path[-4:].lower() not in ['.mkv', '.mp4']:            
+            # Assume output directory, name output based on input filename
+            if not os.path.isdir(output_path):
+                try:
+                    os.makedirs(output_path, exist_ok=True)
+                except PermissionError:
+                    msg = 'Unable to create output directory: {:s}'
+                    msg = msg.format(output_path)
+                    logging.error(msg)
+                    return False, msg
+        
+            # Always default to Matroska output
+            filename = os.path.basename(path)
+            output_filename = '{:s}.mkv'.format(os.path.splitext(filename)[0])
+            
+            # Build final output path
+            output_path = os.path.join(output_path, output_filename)
 
         # Create a QueueItem instance and then probe the file to retrieve its
         # attributes
-        item = QueueItem(path)
+        item = QueueItem(path, output_path)
         if not item.probe():
             msg = 'An error ocurred while probing the video file'
             logging.error(msg)
@@ -145,6 +169,20 @@ class CommandHandler(BaseHTTPRequestHandler):
         
         return True, 'Added to queue'
         
+    def handle_remove_cmd(self, command_data):
+        """ Handler for the "remove" command
+        """
+        if 'position' not in command_data:
+            return False, 'Command format invalid'
+        
+        position = int(command_data['position'])
+        
+        success = ENCODE_QUEUE.remove(position - 1)
+        msg = 'Removed element {:d}'.format(position)
+        if not success:
+            msg = 'Failed to remove element {:d}'.format(position)
+            
+        return success, msg
  
 def main():
     print('starting server...')
diff --git a/software/hbkcd/hbkcd/config.py b/software/hbkcd/hbkcd/config.py
index 921e2b4..196cf05 100644
--- a/software/hbkcd/hbkcd/config.py
+++ b/software/hbkcd/hbkcd/config.py
@@ -15,6 +15,11 @@ HANDBRAKE_CMD = [HANDBRAKE_PATH, '-i', '', '-o', '/home/dsorber/clip-OUT.mkv',
                  '-f', 'av_mkv', '-e', 'x265', '--encoder-preset', 'veryslow', 
                  '--encoder-level', 'auto', '-q', '24', '-2', '-T', '-r', 
                  '23.98', '-E', 'copy:aac']
+                 
+HANDBRAKE_CMD_INPUT_IDX  =  2
+HANDBRAKE_CMD_OUTPUT_IDX =  4
+HANDBRAKE_CMD_FPS_IDX    = 18
+HANDBRAKE_CMD_AUDIO_IDX  = 20
 
 # Absolute path to the ffprobe executable
 FFPROBE_PATH = '/home/dsorber/ffprobe'
diff --git a/software/hbkcd/hbkcd/daemon.py b/software/hbkcd/hbkcd/daemon.py
index e6bd852..76349b1 100755
--- a/software/hbkcd/hbkcd/daemon.py
+++ b/software/hbkcd/hbkcd/daemon.py
@@ -82,9 +82,7 @@ def main():
             item.encode_started = datetime.datetime.now()
             
             # Start Handbrake process
-            cmd = HANDBRAKE_CMD
-            cmd[2] = item.path
-            
+            cmd = item.build_hbk_cmd()
             hbk_proc = subprocess.Popen(cmd, 
                                         stderr=subprocess.PIPE, 
                                         stdout=subprocess.PIPE, 
@@ -98,12 +96,13 @@ def main():
             status_thread.start()
             
             # Let the HandBrake process run and print a message to the log
-            # every minute
+            # every 5 minutes
+            logging.info('Start encode of {:s}'.format(item.path))
             ctr = 0
             while hbk_proc.poll() is None:
                 ctr += 1
-                if ctr % 60 == 0:
-                    logging.info('Encode in progress... {:s}'.format(ctr, item.status))
+                if ctr % 300 == 0:
+                    logging.info('Encode in progress... {:s}'.format(item.status))
                     ctr = 0
                 time.sleep(1)
             
@@ -111,8 +110,18 @@ def main():
             status_thread.join()
             item.in_progress = False
             
+            # Calculate elapsed time
+            elapsed = item.encode_finished - item.encode_started
+            hours, remainder = divmod(elapsed.total_seconds(), 3600)
+            minutes, seconds = divmod(remainder, 60)
+            elapsed_str = '{:02}:{:02}:{:02}'
+            elapsed_str = elapsed_str.format(int(hours), int(minutes), int(seconds))            
+            
+            # Print success or failure message to the log depending on the
+            # HandBrake process's return code
             if hbk_proc.returncode == 0:
-                logging.info('Encode of {:s} completed!'.format(item.path))
+                logging.info('Encode of {:s} completed in {:s}'.format(item.path, 
+                                                                       elapsed_str))
             else:
                 logging.error('Error while encoding {:s}'.format(item.path))
             
diff --git a/software/hbkcd/hbkcd/queue.py b/software/hbkcd/hbkcd/queue.py
index ba72022..619601c 100644
--- a/software/hbkcd/hbkcd/queue.py
+++ b/software/hbkcd/hbkcd/queue.py
@@ -44,7 +44,7 @@ class EncodeQueue(object):
         
         # Re-set positions for all items
         ctr = 1
-        for item in self.queue[]
+        for item in self.queue:
             item.position = ctr
             ctr += 1
         
@@ -64,9 +64,10 @@ class EncodeQueue(object):
 
 class QueueItem(object):
 
-    def __init__(self, path):
+    def __init__(self, path, output_file):
         self.path = path
         self.filename = os.path.basename(path)
+        self.output_file = output_file
         self.position = None
         self.in_progress = False
         self.status = 'queued'
@@ -131,6 +132,7 @@ class QueueItem(object):
     def get_dict_rep(self):
         return {'position':         self.position,
                 'path':             self.path,
+                'output_file':      self.output_file,
                 'in_progress':      self.in_progress,
                 'status':           self.status,
                 'width':            self.width,
@@ -142,15 +144,29 @@ class QueueItem(object):
                 'encode_finished':  format_ds(self.encode_finished)}
 
     def update_status(self, stdout_iter):
-        logging.info('Update status thread running for: {:s}'.format(self.path))
+        logging.debug('Update status thread running for: {:s}'.format(self.path))
         for stdout_line in stdout_iter:
             # Naturally this is very specific parsing for the HBK output
             try:
                 self.status = stdout_line.strip().split(',', 1)[1].strip()
             except IndexError:
                 self.status = stdout_line
-        logging.info('Update status thread terminating')
-
-
+        logging.debug('Update status thread terminating')
+        
+    def build_hbk_cmd(self):
+        """ Build and return templated HandBrake command.
+        """ 
+        cmd = HANDBRAKE_CMD
+        cmd[HANDBRAKE_CMD_INPUT_IDX]  = self.path
+        cmd[HANDBRAKE_CMD_OUTPUT_IDX] = self.output_file
+        cmd[HANDBRAKE_CMD_FPS_IDX]    = self.framerate
+        
+        if self.convert_audio:
+            # If converting audio use AAC
+            # TODO: double check this audio settings make sure VBR is used
+            cmd[HANDBRAKE_CMD_AUDIO_IDX] = 'fdk_aac --aq 5'
+            
+        return cmd
+        
 # Does this need to be protected?
 ENCODE_QUEUE = EncodeQueue()
diff --git a/software/hbkcd/scripts/hbkcd_add.sh b/software/hbkcd/scripts/hbkcd_add.sh
new file mode 100755
index 0000000..d109490
--- /dev/null
+++ b/software/hbkcd/scripts/hbkcd_add.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+ADDRESS="brutus"
+PORT="8081"
+
+if [ "$#" -ne 2 ]; then
+    echo "USAGE: hbkcd_add <path to input file> <path to output file/directory>"
+    exit -1
+fi
+
+# Make sure curl ad jq are installed
+command -v curl >/dev/null 2>&1 || { echo >&2 "Please install curl to use this script. Aborting."; exit 1; }
+command -v jq >/dev/null 2>&1 || { echo >&2 "Please install jq to use this script. Aborting."; exit 1; }
+
+INPUT_PATH=$(readlink -f $1)
+curl -s -X POST -d '{"type": "add", "path": "'$INPUT_PATH'", "output": "'$2'"}' http://$ADDRESS:$PORT/command | jq
diff --git a/software/hbkcd/scripts/hbkcd_queue.sh b/software/hbkcd/scripts/hbkcd_queue.sh
new file mode 100755
index 0000000..91beb3b
--- /dev/null
+++ b/software/hbkcd/scripts/hbkcd_queue.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+ADDRESS="brutus"
+PORT="8081"
+
+# Make sure curl ad jq are installed
+command -v curl >/dev/null 2>&1 || { echo >&2 "Please install curl to use this script. Aborting."; exit 1; }
+command -v jq >/dev/null 2>&1 || { echo >&2 "Please install jq to use this script. Aborting."; exit 1; }
+
+curl -s -X GET http://$ADDRESS:$PORT/queue | jq
diff --git a/software/hbkcd/scripts/hbkcd_remove.sh b/software/hbkcd/scripts/hbkcd_remove.sh
new file mode 100755
index 0000000..e145bc2
--- /dev/null
+++ b/software/hbkcd/scripts/hbkcd_remove.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+ADDRESS="brutus"
+PORT="8081"
+
+if [ "$#" -ne 1 ]; then
+    echo "USAGE: hbkcd_remove <queue position>"
+    exit -1
+fi
+
+# Make sure curl ad jq are installed
+command -v curl >/dev/null 2>&1 || { echo >&2 "Please install curl to use this script. Aborting."; exit 1; }
+command -v jq >/dev/null 2>&1 || { echo >&2 "Please install jq to use this script. Aborting."; exit 1; }
+
+curl -s -X POST -d '{"type": "remove", "position": "'$1'"}' http://$ADDRESS:$PORT/command | jq
diff --git a/software/hbkcd/setup.py b/software/hbkcd/setup.py
index ac53e7c..854f708 100755
--- a/software/hbkcd/setup.py
+++ b/software/hbkcd/setup.py
@@ -3,7 +3,7 @@ from setuptools import setup, find_packages
 
 setup(
     name = 'hbkcd',
-    version = '0.0.3',
+    version = '0.1.0',
     description = 'HandBrake Control Daemon',
     packages = find_packages(),
     python_requires = '>=3.6.0',
