Project

General

Profile

Download (6.92 KB) Statistics
| Branch: | Tag: | Revision:
#!/usr/bin/python3
from http.server import BaseHTTPRequestHandler, HTTPServer
import logging
import json
import os
import sys
import time
import threading

import magic

from hbkcd.queue import ENCODE_QUEUE, QueueItem

# 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.get_list()}
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 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
error = '{:s}: {!r}'.format(type(exp).__name__, exp.args)
response_dict['message'] = error
logging.error(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 or 'output' 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):
msg = 'File does not exist: {:s}'.format(path)
logging.error(msg)
return False, msg
# Check if file is video file
mimetype = magic.from_file(path, mime=True)
if not mimetype.startswith('video/'):
msg = 'File "{:s}" does not appear to be a video file ({:s})'
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, output_path)
if not item.probe():
msg = 'An error ocurred while probing the video file'
logging.error(msg)
return False, msg
# Add item to the queue
ENCODE_QUEUE.add(item)
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...')
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())
(2-2/5)