Project

General

Profile

« Previous | Next » 

Revision d6a44a2b

Added by David Sorber over 7 years ago

Adding very basic command server and module packaging.

View differences:

software/hbkcd/hbkcd.py
#!/usr/bin/python3
import logging
import os
import sys
#
# HbkCD - Handbrake Control Daemon
#
###############################################################################
# Configuration Options
###############################################################################
LOGFILE_PATH = '/var/log/hbkcd/hbkcd.log'
LOG_FORMAT_STR = '%(asctime)s|%(levelname)s|%(filename)s:%(lineno)s|%(message)s'
LOG_DATE_FORMAT_STR = '%a %b %d %H:%M:%S %Y'
DEFAULT_LOG_LEVEL = logging.DEBUG
# Absolute path the to the HandbrakeCLI executable
HANDBRAKE_PATH = '/opt/local/bin/HandBrakeCLI'
# Path to queue file. Note that there is nothing special about this file, it
# is simply a text file containing a list of paths to video files to re-enocde
QUEUE_FILE_PATH = '/home/dsorber/encode_queue'
def check_configuration():
""" Check runtime parameters for correctness before attempting to use them.
"""
# Make sure specified HandbrakeCLI path exists and is a file
if not os.path.isfile(HANDBRAKE_PATH):
logging.error('Handbrake does not exist at path: {:s}'
.format(HANDBRAKE_PATH))
return False
# Make sure the specified HandbrakeCLI file is executable
if not os.access(HANDBRAKE_PATH, os.X_OK):
logging.error('Handbrake exists but is not executable ({:s})'
.format(HANDBRAKE_PATH))
return False
# Make sure specified encode queue file exists and is a file
if not os.path.isfile(QUEUE_FILE_PATH):
logging.error('The specified encode queue file ({:s}) does not exist, '
'please create it to proceed'.format(QUEUE_FILE_PATH))
return False
logging.debug('Runtime configuration checks OKAY')
return True
def main():
# Configure logging before attempting anything serious
logging.basicConfig(format=LOG_FORMAT_STR,
datefmt=LOG_DATE_FORMAT_STR,
# ~ filename=LOGFILE_PATH,
level=DEFAULT_LOG_LEVEL)
# ~ print('foobar', file=sys.stderr)
logging.info('hbkcf started')
# Validate runtime configuration before proceeding
if not check_configuration():
return -1
return 0
if __name__ == '__main__':
sys.exit(main())
software/hbkcd/hbkcd/command_server.py
#!/usr/bin/python3
from http.server import BaseHTTPRequestHandler, HTTPServer
import logging
import json
import os
import sys
import time
import threading
from hbkcd.queue import ENCODE_QUEUE
# https://blog.gocept.com/2011/08/04/shutting-down-an-httpserver/
# https://docs.aws.amazon.com/polly/latest/dg/example-Python-server-code.html
class StoppableHTTPServer(HTTPServer):
def run(self):
try:
self.serve_forever()
except KeyboardInterrupt:
pass
finally:
# Clean-up server (close socket, etc.)
self.server_close()
class CommandHandler(BaseHTTPRequestHandler):
def log_message(format, *args):
logging.debug('Request: {} -- {}'.format(args[1], args[2]))
# ~ print('REQUEST: {} -- {}'.format(args[1], args[2]))
def do_GET(self):
""" Handle HTTP GET requests
"""
# Strip off leading and/or trailing slashes
path = self.path.strip('/')
# Defaults
response_code = 404
response_type = 'text/html'
response_data = ''
# Send response status code
if path == 'queue':
response_code = 200
response_type = 'application/json'
response_dict = {'queue': ENCODE_QUEUE}
response_data = json.dumps(response_dict).encode('utf-8')
elif not path:
response_code = 200
response = 'HandBrake Control Daemon - control interface'
response_data = response.encode('utf-8')
# Send response and data, if any
self.send_response(response_code)
if response_data:
self.send_header('Content-type', response_type)
self.end_headers()
if response_data:
self.wfile.write(response_data)
return
def do_POST(self):
""" Handle HTTP POST requests
"""
# Strip off leading and/or trailing slashes
path = self.path.strip('/')
# Defaults
response_code = 200
response_type = 'application/json'
response_dict = {'success': False, 'message': 'Invalid request'}
# Only attempt to parse things sent to /command
if path == 'command':
try:
content_length = int(self.headers['Content-Length'])
raw_content = self.rfile.read(content_length)
# ~ print(type(raw_content))
content = json.loads(raw_content)
# Default to invalid command
response_dict['message'] = 'Command format invalid'
if 'type' in content:
# Delegate potentiall 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]
except Exception as exp:
response_dict['success'] = False
error = '{:s}: {!r}'.format(type(exp).__name__, exp.args)
response_dict['message'] = error
# Send response
self.send_response(response_code)
self.send_header('Content-type', response_type)
self.end_headers()
self.wfile.write(json.dumps(response_dict).encode('utf-8'))
return
def handle_add_cmd(self, command_data):
""" Handler for the "add" command
"""
if 'path' not in command_data:
return False, 'Command format invalid'
# Check if the path actually exists:
path = command_data['path']
if not os.path.isfile(path):
return False, 'File does not exist: {:s}'.format(path)
# TODO: check if file is video file
# TODO: get width, heigh and frame rate using ffprobe
ENCODE_QUEUE.append(path)
return True, 'Added to queue'
def main():
print('starting server...')
server = StoppableHTTPServer(('', 8081), CommandHandler)
# Start processing requests
thread = threading.Thread(None, server.run)
thread.start()
# Run fo' eva'
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print('[server interrupted]')
finally:
# Shutdown server
server.shutdown()
thread.join()
print('fin')
if __name__ == '__main__':
sys.exit(main())
software/hbkcd/hbkcd/daemon.py
#!/usr/bin/python3
import logging
import os
import sys
import threading
import time
from hbkcd.command_server import StoppableHTTPServer, CommandHandler
from hbkcd.queue import ENCODE_QUEUE
#
# HbkCD - Handbrake Control Daemon
#
###############################################################################
# Configuration Options
###############################################################################
LOGFILE_PATH = '/var/log/hbkcd/hbkcd.log'
LOG_FORMAT_STR = '%(asctime)s|%(levelname)s|%(filename)s:%(lineno)s|%(message)s'
LOG_DATE_FORMAT_STR = '%a %b %d %H:%M:%S %Y'
DEFAULT_LOG_LEVEL = logging.DEBUG
# Absolute path the to the HandbrakeCLI executable
HANDBRAKE_PATH = '/opt/local/bin/HandBrakeCLI'
# Command server port
COMMAND_SERVER_PORT = 8081
def check_configuration():
""" Check runtime parameters for correctness before attempting to use them.
"""
# Make sure specified HandbrakeCLI path exists and is a file
if not os.path.isfile(HANDBRAKE_PATH):
logging.error('Handbrake does not exist at path: {:s}'
.format(HANDBRAKE_PATH))
return False
# Make sure the specified HandbrakeCLI file is executable
if not os.access(HANDBRAKE_PATH, os.X_OK):
logging.error('Handbrake exists but is not executable ({:s})'
.format(HANDBRAKE_PATH))
return False
logging.debug('Runtime configuration checks OKAY')
return True
def main():
# Configure logging before attempting anything serious
logging.basicConfig(format=LOG_FORMAT_STR,
datefmt=LOG_DATE_FORMAT_STR,
# ~ filename=LOGFILE_PATH,
level=DEFAULT_LOG_LEVEL)
logging.info('hbkcd started')
# Validate runtime configuration before proceeding
if not check_configuration():
return -1
# Create the command server
logging.info('Starting command server')
server = StoppableHTTPServer(('', COMMAND_SERVER_PORT), CommandHandler)
# Start the command server in a separate "thread"
thread = threading.Thread(None, server.run)
thread.start()
logging.info('Command server running')
# Run the command server until CTRL-C'ed
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
logging.warn('Command server interrupted')
finally:
# Shutdown server
server.shutdown()
thread.join()
logging.info('Command server stopped')
logging.info('hbkcd stopping')
return 0
if __name__ == '__main__':
sys.exit(main())
software/hbkcd/hbkcd/queue.py
ENCODE_QUEUE = []
class QueueItem(object):
def __init__(self, path):
self.path = path
software/hbkcd/server.py
#!/usr/bin/python3
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
from multiprocessing import Process
import sys
import time
# https://blog.gocept.com/2011/08/04/shutting-down-an-httpserver/
# https://docs.aws.amazon.com/polly/latest/dg/example-Python-server-code.html
class CommandServer(Process):
def __init__(self):
Process.__init__(self)
self.server_address = ('127.0.0.1', 8081)
self.httpd = HTTPServer(self.server_address, CommandHandler)
self.keep_running = True
def stop(self):
print('stop called')
self.keep_running = False
# ~ if self.httpd:
# ~ print('pre shutdown')
# ~ self.httpd.shutdown()
# ~ print('post shutdown')
self.httpd.shutdown()
self.httpd.socket.close()
# ~ self.httpd.server_close()
print('stop returning')
def run(self):
print('command server running')
# ~ while self.keep_running:
self.httpd.serve_forever()
# ~ while self.keep_running:
# ~ self.httpd.handle_request()
print('command server stopping')
# HTTPRequestHandler class
class CommandHandler(BaseHTTPRequestHandler):
# GET
def do_GET(self):
# Strip off leading and/or trailing slashes
path = self.path.strip('/')
# Defaults
response_code = 400
response_type = 'text/html'
response_data = ''
# Send response status code
if path == 'queue':
response_code = 200
response_dict = {'queue': ['alpha', 'bravo', 'charlie']}
response_data = json.dumps(response_dict).encode()
elif not path:
response_code = 200
response = 'Why hello there'
response_data = response.encode('utf-8')
# Send response and data, if any
self.send_response(response_code)
if response_data:
self.send_header('Content-type', response_type)
self.end_headers()
if response_data:
self.wfile.write(response_data)
return
def main():
print('starting server...')
# Server settings
# Choose port 8080, for port 80, which is normally used for a http server,
# you need root access
# ~ server_address = ('127.0.0.1', 8081)
# ~ httpd = HTTPServer(server_address, CommandHandler)
# ~ print('running server...')
# ~ httpd.serve_forever()
cmd_server = CommandServer()
cmd_server.start()
# Run for a while
for idx in range(20):
print(idx)
time.sleep(1)
# Simluate a clean shutdown
cmd_server.stop()
print('post stop')
cmd_server.join()
print('fin')
if __name__ == '__main__':
sys.exit(main())
software/hbkcd/setup.py
#!/usr/bin/python3
from setuptools import setup, find_packages
setup(
name = 'hbkcd',
version = '0.0.1',
description = 'HandBrake Control Daemon',
packages = find_packages(),
)

Also available in: Unified diff