Revision a11df5e5
Added by David Sorber over 7 years ago
| software/hbkcd/hbkcd/config.py | ||
|---|---|---|
|
# Absolute path the to the HandbrakeCLI executable
|
||
|
HANDBRAKE_PATH = '/opt/local/bin/HandBrakeCLI'
|
||
|
|
||
|
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']
|
||
|
|
||
|
# Absolute path to the ffprobe executable
|
||
|
FFPROBE_PATH = '/home/dsorber/ffprobe'
|
||
|
|
||
|
# The ffprobe command used to get information about the video file
|
||
|
FFPROBE_CMD = '{:s} -show_streams -show_entries stream=width,height,'\
|
||
|
'display_aspect_ratio,r_frame_rate,bit_rate,codec_type,'\
|
||
|
'codec_name -of json -v quiet -i "{:s}"'
|
||
|
FFPROBE_CMD = [FFPROBE_PATH, '-show_streams', '-show_entries',
|
||
|
'stream=width,height,display_aspect_ratio,r_frame_rate,'
|
||
|
'bit_rate,codec_type,codec_name', '-of', 'json', '-v', 'quiet',
|
||
|
'-i', '']
|
||
|
|
||
|
# Command server port
|
||
|
COMMAND_SERVER_PORT = 8081
|
||
| software/hbkcd/hbkcd/daemon.py | ||
|---|---|---|
|
#!/usr/bin/python3
|
||
|
import datetime
|
||
|
import logging
|
||
|
import os
|
||
|
import subprocess
|
||
|
import sys
|
||
|
import threading
|
||
|
import time
|
||
|
|
||
|
from hbkcd.config import *
|
||
|
from hbkcd.command_server import StoppableHTTPServer, CommandHandler
|
||
|
# ~ from hbkcd.queue import ENCODE_QUEUE
|
||
|
from hbkcd.queue import ENCODE_QUEUE
|
||
|
|
||
|
#
|
||
|
# HbkCD - Handbrake Control Daemon
|
||
| ... | ... | |
|
try:
|
||
|
while True:
|
||
|
time.sleep(1)
|
||
|
item = ENCODE_QUEUE.get_next()
|
||
|
if item is None:
|
||
|
continue
|
||
|
|
||
|
# Mark next item as in progress
|
||
|
item.in_progress = True
|
||
|
item.encode_started = datetime.datetime.now()
|
||
|
|
||
|
# Start Handbrake process
|
||
|
cmd = HANDBRAKE_CMD
|
||
|
cmd[2] = item.path
|
||
|
|
||
|
hbk_proc = subprocess.Popen(cmd,
|
||
|
stderr=subprocess.PIPE,
|
||
|
stdout=subprocess.PIPE,
|
||
|
encoding='utf-8')
|
||
|
|
||
|
# Create an interator to the stdout of the process so we can
|
||
|
# monitor status
|
||
|
proc_stdout = iter(hbk_proc.stdout.readline, '')
|
||
|
status_thread = threading.Thread(target=item.update_status,
|
||
|
args=(proc_stdout,))
|
||
|
status_thread.start()
|
||
|
|
||
|
# Let the HandBrake process run and print a message to the log
|
||
|
# every minute
|
||
|
ctr = 0
|
||
|
while hbk_proc.poll() is None:
|
||
|
ctr += 1
|
||
|
if ctr % 60 == 0:
|
||
|
logging.info('Encode in progress... {:s}'.format(ctr, item.status))
|
||
|
ctr = 0
|
||
|
time.sleep(1)
|
||
|
|
||
|
item.encode_finished = datetime.datetime.now()
|
||
|
status_thread.join()
|
||
|
item.in_progress = False
|
||
|
|
||
|
if hbk_proc.returncode == 0:
|
||
|
logging.info('Encode of {:s} completed!'.format(item.path))
|
||
|
else:
|
||
|
logging.error('Error while encoding {:s}'.format(item.path))
|
||
|
|
||
|
# Remove the item from the queue
|
||
|
ENCODE_QUEUE.remove(0)
|
||
|
|
||
|
except KeyboardInterrupt:
|
||
|
logging.warn('Command server interrupted')
|
||
|
finally:
|
||
| software/hbkcd/hbkcd/queue.py | ||
|---|---|---|
|
item.position = position
|
||
|
|
||
|
def remove(self, idx):
|
||
|
# TODO: implement removal
|
||
|
pass
|
||
|
if idx >= len(self.queue):
|
||
|
logging.error('Invalid index: {:d}'.format(idx))
|
||
|
return False
|
||
|
|
||
|
if self.queue[idx].in_progress:
|
||
|
logging.error('Unable to remove index {:d}; encode in progress'
|
||
|
.format(idx))
|
||
|
return False
|
||
|
|
||
|
logging.debug('Removing index: {:d}'.format(idx))
|
||
|
self.queue.pop(idx)
|
||
|
|
||
|
# Re-set positions for all items
|
||
|
ctr = 1
|
||
|
for item in self.queue[]
|
||
|
item.position = ctr
|
||
|
ctr += 1
|
||
|
|
||
|
return True
|
||
|
|
||
|
def get_list(self):
|
||
|
""" Get queue as a list of dicts that can be converted to JSON easily
|
||
|
"""
|
||
|
return [item.get_dict_rep() for item in self.queue]
|
||
|
|
||
|
def get_next(self):
|
||
|
if not self.queue:
|
||
|
return None
|
||
|
else:
|
||
|
return self.queue[0]
|
||
|
|
||
|
|
||
|
class QueueItem(object):
|
||
|
|
||
| ... | ... | |
|
self.filename = os.path.basename(path)
|
||
|
self.position = None
|
||
|
self.in_progress = False
|
||
|
self.status = 'queued'
|
||
|
|
||
|
# Video
|
||
|
self.width = None
|
||
| ... | ... | |
|
|
||
|
def probe(self):
|
||
|
# Fill in ffprobe command
|
||
|
cmd = FFPROBE_CMD.format(FFPROBE_PATH, self.path)
|
||
|
proc = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE,
|
||
|
cmd = FFPROBE_CMD
|
||
|
cmd[9] = self.path
|
||
|
proc = subprocess.run(cmd,
|
||
|
stdout=subprocess.PIPE,
|
||
|
stderr=subprocess.PIPE)
|
||
|
|
||
|
probe_okay = True
|
||
| ... | ... | |
|
return {'position': self.position,
|
||
|
'path': self.path,
|
||
|
'in_progress': self.in_progress,
|
||
|
'status': self.status,
|
||
|
'width': self.width,
|
||
|
'height': self.height,
|
||
|
'framerate': self.framerate,
|
||
| ... | ... | |
|
'encode_started': format_ds(self.encode_started),
|
||
|
'encode_finished': format_ds(self.encode_finished)}
|
||
|
|
||
|
def update_status(self, stdout_iter):
|
||
|
logging.info('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')
|
||
|
|
||
|
|
||
|
# Does this need to be protected?
|
||
|
ENCODE_QUEUE = EncodeQueue()
|
||
| software/hbkcd/setup.py | ||
|---|---|---|
|
|
||
|
setup(
|
||
|
name = 'hbkcd',
|
||
|
version = '0.0.2',
|
||
|
version = '0.0.3',
|
||
|
description = 'HandBrake Control Daemon',
|
||
|
packages = find_packages(),
|
||
|
python_requires = '>=3.6.0',
|
||
It works! Need to add some polish before we're ready to call it 0.1, but
nearly there!