Médiacad dispose de 4 webServices permettant d'obtenir le nombre de tâches d'encodage en cours ou une nouvelle tâche, et d'indiquer un succès ou une erreur d'encodage.
C'est via ces webServices que le serveur d'encodage va interagir avec Médiacad.
La répartition des rôles est très claire et doit être respectée à la lettre :
Un script python tournant sur le serveur d'encodage est disponible. Il gère toutes les opérations d'encodage externes.
Dans /local/config/application.cfg.array.php, modifier les entrées suivantes :
['medias']['isAllowedEncoding'] = false => Médiacad ne réalisera plus d'encodage
['medias']['maxParallelEncoding'] = XX => nombre maxi d'encodages en parallèle sur le serveur d'encodage
tous les paths de la partie ['medias']['encode'] concernent le serveur web de Médiacad, pas le serveur d'encodage
dans ['auth']['adapters']['salt']['config']['passwords'], ajouter une entrée pour le serveur d'encodage différente de Médiacad (ce sera le login), par exemple "encodeServer", et indiquer son mot de passe.
Dans /local/config/wsAcl.cfg.array.php, modifier les entrées suivantes :
dans ['ws']['encode']['getinprogresstaskcount'], ['getnexttask'], ['setencodesuccess'] et ['setencodeerror'], ajouter le login du serveur d'encodage ("encodeServer" dans notre exemple).
La documentation ci-dessous n'est utile que si vous ne souhaitez pas utiliser le script python fourni.
Les webServices utilisent par défaut l'adaptateur d'authentification salt (= "grain de sel").
Le principe :
La connexion n'est valable que pour une seule action (la session est réinitialisée ensuite), tout nouvel appel au ws doit recommencer la procédure.
Pour information, voici les deux méthodes de l'adaptateur salt :
public function isAuth()
{
if (!isset($_POST['login']) or !isset($_POST['password'])
or !isset($this->_adapterOptions['passwords'][(string) $_POST['login']])
or is_null(App_Lib_Session::get('serviceAuth', 'salt'))) {
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE, array());
}
$saltedPassword = hash('sha256', $this->_adapterOptions['passwords'][(string) $_POST['login']] . App_Lib_Session::get('serviceAuth', 'salt'));
App_Lib_Session::namespaceUnset('serviceAuth');
return (string) $_POST['password'] == $saltedPassword ? new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, array('uid' => (string) $_POST['login']))
: new Zend_Auth_Result(Zend_Auth_Result::FAILURE, array());
}
public function authenticate()
{
$_SESSION = array();
// renvoi du grain de sel en Json
$appLibControllerActionHelperRandomString = new App_Lib_Controller_Action_Helper_RandomString();
$salt = $appLibControllerActionHelperRandomString->direct(30);
App_Lib_Session::set('serviceAuth', 'salt', $salt);
echo Zend_Json::encode(array('isSuccess' => true, 'data' => array('salt' => $salt)));
exit;
}
Médiacad utilise une classe (trunk/application/library/WebService.php) qui permet de requêter ses webServices.
Le code principal est le suivant :
public function requestWS($uri, $params = array())
{
// initialisation
$ch = curl_init($uri);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array('data' => 'data'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIESESSION, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'mediacadWSSession');
// récupération du salt
$result = curl_exec($ch);
if ($result === false) {
return array('isSuccess' => false, 'errorCode' => curl_error($ch), 'errorMessage' => curl_error($ch), 'data' => array());
}
$result = Zend_Json::decode($result, Zend_Json::TYPE_ARRAY);
if (!isset($result['data']['salt'])) {
$translate = Zend_Registry::get('Zend_Translate');
return array('isSuccess' => false, 'errorCode' => 'appLibWebServiceMissingSaltError', 'errorMessage' => $translate->_('appLibWebServiceMissingSaltError'), 'data' => array());
}
// login et requête
$params['login'] = $this->_login;
$params['password'] = hash('sha256', $this->_password . $result['data']['salt']);
curl_setopt($ch, CURLOPT_COOKIEFILE, 'mediacadWSSession');
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_URL, $uri);
$result = curl_exec($ch);
if ($result === false) {
$message = array('isSuccess' => false, 'errorCode' => curl_error($ch), 'errorMessage' => curl_error($ch), 'data' => array());
} else {
$message = Zend_Json::decode($result, Zend_Json::TYPE_ARRAY);
}
curl_close($ch);
return $message;
}
Chaque webService renvoie un json de la forme suivante :
message de succès :
{"isSuccess":true,"data":{"key":"value"}}
ce qui, une fois décodé, donne en php :
array('isSuccess' => true,
'data' => array('key' => 'value'));
message d'erreur :
{"isSuccess":false,"errorCode":"wsEncodeError404","errorMessage":"Action introuvable.","data":[]}
ce qui, une fois décodé, donne en php :
array('isSuccess' => false,
'errorCode' => 'wsEncodeError404',
'errorMessage' => 'Action introuvable.',
'data' => array());
Les messages d'erreurs possibles à la connexion sont :
errorCode |
errorMessage |
solution |
wsEncodeError401 |
Accès à cette action interdit. |
problème d'ACL => revoir les configs |
wsEncodeError404 |
Action introuvable |
revoir l'uri demandée |
.
webService : getnexttask (path_de_mediacad/ws/encode/gestnexttask)
données à transmettre : aucune.
Médiacad renvoie un message de succès mais avec 'hasNewEncodingTask' == false.
{"isSuccess":true,"data":{"hasNewEncodingTask":false}}
ce qui, une fois décodé, donne en php :
array('isSuccess' => true,
'data' => array(
'hasNewEncodingTask' => false));
Médiacad renvoie les données nécessaires à l'accomplissement de la tâche.
Il y a 3 types de tâches possibles : copy, delete et encode.
{"isSuccess":true,"data":{"hasNewEncodingTask":true,"encodingTaskId":"68","task":"copy","sourceLocation":"##sourcePath##\/video\/17.mp4","destLocation":"##encodedPath##\/video\/17.mp4"}}
ce qui, une fois décodé, donne en php :
array('isSuccess' => true,
'data' => array(
'hasNewEncodingTask' => true,
'encodingTaskId' => '68',
'task' => 'copy',
'sourceLocation' => '##sourcePath##/video/17.mp4',
'destLocation' => '##encodedPath##/video/17.mp4'));
{"isSuccess":true,"data":{"hasNewEncodingTask":true,"encodingTaskId":"69","task":"delete","location":"##sourcePath##\/book\/18.odt"}}
ce qui, une fois décodé, donne en php :
array('isSuccess' => true,
'data' => array(
'hasNewEncodingTask' => true,
'encodingTaskId' => '69',
'task' => 'delete',
'location' => '##sourcePath##/book/18.odt'));
{"isSuccess":true,"data":{"hasNewEncodingTask":true,"encodingTaskId":"67","task":"encode","application":"ffmpeg","exe":"ffmpeg","command":"##applicationPath## -i \"##sourcePath##\/video\/17.mp4\" -vcodec libvpx -vb 700k -maxrate 8000k -bufsize 1000K -minrate 10k -qmin 3 -qmax 30 -rc_lookahead 16 -keyint_min 0 -g 360 -skip_threshold 0 -level 116 -preset slow -ab 192k \"##encodedPath##\/video\/17.webm\""}}
ce qui, une fois décodé, donne en php :
array('isSuccess' => true,
'data' => array(
'hasNewEncodingTask' => true,
'encodingTaskId' => '67',
'task' => 'encode',
'application' => 'ffmpeg',
'exe' => 'ffmpeg',
'command' => '##applicationPath## -i "##sourcePath##/video/17.mp4" -vcodec libvpx -vb 700k -maxrate 8000k -bufsize 1000K -minrate 10k -qmin 3 -qmax 30 -rc_lookahead 16 -keyint_min 0 -g 360 -skip_threshold 0 -level 116 -preset slow -ab 192k "##encodedPath##/video/17.webm"'));
Avant de réaliser la tâche, il faut remplacer les ##xxxPath## des données renvoyées.
##applicationPath## : sera remplacé par le path de l'application sur le serveur d'encodage, en fonction des clefs 'application' et 'exe' (voir plus haut l'équivalent dans Médiacad (trunk/local/config/application.cfg.array.php), partie ['medias']['encode']).
##tmpPath##, ##sourcePath## et ##encodedPath## : sera remplacé par les paths correspondants (voir plus haut l'équivalent dans Médiacad (trunk/local/config/application.cfg.array.php), partie ['medias']).
Une fois la tâche réalisée, le serveur d'encodage doit requêter selon le cas "setencodesuccess" ou "setencodeerror".
Pour information, la méthode utilisée par Médiacad pour l'encodage (trunk/application/library/Media.php) est la suivante :
public function runNextEncodingTask()
{
$appModelEncodingTask = $this->_getAppModelEncodingTask();
// récupération des tâches inProgress avec timeout dépassé depuis plus de 5 min
$exceededTimeTaskRowset = $appModelEncodingTask->getExceededTimeTasks(5);
if (!is_null($exceededTimeTaskRowset)) {
// met les tâches en erreur
foreach ($exceededTimeTaskRowset as $exceededTimeTaskRow) {
$this->setEncodeError($exceededTimeTaskRow['encodingTaskId'], 'appLibMediaEncodingTimeout');
}
}
// récupération du nombre de tâches inProgress
$inProgressTaskCount = $appModelEncodingTask->getInProgressTasksCount();
// si ce nombre est < au nombre maxi => récupération de la tâche suivante en wait + passage à inProgress
$encodingTaskRow = null;
if ($inProgressTaskCount < $this->_localConfig['maxParallelEncoding']) {
$encodingTaskRow = $appModelEncodingTask->setInProgressForNextTask();
}
// pas de nouvelle tâche disponible => fin de la méthode
if (is_null($encodingTaskRow)) {
return null;
}
// envoi d un nouveau ping s il reste des tâches et que le max n est pas atteint
if (($inProgressTaskCount + 1) < $this->_localConfig['maxParallelEncoding']) {
$this->pingEncode();
}
// traitement de la tâche
// config php
ini_set('memory_limit', $this->_localConfig['encodingTaskMemoryLimit']);
set_time_limit($encodingTaskRow['maxInProgressDuration'] * 60);
// lancement de la tâche
$encodeCommand = Zend_Json::decode($encodingTaskRow['params']);
$beginTime = microtime(true);
// copy
if ($encodeCommand['task'] == 'copy') {
$errorMessage = 'appLibMediaFileCopyError';
$sourceLocation = str_replace('##tmpPath##', MEDIA_TMP_PATH, $encodeCommand['sourceLocation']);
$sourceLocation = str_replace('##sourcePath##', MEDIA_SOURCE_PATH, $sourceLocation);
$sourceLocation = str_replace('##encodedPath##', MEDIA_ENCODED_PATH, $sourceLocation);
$destLocation = str_replace('##tmpPath##', MEDIA_TMP_PATH, $encodeCommand['destLocation']);
$destLocation = str_replace('##sourcePath##', MEDIA_SOURCE_PATH, $destLocation);
$destLocation = str_replace('##encodedPath##', MEDIA_ENCODED_PATH, $destLocation);
$result = copy($sourceLocation, $destLocation);
// delete
} else if ($encodeCommand['task'] == 'delete') {
$errorMessage = 'appLibMediaFileDeleteError';
$location = str_replace('##tmpPath##', MEDIA_TMP_PATH, $encodeCommand['location']);
$location = str_replace('##sourcePath##', MEDIA_SOURCE_PATH, $location);
$location = str_replace('##encodedPath##', MEDIA_ENCODED_PATH, $location);
$result = unlink($location);
// encode
} else {
$errorMessage = 'appLibMediaEncodingTaskError';
$encodeCommand = str_replace('##applicationPath##', $this->_localConfig['encode']['applications'][$encodeCommand['application']][$encodeCommand['exe']]['applicationPath'], $encodeCommand['command']);
$encodeCommand = str_replace('##tmpPath##', MEDIA_TMP_PATH, $encodeCommand);
$encodeCommand = str_replace('##sourcePath##', MEDIA_SOURCE_PATH, $encodeCommand);
$encodeCommand = str_replace('##encodedPath##', MEDIA_ENCODED_PATH, $encodeCommand) . ' 2>&1';
$output = array();
$result = 0;
exec($encodeCommand, $output, $result);
$result > 0 ? $result = false : $result = true;
if (!$result) {
if (count($output) > 0) {
isset($encodingTaskRow['fileId']) ? $fileId = $encodingTaskRow['fileId'] : $fileId = 'xx';
$errorLog = 'appLibMediaEncodingTaskError - fileId=' . $fileId . ' - errorMessage=';
foreach ($output as $line) {
$errorLog .= $line;
}
App_Lib_Log::log($errorLog, Zend_Log::ERR);
}
}
}
$endTime = microtime(true);
// fin de l encodage
$result ? $this->setEncodeSuccess($encodingTaskRow['encodingTaskId'], $endTime - $beginTime) : $this->setEncodeError($encodingTaskRow['encodingTaskId'], $errorMessage);
// envoi d un nouveau ping
$this->pingEncode();
}
Pas d'erreurs spécifiques au webService.
Le webService peut renvoyer les exceptions de l'application.
webService : setencodesuccess(path_de_mediacad/ws/encode/setencodesuccess)
données à transmettre : (string) ['encodingTaskId'] et (float) ['encodeDuration'] (en secondes).
{"isSuccess":true,"data":[]}
ce qui, une fois décodé, donne en php :
array('isSuccess' => true,
'data' => array());
errorCode |
errorMessage |
solution |
wsEncodeSetEncodeSuccessRequestMustBePost |
Les données doivent être envoyées en POST |
|
wsEncodeSetEncodeSuccessMissingPostFieldEncodingTaskId |
Champs POST 'encodingTaskId' manquant. |
renvoyer l'encodingTaskId reçu par gestnexttask |
wsEncodeSetEncodeSuccessUnknownEncodingTaskId |
EncodingTaskId inconnu. |
soit l'encodingTaskId a été modifié par le serveur d'encodage, soit le webService a déjà été appelé pour ce même encodingTaskId |
wsEncodeSetEncodeSuccessMissingPostFieldEncodeDuration |
Champs POST 'encodeDuration' manquant. |
renvoyer la durée d'encodage (float en secondes) |
Le webService peut aussi renvoyer les exceptions de l'application.
webService : setencodeerror(path_de_mediacad/ws/encode/setencodeerror)
données à transmettre : (string) ['encodingTaskId'] et (string) ['errorMessage'].
{"isSuccess":true,"data":[]}
ce qui, une fois décodé, donne en php :
array('isSuccess' => true,
'data' => array());
errorCode |
errorMessage |
solution |
wsEncodeSetEncodeErrorRequestMustBePost |
Les données doivent être envoyées en POST. |
|
wsEncodeSetEncodeErrorMissingPostFieldEncodingTaskId |
Champs POST 'encodingTaskId' manquant. |
renvoyer l'encodingTaskId reçu par gestnexttask |
wsEncodeSetEncodeErrorUnknownEncodingTaskId |
EncodingTaskId inconnu. |
soit l'encodingTaskId a été modifié par le serveur d'encodage, soit le webService a déjà été appelé pour ce même encodingTaskId |
wsEncodeSetEncodeErrorMissingPostFieldErrorMessage |
Champs POST 'errorMessage' manquant. |
renvoyer le message d'erreur |
Le webService peut aussi renvoyer les exceptions de l'application.
Créé avec HelpNDoc Personal Edition: Créer des documents d'aide PDF facilement