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