PageRenderTime 58ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/FlixCloud.php

https://github.com/michaellehmkuhl/flix_cloud-php
PHP | 530 lines | 375 code | 90 blank | 65 comment | 48 complexity | ed976144079565438cf9768c94c07cea MD5 | raw file
  1. <?php
  2. /*
  3. FlixCloud API PHP Library
  4. Version: 1.1
  5. Author: Steve Heffernan <steve@sevenwire.com>
  6. See the README file for info on how to use this library.
  7. */
  8. // FlixCloud Transcoding Job
  9. class FlixCloudJob {
  10. var $api_key; // Provided at https://flixcloud.com/settings
  11. var $recipe_id; // Can be found at http://flixcloud.com/overviews/recipes
  12. var $api_url = "https://www.flixcloud.com/jobs";
  13. var $input; // FlixCloudJobInputFile Object
  14. var $output; // FlixCloudJobOutputFile Object
  15. var $watermark; // FlixCloudJobWatermarkFile Object
  16. var $thumbnail; // FlixCloudJobThumbnailFile Object
  17. var $notification_url; // Sets notification URL if supplied.
  18. var $pass_through; // Allows user to pass an internally used value back with notification.
  19. // cURL Options
  20. var $timeout = 0; // Time in seconds to timeout send request. 0 is no timeout.
  21. // Dealing with the certificate. Still a sketchy area.
  22. // If you learn anything from working with it let me know.
  23. var $insecure; // Bypasses verifying the certificate if needed. Like curl -k or --insecure. Still somewhat secure.
  24. var $certificate; // Full path to the www.flixcloud.com.pem certificate. Not required most of the time. Look up CURLOPT_CAINFO for more info.
  25. var $certificate_dir; // Directory where certs live. Not required most of the time. Look up CURLOPT_CAPATH for more info.
  26. var $success;
  27. var $errors = array();
  28. var $status_code; // Used for testing
  29. var $result; // Used for testing
  30. var $final_xml; // Used for testing
  31. var $id; // Provided after job has been sent
  32. var $initialized_job_at; // Provided after job has been sent
  33. // Initialize. API key is required. Option to load up job info on initialize using params hash.
  34. function FlixCloudJob($api_key, $params = "") {
  35. $this->api_key = $api_key;
  36. if (is_array($params)) {
  37. if($params["recipe_id"]) $this->recipe_id = $params["recipe_id"];
  38. if($params["input_url"]) $this->set_input($params["input_url"], array("user" => $params["input_user"], "password" => $params["input_password"]));
  39. if($params["output_url"]) $this->set_output($params["output_url"], array("user" => $params["output_user"], "password" => $params["output_password"]));
  40. if($params["watermark_url"]) $this->set_watermark($params["watermark_url"], array("user" => $params["watermark_user"], "password" => $params["watermark_password"]));
  41. if($params["thumbnail_url"]) $this->set_thumbnail($params["thumbnail_url"], array("prefix" => $params["thumbnail_prefix"], "user" => $params["thumbnail_user"], "password" => $params["thumbnail_password"]));
  42. if($params["notification_url"]) $this->notification_url = $params["notification_url"];
  43. if($params["pass_through"]) $this->pass_through = $params["pass_through"];
  44. if($params["insecure"]) $this->insecure = true;
  45. if($params["certificate"]) $this->certificate = $params["certificate"];
  46. if($params["api_url"]) $this->api_url = $params["api_url"];
  47. // If params array is used it sends by default
  48. if($params["send"] !== false) $this->send();
  49. } elseif(intval($params) > 0) {
  50. $this->recipe_id = $params; // Backwards compatible for when recipe was second arg.
  51. }
  52. }
  53. // Create input file object from URL and option user credentials
  54. function set_input($url, $params = array()) {
  55. $this->input = new FlixCloudJobInputFile($url, $params);
  56. }
  57. // Create output file object from URL and option user credentials
  58. function set_output($url, $params = array()) {
  59. $this->output = new FlixCloudJobOutputFile($url, $params);
  60. }
  61. // Create watermark file object from URL and option user credentials
  62. function set_watermark($url, $params = array()) {
  63. $this->watermark = new FlixCloudJobWatermarkFile($url, $params);
  64. }
  65. // Create thumbnail file object from URL and option user credentials
  66. function set_thumbnail($url, $params = array()) {
  67. $this->thumbnail = new FlixCloudJobThumbnailFile($url, $params);
  68. }
  69. // Check that all required info is available and valid. Used before sending.
  70. function validate() {
  71. if ( !function_exists("curl_version")) $this->errors[] = "cURL is not installed.";
  72. if ( !$this->api_key) $this->errors[] = "API key is required.";
  73. if ( !$this->recipe_id || intval($this->recipe_id) <= 0) $this->errors[] = "Recipe ID is required and must be an integer.";
  74. // Validate the different file types.
  75. foreach(array($this->input, $this->output, $this->watermark, $this->thumbnail) as $job_file) {
  76. if($job_file) {
  77. if ( !$job_file->validate()) $this->errors = array_merge($this->errors, $job_file->errors);
  78. }
  79. }
  80. // Return false if any errors.
  81. if(count($this->errors) > 0) return false;
  82. return true;
  83. }
  84. // Send job request to FlixCloud
  85. function send() {
  86. $this->success = false;
  87. if ( !$this->validate()) return false;
  88. $this->final_xml = $this->get_job_xml();
  89. // Set up cURL connection
  90. $ch = curl_init($this->api_url);
  91. curl_setopt_array($ch, array(
  92. CURLOPT_RETURNTRANSFER => 1,
  93. CURLOPT_HEADER => 0, // Don't return the header in result
  94. CURLOPT_HTTPHEADER => array("Accept: text/xml", "Content-type: application/xml"), // Required headers
  95. CURLOPT_POST => 1,
  96. CURLOPT_POSTFIELDS => $this->final_xml, // XML data
  97. CURLOPT_CONNECTTIMEOUT => $this->timeout,
  98. ));
  99. if($this->certificate) {
  100. curl_setopt($ch, CURLOPT_CAINFO, $this->certificate);
  101. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
  102. }
  103. if($this->certificate_dir) {
  104. curl_setopt($ch, CURLOPT_CAPATH, $this->certificate_dir);
  105. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
  106. }
  107. if($this->insecure) {
  108. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  109. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  110. }
  111. // Execute send
  112. $this->result = curl_exec($ch);
  113. if (curl_errno($ch)) {
  114. $this->errors[] = "Curl error (".curl_errno($ch)."): ".curl_error($ch);
  115. return false;
  116. }
  117. // Store the HTTP status code given (201, 400, etc.)
  118. $this->status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  119. // Close cURL connection;
  120. curl_close($ch);
  121. // Respond based on HTTP status code.
  122. switch ($this->status_code) {
  123. case "201": // Successful
  124. // Parse returned XML and get vars array
  125. $xml_obj = get_object_vars(new SimpleXMLElement($this->result));
  126. // Set job ID
  127. $this->id = $xml_obj["id"];
  128. // Set job's initialized time in UTC using the format YYYY-MM-DDTHH:MM:SSZ
  129. $this->initialized_job_at = $xml_obj["initialized-job-at"];
  130. $this->success = true;
  131. return true;
  132. case "200":
  133. $xml_obj = get_object_vars(new SimpleXMLElement($this->result));
  134. $this->errors[] = $xml_obj["error"];
  135. $this->array_flatten($this->errors);
  136. return false;
  137. case "302":
  138. $this->errors[] = "Redirection occurred (302). There's an error in the API URL.";
  139. return false;
  140. case "400":
  141. $xml_obj = get_object_vars(new SimpleXMLElement($this->result));
  142. $this->errors[] = $xml_obj["description"].$xml_obj["error"];
  143. return false;
  144. case "401":
  145. $this->errors[] = "Access denied (401). Probably a bad API key.";
  146. return false;
  147. case "404":
  148. $this->errors[] = "Page not found (404). The API url may be wrong, or the site is down.";
  149. return false;
  150. case "500":
  151. $this->errors[] = "The server returned an error (500). The API may be down.";
  152. return false;
  153. default:
  154. $temp_status_code = ($this->status_code) ? $this->status_code : "none";
  155. $temp_result = ($this->result) ? "\"".$this->result."\"" : "empty";
  156. $this->errors[] = "An unknown error has occurred."." Status Code: ".$temp_status_code.". Result ".$temp_result;
  157. return false;
  158. }
  159. }
  160. // Organize job data needed for API into a hash, then convert to XML.
  161. function get_job_xml() {
  162. // Watermark is optional. If left blank it won't be included in XML.
  163. if($this->watermark) {
  164. $watermark_hash = $this->watermark->get_data_hash();
  165. }
  166. // Thumbnail is optional. If left blank it won't be included in XML.
  167. if($this->thumbnail) {
  168. $thumbnail_hash = $this->thumbnail->get_data_hash();
  169. }
  170. // API XML in hash form
  171. // If key is an integer (0 included) value will be added to XML as is.
  172. $xml_hash = array(
  173. 0 => '<?xml version="1.0" encoding="UTF-8"?>',
  174. "api-request" => array(
  175. "api-key" => $this->api_key,
  176. "recipe-id" => $this->recipe_id,
  177. "file-locations" => array(
  178. "input" => $this->input->get_data_hash(),
  179. "output" => $this->output->get_data_hash(),
  180. "watermark" => $watermark_hash,
  181. "thumbnails" => $thumbnail_hash,
  182. ),
  183. ),
  184. );
  185. if($this->notification_url) {
  186. $xml_hash["api-request"]["notification-url"] = $this->notification_url;
  187. }
  188. if($this->pass_through) {
  189. $xml_hash["api-request"]["pass-through"] = $this->pass_through;
  190. }
  191. return $this->hash_to_xml($xml_hash);
  192. }
  193. // Create XML from a hash
  194. // Does some uneccessary formatting of the XML, but it's nice for debugging.
  195. function hash_to_xml($hash, $starting_indent = 0) {
  196. $xml = "";
  197. $indent = "";
  198. for($i=0; $i<$starting_indent; $i++) { $indent .= " "; }
  199. foreach($hash as $tag_name => $tag_contents) {
  200. $xml .= $indent;
  201. // If key is an integer (0 included) value will be added to XML as is.
  202. if (is_int($tag_name)) {
  203. $xml .= $tag_contents."\n";
  204. } elseif (is_array($tag_contents)) {
  205. $xml .= $this->tag($tag_name, "\n".$this->hash_to_xml($tag_contents, $starting_indent+1).$indent)."\n";
  206. } elseif ($tag_contents) {
  207. $xml .= $this->tag($tag_name, htmlspecialchars($tag_contents))."\n";
  208. }
  209. }
  210. return $xml;
  211. }
  212. // Create a simple XML tag from a name and content. Escape special characters (&<>).
  213. function tag($tag_name, $tag_content) {
  214. return '<'.$tag_name.'>'.$tag_content.'</'.$tag_name.'>';
  215. }
  216. // Flatten a basic array. Kinda surpised PHP doesn't have this.
  217. function array_flatten(&$array) {
  218. foreach($array as $key => $val) {
  219. if(is_array($val)) {
  220. unset($array[$key]);
  221. $this->array_flatten($val);
  222. $array = array_merge($array, $val);
  223. }
  224. }
  225. return $array;
  226. }
  227. }
  228. // Class for holding media file info (file url, user, pass)
  229. class FlixCloudJobFile {
  230. var $name = "base"; // Used in errors
  231. var $url; // Location of file including tranfer protocol (http, ftp, etc.)
  232. var $user; // Username
  233. var $password; // Password
  234. var $protocol; // Set and used in validating.
  235. var $errors = array();
  236. // Info received from notification
  237. var $width; // In pixels
  238. var $height; // In pixels
  239. var $size; // In bytes
  240. var $duration; // In milliseconds
  241. var $cost; // In millicents
  242. // Initialize
  243. function FlixCloudJobFile($url, $params = array()) {
  244. $this->url = trim($url);
  245. $this->set_attributes($params);
  246. }
  247. // Validate that all needed data is available.
  248. function validate() {
  249. if( !$this->url) $this->errors[] = $this->name." file url required.";
  250. // Return false if any errors.
  251. if(count($this->errors) > 0) return false;
  252. return true;
  253. }
  254. // Check if protocal supplied matches an accepted protocol for the file type
  255. function check_protocol() {
  256. preg_match ("/^[^:]+/i", $this->url, $matches);
  257. $this->protocol = $matches[0];
  258. if( !in_array($this->protocol, $this->acceptable_protocols)) {
  259. return false;
  260. }
  261. return true;
  262. }
  263. // Return a hash of values to be turned into XML later
  264. function get_data_hash() {
  265. $hash = array("url" => $this->url);
  266. if($this->user) {
  267. $hash["parameters"] = array(
  268. "user" => $this->user,
  269. "password" => $this->password
  270. );
  271. }
  272. return $hash;
  273. }
  274. function set_attributes($hash) {
  275. if(is_array($hash)) {
  276. foreach($hash as $key => $value) {
  277. if(strpos($key, "-")) $key = str_replace("-", "_", $key);
  278. $this->$key = $value;
  279. }
  280. }
  281. }
  282. }
  283. // Input File Data Object
  284. class FlixCloudJobInputFile extends FlixCloudJobFile {
  285. var $name = "Input";
  286. //var $acceptable_protocols = array("http", "https", "ftp", "sftp", "s3");
  287. }
  288. // Output File Data Object
  289. class FlixCloudJobOutputFile extends FlixCloudJobFile {
  290. var $name = "Output";
  291. //var $acceptable_protocols = array("ftp", "sftp", "s3");
  292. }
  293. // Watermark File Data Object
  294. class FlixCloudJobWatermarkFile extends FlixCloudJobFile {
  295. var $name = "Watermark";
  296. //var $acceptable_protocols = array("http", "https", "ftp", "sftp", "s3");
  297. }
  298. // Thumbnail File Data Object
  299. class FlixCloudJobThumbnailFile extends FlixCloudJobFile {
  300. var $name = "Thumbnail";
  301. var $prefix;
  302. // Info received from notification
  303. var $total_size;
  304. var $number_of_thumbnails;
  305. function get_data_hash() {
  306. $hash = array("url" => $this->url);
  307. $hash["prefix"] = $this->prefix;
  308. if($this->user) {
  309. $hash["parameters"] = array(
  310. "user" => $this->user,
  311. "password" => $this->password
  312. );
  313. }
  314. return $hash;
  315. }
  316. }
  317. class FlixCloudNotificationHandler {
  318. function catch_and_parse() {
  319. $incoming = file_get_contents('php://input');
  320. $incoming = trim($incoming);
  321. $hash = get_object_vars(new SimpleXMLElement($incoming));
  322. return new FlixCloudJobNotification($hash);
  323. }
  324. }
  325. // Class for catching and parsing XML data sent from FlixCloud when job is finished.
  326. // Notification URL must be set in https://flixcloud.com/settings
  327. class FlixCloudJobNotification {
  328. var $original_hash; // From XML object
  329. var $id; // The job's ID
  330. var $initialized_job_at; // When the job was initialized. UTC YYYY-MM-DDTHH:MM:SSZ
  331. var $recipe_name; // Name of recipe used
  332. var $recipe_id; // ID of recipe
  333. var $state; // failed_job, cancelled_job, or successful_job
  334. var $error_message; // Error message if there was one.
  335. var $finished_job_at; // When the job finished. UTC YYYY-MM-DDTHH:MM:SSZ
  336. var $pass_through; // User defined value passed from job request
  337. // FlixCloudJobFile Objects
  338. var $input; // $fcjn->input->url (url, width, height, size, duration, cost)
  339. var $output; // $fcjn->output->url, (url, width, height, size, duration, cost)
  340. var $watermark; // $fcjn->watermark->url (url, size, cost)
  341. var $thumbnail; // $fcjn->thumbnail->url (url, total_size, number_of_thumbnails, cost)
  342. function FlixCloudJobNotification($hash) {
  343. $this->original_hash = $hash;
  344. // Set attributes from XML on object
  345. // Creat job file objects from file info
  346. foreach($hash as $hash_key => $hash_value) {
  347. $method_name = "set_".str_replace("-", "_", $hash_key);
  348. if(method_exists($this, $method_name)) {
  349. $this->$method_name($hash_value);
  350. } else {
  351. $var_name = str_replace("-", "_", $hash_key);
  352. $this->$var_name = trim($hash_value);
  353. }
  354. }
  355. }
  356. function set_input_media_file($hash_value) {
  357. $this->input = new FlixCloudJobInputFile($hash_value["url"], get_object_vars($hash_value));
  358. }
  359. function set_output_media_file($hash_value) {
  360. $this->output = new FlixCloudJobOutputFile($hash_value["url"], get_object_vars($hash_value));
  361. }
  362. function set_watermark_file($hash_value) {
  363. $this->watermark = new FlixCloudJobWatermarkFile($hash_value["url"], get_object_vars($hash_value));
  364. }
  365. function set_thumbnail_media_file($hash_value) {
  366. $this->thumbnail = new FlixCloudJobThumbnailFile($hash_value["url"], get_object_vars($hash_value));
  367. }
  368. }
  369. // Class for checking the status of a job and its tasks.
  370. class FlixCloudJobStatusChecker {
  371. var $api_key;
  372. var $job_id;
  373. var $result;
  374. var $result_hash;
  375. var $errors = array();
  376. var $check_successful;
  377. function FlixCloudJobStatusChecker($api_key, $job_id) {
  378. $this->api_key = $api_key;
  379. $this->job_id = $job_id;
  380. $this->check_successful = $this->send();
  381. }
  382. function send() {
  383. // Set up cURL connection
  384. $ch = curl_init("https://www.flixcloud.com/jobs/".$this->job_id."/status");
  385. curl_setopt_array($ch, array(
  386. CURLOPT_RETURNTRANSFER => 1,
  387. CURLOPT_HEADER => 0, // Don't return the header in result
  388. CURLOPT_HTTPHEADER => array("Accept: text/xml", "Content-type: application/xml"), // Required headers
  389. CURLOPT_POST => 0,
  390. CURLOPT_CONNECTTIMEOUT => 0
  391. ));
  392. // Bypass cert check for status check to avoid issues
  393. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  394. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  395. // HTTP Basic Auth
  396. curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
  397. curl_setopt($ch, CURLOPT_USERPWD, 'user:'.$this->api_key);
  398. // Execute
  399. $this->result = curl_exec($ch);
  400. // Close cURL connection;
  401. curl_close($ch);
  402. if($this->result) {
  403. if(strpos($this->result, "Access denied.") > -1) {
  404. $this->errors[] = $this->result;
  405. return false;
  406. } else {
  407. $this->result_hash = get_object_vars(new SimpleXMLElement($this->result));
  408. return true;
  409. }
  410. } else {
  411. if (curl_errno($ch)) {
  412. $this->errors[] = "Curl error (".curl_errno($ch)."): ".curl_error($ch);
  413. }
  414. return false;
  415. }
  416. }
  417. function get_job_status() {
  418. if($this->check_successful) {
  419. return $this->result_hash["task-state"];
  420. } else {
  421. return false;
  422. }
  423. }
  424. }
  425. // Function that makes checking the overall status of a job a little simpler.
  426. function get_flix_cloud_job_status($api_id, $job_id) {
  427. $checker = new FlixCloudJobStatusChecker($api_id, $job_id);
  428. $status = $checker->get_job_status();
  429. return ($status) ? $status : "Status Unavailable";
  430. }
  431. ?>