Fix recording through ecasound

This is part 2 of fixing ecasound recordings from line-in. Part 1 restored the User-Interface, part 2 takes care of getting to the point where ecasound gets started, records something and uploads it through rest when done. Part 3 will take care of making sure that the recorded file is mapped to the show and not just stored as a new track.

I refactored api_clients to not use urllib2 for posting multipart data since I was loosing my sanity over it and requests seems to have a modern approach to doing this compared to what api_clients was previously doing.
This commit is contained in:
Lucas Bickel 2017-03-24 15:12:06 +01:00
parent 674f17213f
commit b267036483
5 changed files with 55 additions and 36 deletions

View File

@ -73,10 +73,6 @@ class Config {
$CC_CONFIG['apiKey'] = array($values['general']['api_key']);
if (defined('APPLICATION_ENV') && APPLICATION_ENV == "development"){
$CC_CONFIG['apiKey'][] = "";
}
$CC_CONFIG['soundcloud-connection-retries'] = $values['soundcloud']['connection_retries'];
$CC_CONFIG['soundcloud-connection-wait'] = $values['soundcloud']['time_between_retries'];

View File

@ -642,25 +642,6 @@ class ApiController extends Zend_Controller_Action
}
}
public function uploadFileAction()
{
Logging::error("FIXME: Change the show recorder to use the File Upload API and remove this function."); // Albert - April 3, 2014
/**
$upload_dir = ini_get("upload_tmp_dir");
$tempFilePath = Application_Model_StoredFile::uploadFile($upload_dir);
$tempFileName = basename($tempFilePath);
$fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : '';
$result = Application_Model_StoredFile::copyFileToStor($upload_dir, $fileName, $tempFileName);
if (!is_null($result)) {
$this->_helper->json->sendJson(
array("jsonrpc" => "2.0", "error" => array("code" => $result['code'], "message" => $result['message']))
);
}
**/
}
public function uploadRecordedAction()
{
$show_instance_id = $this->_getParam('showinstanceid');

View File

@ -87,6 +87,8 @@ yum install -y \
fdk-aac \
liquidsoap \
silan \
ecasound \
alsa-utils \
icecast \
python-pip \
selinux-policy \
@ -155,3 +157,6 @@ systemctl restart httpd
# icecast needs to be available to everyone
sed -i -e 's@<bind-address>127.0.0.1</bind-address>@<bind-address>0.0.0.0</bind-address>@' /etc/icecast.xml
systemctl enable --now icecast
# let em use alsa
usermod -a -G audio apache

View File

@ -10,6 +10,7 @@ import sys
import time
import urllib
import urllib2
import requests
import socket
import logging
import json
@ -61,7 +62,7 @@ api_config['reload_metadata_group'] = 'reload-metadata-group/format/json/api_key
api_config['handle_watched_dir_missing'] = 'handle-watched-dir-missing/format/json/api_key/%%api_key%%/dir/%%dir%%'
#show-recorder
api_config['show_schedule_url'] = 'recorded-shows/format/json/api_key/%%api_key%%'
api_config['upload_file_url'] = 'upload-file/format/json/api_key/%%api_key%%'
api_config['upload_file_url'] = 'rest/media'
api_config['upload_retries'] = '3'
api_config['upload_wait'] = '60'
#pypo
@ -278,33 +279,57 @@ class AirtimeApiClient(object):
self.logger.error(str(e))
return None
def upload_recorded_show(self, data, headers):
def upload_recorded_show(self, files, show_id):
logger = self.logger
response = ''
retries = int(self.config["upload_retries"])
retries_wait = int(self.config["upload_wait"])
url = self.construct_url("upload_file_url")
url = self.construct_rest_url("upload_file_url")
logger.debug(url)
for i in range(0, retries):
logger.debug("Upload attempt: %s", i + 1)
logger.debug(files)
logger.debug(ApiRequest.API_HTTP_REQUEST_TIMEOUT)
try:
request = urllib2.Request(url, data, headers)
response = urllib2.urlopen(request, timeout=ApiClient.API_HTTP_REQUEST_TIMEOUT).read().strip()
request = requests.post(url, files=files, timeout=float(ApiRequest.API_HTTP_REQUEST_TIMEOUT))
response = request.json()
logger.debug(response)
logger.info("uploaded show result %s", response)
"""
FIXME: We need to tell LibreTime that the uploaded track was recorded for a specific show
My issue here is that response does not yet have an id. The id gets generated at the point
where analyzer is done with it's work. We probably need to do what is below in analyzer
and also make sure that the show instance id is routed all the way through.
It already gets uploaded by this but the RestController does not seem to care about it. In
the end analyzer doesn't have the info in it's rabbitmq message and imports the show as a
regular track.
logger.info("uploaded show result as file id %s", response.id)
url = self.construct_url("upload_recorded")
url = url.replace('%%fileid%%', response.id)
url = url.replace('%%showinstanceid%%', show_id)
request.get(url)
logger.info("associated uploaded file %s with show instance %s", response.id, show_id)
"""
break
except urllib2.HTTPError, e:
except requests.exceptions.HTTPError, e:
logger.error("Http error code: %s", e.code)
except urllib2.URLError, e:
logger.error("traceback: %s", traceback.format_exc())
except requests.exceptions.ConnectionError, e:
logger.error("Server is down: %s", e.args)
logger.error("traceback: %s", traceback.format_exc())
except Exception, e:
logger.error("Exception: %s", e)
logger.error("traceback: %s", traceback.format_exc())
#wait some time before next retry
time.sleep(retries_wait)
@ -332,6 +357,19 @@ class AirtimeApiClient(object):
url = url.replace("%%api_key%%", self.config["general"]["api_key"])
return url
def construct_rest_url(self,config_action_key):
"""Constructs the base url for RESTful requests"""
if self.config["general"]["base_dir"].startswith("/"):
self.config["general"]["base_dir"] = self.config["general"]["base_dir"][1:]
url = "%s://%s:@%s:%s/%s/%s" % \
(str(("http", "https")[int(self.config["general"]["base_port"]) == 443]),
self.config["general"]["api_key"],
self.config["general"]["base_url"], str(self.config["general"]["base_port"]),
self.config["general"]["base_dir"],
self.config[config_action_key])
return url
"""
Caller of this method needs to catch any exceptions such as
ValueError thrown by json.loads or URLError by urllib2.urlopen

View File

@ -95,7 +95,7 @@ class ShowRecorder(Thread):
self.logger.info("starting record")
self.logger.info("command " + command)
self.p = Popen(args,stdout=PIPE)
self.p = Popen(args,stdout=PIPE,stderr=PIPE)
#blocks at the following line until the child process
#quits
@ -129,11 +129,10 @@ class ShowRecorder(Thread):
# Register the streaming http handlers with urllib2
register_openers()
# headers contains the necessary Content-Type and Content-Length
# datagen is a generator object that yields the encoded parameters
datagen, headers = multipart_encode({"file": open(filepath, "rb"), 'name': filename, 'show_instance': self.show_instance})
# files is what requests actually expects
files = {'file': open(filepath, "rb"), 'name': filename, 'show_instance': str(self.show_instance)}
self.api_client.upload_recorded_show(datagen, headers)
self.api_client.upload_recorded_show(files, self.show_instance)
def set_metadata_and_save(self, filepath):
"""