From 0c8e7ae689eed6291bb2061c9c75e3057b230339 Mon Sep 17 00:00:00 2001 From: Mohamed Mujahid Date: Tue, 6 Jul 2010 17:04:02 +0000 Subject: merging changes from DRUPAL-6--4 --- plugins/video_zencoder/includes/LICENSE | 20 + plugins/video_zencoder/includes/README.markdown | 199 ++++++ plugins/video_zencoder/includes/Zencoder.php | 239 +++++++ plugins/video_zencoder/includes/lib/JSON.php | 806 ++++++++++++++++++++++++ plugins/video_zencoder/includes/zencoder.inc | 280 ++++++++ 5 files changed, 1544 insertions(+) create mode 100644 plugins/video_zencoder/includes/LICENSE create mode 100644 plugins/video_zencoder/includes/README.markdown create mode 100644 plugins/video_zencoder/includes/Zencoder.php create mode 100644 plugins/video_zencoder/includes/lib/JSON.php create mode 100644 plugins/video_zencoder/includes/zencoder.inc (limited to 'plugins/video_zencoder/includes') diff --git a/plugins/video_zencoder/includes/LICENSE b/plugins/video_zencoder/includes/LICENSE new file mode 100644 index 0000000..d1f0a51 --- /dev/null +++ b/plugins/video_zencoder/includes/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2010 Zencoder + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/video_zencoder/includes/README.markdown b/plugins/video_zencoder/includes/README.markdown new file mode 100644 index 0000000..0be2256 --- /dev/null +++ b/plugins/video_zencoder/includes/README.markdown @@ -0,0 +1,199 @@ +Zencoder API PHP Library +========================== + +Author: [Steve Heffernan](http://www.steveheffernan.com) (steve (a) zencoder (.) com) +Company: [Zencoder - Online Video Encoder](http://zencoder.com) +Version: 1.1 +Date: 2010-06-04 +Repository: + +For more details on the Zencoder API requirements visit + + + +ENCODING JOB +------------ +The ZencoderJob object creates an encoding job using [cURL](http://zencoder.com/docs/glossary/curl/) +to send [JSON](http://zencoder.com/docs/glossary/json/) formatted parameters to Zencoder's encoding API. + +### Step 1 +Visit the [API builder](https://app.zencoder.com/api_builder) in your account, +and execute a successful encoding job. + +### Step 2 +Copy the successful JSON string, starting with the first curly brace "{", +and pass it as the parameters for a new ZencoderJob object. Execute the script on your server to test that it works. + +#### Example +
+    created) {
+      // Success
+      echo "w00t! \n\n";
+      echo "Job ID: ".$encoding_job->id."\n";
+      echo "Output '".$encoding_job->outputs["web"]->label."' ID: ".$encoding_job->outputs["web"]->id."\n";
+      // Store Job/Output IDs to update their status when notified or to check their progress.
+    } else {
+      // Failed
+      echo "Fail :(\n\n";
+      echo "Errors:\n";
+      foreach($encoding_job->errors as $error) {
+        echo $error."\n";
+      }
+    }
+
+    echo "\nAll Job Attributes:\n";
+    var_dump($encoding_job);
+
+    ?>
+    
+ +### Step 3 +Modify the above script to meet your needs. +Your [API Request History](https://app.zencoder.com/api_requests) may come in handy. +You can revisit your [API builder](https://app.zencoder.com/api_builder) to add/update parameters of the JSON. + +You can translate the JSON string into nested associative arrays so that you can dynamically change attributes like "input". +The previous JSON example would become: + + $encoding_job = new ZencoderJob(array( + "api_key" => "93h630j1dsyshjef620qlkavnmzui3", + "input" => "s3://bucket-name/file-name.avi", + "outputs" => array( + array( + "label" => "web" + ) + ) + )); + + +GENERAL API REQUESTS +-------------------- +A general API request can be used for all API functionality including **Job Listing**, **Job Details**, **Account Creation**, **Account Details** (even Job Creation if desired). See the [API docs](http://zencoder.com/docs/api/) for all possible API requests. +The first argument is the **API URL**. +The second argument is your **API Key**. +The third argument is the **request parameters** if needed. It can either be a JSON string or an array of parameters. + + +#### Example Job List Request + + $request = new ZencoderRequest( + 'https://app.zencoder/api/jobs', + '93h630j1dsyshjef620qlkavnmzui3' + ); + + if ($request->successful) { + print_r($request->results); + } else { + foreach($request->errors as $error) { + echo $error."\n"; + } + } + +#### Example Account Creation Request + + $request = new ZencoderRequest( + 'https://app.zencoder/api/account', + false, // API key isn't needed for new account creation + array( + "terms_of_service" => "1", + "email" => "test@example.com", + "password" => "1234" + ) + ); + + if ($request->successful) { + print_r($request->results); + } else { + foreach($request->errors as $error) { + echo $error."\n"; + } + } + + +NOTIFICATION HANDLING +---------------------- +The ZencoderOutputNotification class is used to capture and parse JSON data sent from +Zencoder to your app when an output file has been completed. + + + +### Step 1 +Create a script to receive notifications, and upload it to a location on your server that is publicly accessible. + +#### Example + output->state == "finished") { + echo "w00t!\n"; + + // If you're encoding to multiple outputs and only care when all of the outputs are finished + // you can check if the entire job is finished. + if($notification->job->state == "finished") { + echo "Dubble w00t!"; + } + } elseif ($notification->output->state == "cancelled") { + echo "Cancelled!\n"; + } else { + echo "Fail!\n"; + echo $notification->output->error_message."\n"; + echo $notification->output->error_link; + } + + ?> + +### Step 2 +In the parameters for an encoding job, add the URL for your script to the notifications array of each output you want to be notified for. +Then submit the job to test if it works. + +**You can view the results at:** + + +#### Example + ... + "outputs" => array( + array( + "label" => "web", + "notifications" => array("http://example.com.com/encoding/notification.php") + ), + array( + "label" => "iPhone", + "notifications" => array("http://example.com.com/encoding/notification.php") + ) + ) + ... + + +### Step 3 +Modify the above script to meet your needs. +Your [notifications page](https://app.zencoder.com/notifications) will come in handy. + +VERSIONS +--------- + Version 1.1 - 2010-06-04 Added General API Requests + Version 1.0 - 2010-04-02 Jobs and Notifications. \ No newline at end of file diff --git a/plugins/video_zencoder/includes/Zencoder.php b/plugins/video_zencoder/includes/Zencoder.php new file mode 100644 index 0000000..df52526 --- /dev/null +++ b/plugins/video_zencoder/includes/Zencoder.php @@ -0,0 +1,239 @@ +encode($value); } + function json_decode($value) { return $GLOBALS['JSON_OBJECT']->decode($value); } +} + +class ZencoderJob { + + var $new_job_url = "https://app.zencoder.com/api/jobs"; //https://app.zencoder.com/api/jobs + //https://zencoder-staging.heroku.com/api/jobs + var $new_job_params = array(); + var $new_job_json; + var $created = false; + var $errors = array(); + + // Attributes + var $id; + var $outputs = array(); + + // Initialize + function ZencoderJob($params, $options = array()) { + + // Build using params if not sending request + if($options["build"]) { + $this->update_attributes($params); + return true; + } + + $this->new_job_params = $params; + $this->created = $this->create(); + } + + // Send Job Request to API + function create() { + // Send request + $request = new ZencoderRequest($this->new_job_url, false, $this->new_job_params); + + if($request->successful) { + $this->update_attributes($request->results); + return true; + } else { + $this->errors = array_merge($this->errors, $request->errors); + return false; + } + } + + // Add/Update attributes on the job object. + function update_attributes($attributes = array()) { + foreach($attributes as $attr_name => $attr_value) { + // Create output file objects + if($attr_name == "outputs" && is_array($attr_value)) { + $this->create_outputs($attr_value); + } elseif (!function_exists($this->$attr_name)) { + $this->$attr_name = $attr_value; + } + } + } + + // Create output file objects from returned parameters. + // Use the Label for the key if avaiable. + function create_outputs($outputs = array()) { + foreach($outputs as $output_attrs) { + if($output_attrs["label"]) { + $this->outputs[$output_attrs["label"]] = new ZencoderOutputFile($output_attrs); + } else { + $this->outputs[] = new ZencoderOutputFile($output_attrs); + } + } + } +} + +class ZencoderOutputFile { + + var $id; + var $label; + var $url; + var $state; + var $error_message; + var $error_link; + + function ZencoderOutputFile($attributes = array()) { + $this->update_attributes($attributes); + } + + // Add/Update attributes on the file object. + function update_attributes($attributes = array()) { + foreach($attributes as $attr_name => $attr_value) { + if(!function_exists($this->$attr_name)) { + $this->$attr_name = $attr_value; + } + } + } +} + +// General API request class +class ZencoderRequest { + + var $successful = false; + var $errors = array(); + var $raw_results; + var $results; + + function ZencoderRequest($url, $api_key = "", $params = "") { + + // Add api_key to url if supplied + if($api_key) { + $url .= "?api_key=".$api_key; + } + + // Get JSON + if(is_string($params)) { + $json = trim($params); + } else if(is_array($params)) { + $json = json_encode($params); + } else { + $json = false; + } + + // Create request + $request = new ZencoderCURL($url, $json); + + // Check for connection errors + if ($request->connected == false) { + $this->errors[] = $request->error; + return; + } + + $status_code = intval($request->status_code); + $this->raw_results = $request->results; + + // Parse returned JSON + $this->results = json_decode($this->raw_results, true); + + // Return based on HTTP status code + if($status_code >= 200 && $status_code <= 206) { + $this->successful = true; + } else { + // Get job request errors if any + if(is_array($this->results["errors"])) { + foreach($this->results["errors"] as $error) { + $this->errors[] = $error; + } + } else { + $this->errors[] = "Unknown Error\n\nHTTP Status Code: ".$request->status_code."\n"."Raw Results: \n".$request->raw_results; + } + } + } +} + +// ZencoderCURL +// The connection class to perform the actual request to the surver +// using cURL http://php.net/manual/en/book.curl.php +class ZencoderCURL { + + var $options = array( + CURLOPT_RETURNTRANSFER => 1, // Return content of the url + CURLOPT_HEADER => 0, // Don't return the header in result + CURLOPT_HTTPHEADER => array("Content-Type: application/json", "Accept: application/json"), + CURLOPT_CONNECTTIMEOUT => 0, // Time in seconds to timeout send request. 0 is no timeout. + CURLOPT_FOLLOWLOCATION => 1, // Follow redirects. + ); + + var $connected; + var $results; + var $status_code; + var $error; + + // Initialize + function ZencoderCURL($url, $json, $options = array()) { + + // Add library details to request + $this->options[CURLOPT_HTTPHEADER][] = "Zencoder-Library-Name: ".ZENCODER_LIBRARY_NAME; + $this->options[CURLOPT_HTTPHEADER][] = "Zencoder-Library-Version: ".ZENCODER_LIBRARY_VERSION; + + // If posting data + if($json) { + $this->options[CURLOPT_POST] = 1; + $this->options[CURLOPT_POSTFIELDS] = $json; + } + + // Add cURL options to defaults (can't use array_merge) + foreach($options as $option_key => $option) { + $this->options[$option_key] = $option; + } + + // Initialize session + $ch = curl_init($url); + + // Set transfer options + curl_setopt_array($ch, $this->options); + + // Execute session and store returned results + $this->results = curl_exec($ch); + + // Store the HTTP status code given (201, 404, etc.) + $this->status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + // Check for cURL error + if (curl_errno($ch)) { + $this->error = 'cURL connection error ('.curl_errno($ch).'): '.htmlspecialchars(curl_error($ch)).' Search'; + $this->connected = false; + } else { + $this->connected = true; + } + + // Close session + curl_close($ch); + } +} + +// Capture incoming notifications from Zencoder to your app +class ZencoderOutputNotification { + + var $output; + var $job; + + function ZencoderOutputNotification($params) { + if($params["output"]) $this->output = new ZencoderOutputFile($params["output"]); + if($params["job"]) $this->job = new ZencoderJob($params["job"], array("build" => true)); + } + + function catch_and_parse() { + $notificiation_data = json_decode(trim(file_get_contents('php://input')), true); + return new ZencoderOutputNotification($notificiation_data); + } +} diff --git a/plugins/video_zencoder/includes/lib/JSON.php b/plugins/video_zencoder/includes/lib/JSON.php new file mode 100644 index 0000000..229799b --- /dev/null +++ b/plugins/video_zencoder/includes/lib/JSON.php @@ -0,0 +1,806 @@ + + * @author Matt Knapp + * @author Brett Stimmerman + * @copyright 2005 Michal Migurski + * @version CVS: $Id$ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_SLICE', 1); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_STR', 2); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_ARR', 3); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_OBJ', 4); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_CMT', 5); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_LOOSE_TYPE', 16); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_SUPPRESS_ERRORS', 32); + +/** + * Converts to and from JSON format. + * + * Brief example of use: + * + * + * // create a new instance of Services_JSON + * $json = new Services_JSON(); + * + * // convert a complexe value to JSON notation, and send it to the browser + * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); + * $output = $json->encode($value); + * + * print($output); + * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] + * + * // accept incoming POST data, assumed to be in JSON notation + * $input = file_get_contents('php://input', 1000000); + * $value = $json->decode($input); + * + */ +class Services_JSON +{ + /** + * constructs a new JSON instance + * + * @param int $use object behavior flags; combine with boolean-OR + * + * possible values: + * - SERVICES_JSON_LOOSE_TYPE: loose typing. + * "{...}" syntax creates associative arrays + * instead of objects in decode(). + * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. + * Values which can't be encoded (e.g. resources) + * appear as NULL instead of throwing errors. + * By default, a deeply-nested resource will + * bubble up with an error, so all return values + * from encode() should be checked with isError() + */ + function Services_JSON($use = 0) + { + $this->use = $use; + } + + /** + * convert a string from one UTF-16 char to one UTF-8 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf16 UTF-16 character + * @return string UTF-8 character + * @access private + */ + function utf162utf8($utf16) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); + } + + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); + + switch(true) { + case ((0x7F & $bytes) == $bytes): + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x7F & $bytes); + + case (0x07FF & $bytes) == $bytes: + // return a 2-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xC0 | (($bytes >> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + // return a 3-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encode($var) + { + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $properties = array_map(array($this, 'name_value'), + array_keys($var), + array_values($var)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + // treat it like a regular array + $elements = array_map(array($this, 'encode'), $var); + + foreach($elements as $element) { + if(Services_JSON::isError($element)) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = get_object_vars($var); + + $properties = array_map(array($this, 'name_value'), + array_keys($vars), + array_values($vars)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function name_value($name, $value) + { + $encoded_value = $this->encode($value); + + if(Services_JSON::isError($encoded_value)) { + return $encoded_value; + } + + return $this->encode(strval($name)) . ':' . $encoded_value; + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + function reduce_string($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to Services_JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + function decode($str) + { + $str = $this->reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = substr($str, 0, 1); + $chrs = substr($str, 1, -1); + $utf8 = ''; + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = substr($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) + . chr(hexdec(substr($chrs, ($c + 4), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array('what' => SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = substr($str, 1, -1); + $chrs = $this->reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = substr($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = substr($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, $this->decode($slice)); + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // "name":value pair + $key = $this->decode($parts[1]); + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == SERVICES_JSON_IN_STR) && + ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { + // found a quote, we're in a string, and it's not escaped + // we know that it's not escaped becase there is _not_ an + // odd number of backslashes at the end of the string so far + array_pop($stk); + //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * @todo Ultimately, this should just call PEAR::isError() + */ + function isError($data, $code = null) + { + if (class_exists('pear')) { + return PEAR::isError($data, $code); + } elseif (is_object($data) && (get_class($data) == 'services_json_error' || + is_subclass_of($data, 'services_json_error'))) { + return true; + } + + return false; + } +} + +if (class_exists('PEAR_Error')) { + + class Services_JSON_Error extends PEAR_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + parent::PEAR_Error($message, $code, $mode, $options, $userinfo); + } + } + +} else { + + /** + * @todo Ultimately, this class shall be descended from PEAR_Error + */ + class Services_JSON_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + + } + } + +} + +?> \ No newline at end of file diff --git a/plugins/video_zencoder/includes/zencoder.inc b/plugins/video_zencoder/includes/zencoder.inc new file mode 100644 index 0000000..1269505 --- /dev/null +++ b/plugins/video_zencoder/includes/zencoder.inc @@ -0,0 +1,280 @@ +access_key = variable_get('zencoder_api_key', ''); + $this->limit = variable_get('amazon_s3_limit',5); + $this->bucket = variable_get('amazon_s3_bucket', ''); + } + + + /** + * create transcoding job on Zencoder.com + */ + public function create($file) { + // API Key + $api_key = variable_get('zencoder_api_key', ''); + // File details + $filename = $file->filename; + // Get varialbes + $lable = 'VIDEO_' . $file->fid; + $base_url = $folder; + $width = variable_get('zc_width', ''); + $height = variable_get('zc_height', ''); + $quality = variable_get('zc_quality', 3); + $speed = variable_get('zc_speed', 3); + $upscale = variable_get('zc_upscale', ''); + $stretch = variable_get('zc_stretch', ''); + $frame_rate = variable_get('zc_frame_rate', ''); + $max_frame_rate = variable_get('zc_max_frame_rate', ''); + $keyframe_interval = variable_get('zc_key_frame_interval', ''); + $video_bitrate = variable_get('zc_vid_bit_rate', ''); + $bitrate_cap = variable_get('zc_bit_rate_cap', ''); + $buffer_size = variable_get('zc_buffer_size', ''); + $h264_profile = variable_get('zc_h245_profile', 1); + $h264_level = variable_get('zc_h245_level', 0); + $skip_video = variable_get('zc_skip_video', ''); + $audio_codec = variable_get('zc_audio_codec', 'aac'); + $audio_bitrate = variable_get('zc_audio_bitrate', ''); + $audio_channels = variable_get('zc_audio_channels', 2); + $audio_sample_rate = variable_get('zc_audio_sample_rate', ''); + $skip_audio = variable_get('zc_skip_audio', ''); + $thumb_no = variable_get('no_of_video_thumbs', 5); + // TODO : + $thumb_size = '160x120'; + $thumb_base = $baseurl; + $thumb_prefix = $filename; + $notify_url = variable_get('zc_notify_url', ''); + $notify_email = variable_get('zc_notify_email', 'heshanmw@gmail.com'); + $start_clip = variable_get('zc_start_clip', ''); + $clip_length = variable_get('zc_clip_length', ''); + + $bucket = $this->bucket; + + // Job details + $input_name = $bucket . '/' . $filename; +// watchdog('zencoder', $input_name); + + $zc_output = array(); + if(!empty($lable)) + $zc_output['label'] = $lable; + if(!empty($bucket)) + $zc_output['url'] = 's3://' . $bucket . '/' . $filename . '.flv'; + if(!empty($width)) + $zc_output['width'] = $width; + if(!empty($height)) + $zc_output['height'] = $height; + if($quality != 3) + $zc_output['quality'] = $quality; + if($speed != 3) + $zc_output['speed'] = $speed; + if(!empty($upscale)) + $zc_output['upscale'] = $upscale; + if(!empty($frame_rate)) + $zc_output['frame_rate'] = $frame_rate; + if(!empty($max_frame_rate)) + $zc_output['max_frame_rate'] = $max_frame_rate; + if(!empty($keyframe_interval)) + $zc_output['keyframe_interval'] = $keyframe_interval; + if(!empty($video_bitrate)) + $zc_output['video_bitrate'] = $video_bitrate; + if(!empty($bitrate_cap)) + $zc_output['bitrate_cap'] = $bitrate_cap; + if(!empty($buffer_size)) + $zc_output['buffer_size'] = $buffer_size; + if(($h264_profile != 1)) + $zc_output['h264_profile'] = $h264_profile; + if(($h264_level != 0)) + $zc_output['h264_level'] = $h264_level; + if(!empty($skip_video)) + $zc_output['skip_video'] = $skip_video; + if(($audio_codec != 'aac')) + $zc_output['audio_codec'] = $audio_codec; + if(!empty($audio_bitrate)) + $zc_output['audio_bitrate'] = $audio_bitrate; + if(($audio_channels != 2)) + $zc_output['audio_channels'] = $audio_channels; + if(!empty($audio_sample_rate)) + $zc_output['audio_sample_rate'] = $audio_sample_rate; + if(!empty($skip_audio)) + $zc_output['skip_audio'] = $skip_audio; + if(!empty($start_clip)) + $zc_output['start_clip'] = $start_clip; + if(!empty($clip_length)) + $zc_output['clip_length'] = $clip_length; + +// thumbnails + $thumbnails['number'] = $thumb_no; + if(!empty($thumb_size)) + $thumbnails['thumb_size'] = $thumb_size; + if(!empty($bucket)) + $thumbnails['base_url'] = 's3://' . $bucket; + if(!empty($lable)) + $thumbnails['prefix'] = $lable; + $zc_output['thumbnails'] = $thumbnails; + + // Notifications + if(!empty($notify_url)) + $notifications[] = $notify_url; + if(!empty($notify_email)) + $notifications[] = $notify_email; + $zc_output['notifications'] = $notifications; + + + $encoding_job_json = array( + 'test' => 1, +// 'download_connections' => -1, + 'api_key' => $this->access_key, + 'input' => 's3://' . $input_name . '', + 'output' => $zc_output + ); + + + watchdog('zencoder', json_encode($encoding_job_json)); + + $encoding_job = new ZencoderJob(json_encode($encoding_job_json)); + + // Check if it worked + if ($encoding_job->created) { +// watchdog('zencoder', $encoding_job->id); + return $encoding_job; + } + else { + foreach($encoding_job->errors as $error) { + // echo $error."\n"; + watchdog('zencoder', 'Zencoder reports some errors. !error', array('!error' => $error), WATCHDOG_ERROR); + } + return false; + } + + } + + /* + * Inserts file object into the database. + */ + public function insert($fid, $jobid = 0) { + db_query("INSERT INTO {video_zencoder} (fid, jobid, status) VALUES (%d, %d, %d)", $fid, $jobid, VIDEO_ZC_PENDING); + } + + /* + * Updates the database after a successful transfer to amazon. + */ + public function update($video) { + $result = db_query("UPDATE {video_zencoder} SET jobid = %d, outputid = %d, bucket='%s', filename='%s', filepath='%s', filemime='%s', filesize='%s', status=%d, completed=%d WHERE vid=%d", + $video->jobid, $video->outputid, $video->bucket, $video->filename, $video->filepath, $video->filemime, $video->filesize, VIDEO_ZC_ACTIVE, time(), $video->vid); + return $result; + } + + public function working($vid) { + db_query("UPDATE {video_zencoder} SET status=%d WHERE vid=%d", VIDEO_ZC_WORKING, $vid); + } + + public function failed($vid) { + db_query("UPDATE {video_zencoder} SET status=%d WHERE vid=%d", VIDEO_ZC_FAILED, $vid); + } + /* + * Verifies the existence of a file id, returns the row or false if none found. + */ + public function verify($fid) { + $sql = db_query("SELECT * FROM {video_zencoder} WHERE fid=%d", $fid); + $row = db_fetch_object($sql); + return $row; + } + + public function delete($fid) { + // Lets get our file no matter the status and delete it. + if($video = $this->verify($fid)) { + // Lets delete our record from the database. + db_query("DELETE FROM {video_zencoder} WHERE vid=%d", $video->vid); + } + } + public function get_job_id() { + return $this->jobid; + } + /* + * Gets a video object from the database. + */ + public function get($fid) { + $sql = db_query("SELECT * FROM {video_zencoder} WHERE fid=%d AND status=%d", $fid, VIDEO_ZC_ACTIVE); + $row = db_fetch_object($sql); + return $row; + } + + /* + * Selects the pending queue to be transfered to amazon. + */ + public function queue() { + $video = false; + $sql = db_query("SELECT vid, fid FROM {video_zencoder} WHERE status=%d LIMIT %d", VIDEO_ZC_PENDING, $this->limit); + while($row = db_fetch_object($sql)) { + // We need to check if this file id exists in our S3 table to avoid filenot found error. + $sql_video = db_query("SELECT * FROM {video_s3} WHERE fid=%d", $row->fid); + if($sql_video_row = db_fetch_object($sql_video)) { + // This is a s3 file, lets verify it has been pushed and if so lets push Zencoder queue. + module_load_include('inc', 'video', '/includes/conversion'); + if($sql_video_row->status == VIDEO_ZC_ACTIVE) { + $video = $sql_video_row; + } + } + else { + // This is a regular video file, lets get our file object from the files table and push it to amazon. + $sql_files = db_query("SELECT * FROM {files} WHERE fid=%d", $row->fid); + if($sql_files_row = db_fetch_object($sql_files)) { + $video = $sql_files_row; + } + } + // If we have a video lets go ahead and send it. + if($video) { + // Get Output file info + // API Key + $api_key = variable_get('zencoder_api_key', ''); + + // Update our status to working. + $this->working($row->vid); + $filepath = $video->filepath; + $filename = basename($video->filepath); + if ($encoding_job = $this->create($video)) { + // Update our table. + $video->bucket = $this->bucket; + $video->filename = $filename . '.flv'; + $video->filemime = 'video/x-flv'; + $video->vid = $row->vid; + $video->jobid = $encoding_job->id; + $video->outputid = $encoding_job->outputs["VIDEO" . $video->fid]->id; + $prefix = $this->ssl ? 'https://' : 'http://'; + $video->filepath = $prefix . $video->bucket .'.s3.amazonaws.com/'. $filename . '.flv'; + if($this->update($video)) { + watchdog('zencoder', t('Successfully created trancoding job on file: !file into the bucket %bucket on Zencoder.', array('!file' => $filepath, '%bucket' => $this->bucket)), array(), WATCHDOG_INFO); + } + } + else { + watchdog('zencoder', 'Failed to queus our file to Zencoder.', array(), WATCHDOG_ERROR); + $this->failed($row->vid); + } + } + else { + watchdog('zencoder', 'We did not find the file id: '.$row->fid.' or it is still queued for ffmpeg processing or S3 push.', array(), WATCHDOG_DEBUG); + } + } + } +} \ No newline at end of file -- cgit v1.2.3