if (!defined('STATUSNET')) {
exit(1);
}
-require_once INSTALLDIR.'/extlib/Services/oEmbed.php';
/**
- * Utility class to wrap Services_oEmbed:
+ * Utility class to wrap basic oEmbed lookups.
*
* Blacklisted hosts will use an alternate lookup method:
* - Twitpic
protected static $apiMap = array(
'flickr.com' => 'http://www.flickr.com/services/oembed/',
'yfrog.com' => 'http://www.yfrog.com/api/oembed',
+ 'youtube.com' => 'http://www.youtube.com/oembed',
+ 'viddler.com' => 'http://lab.viddler.com/services/oembed/',
+ 'qik.com' => 'http://qik.com/api/oembed.json',
+ 'revision3.com' => 'http://revision3.com/api/oembed/',
+ 'hulu.com' => 'http://www.hulu.com/api/oembed.json',
+ 'vimeo.com' => 'http://www.vimeo.com/api/oembed.json',
+ 'my.opera.com' => 'http://my.opera.com/service/oembed',
);
protected static $functionMap = array(
'twitpic.com' => 'oEmbedHelper::twitPic',
* Some known hosts are whitelisted with API endpoints where we
* know they exist but autodiscovery data isn't available.
* If autodiscovery links are missing and we don't recognize the
- * host, we'll pass it to oohembed.com's public service which
+ * host, we'll pass it to noembed.com's public service which
* will either proxy or fake info on a lot of sites.
*
* A few hosts are blacklisted due to known problems with oohembed,
*/
public static function getObject($url, $params=array())
{
- common_log(LOG_INFO, 'QQQ: wtf? ' . $url);
$host = parse_url($url, PHP_URL_HOST);
if (substr($host, 0, 4) == 'www.') {
$host = substr($host, 4);
}
- // Blacklist: systems with no oEmbed API of their own, which are
- // either missing from or broken on oohembed.com's proxy.
- // we know how to look data up in another way...
- if (array_key_exists($host, self::$functionMap)) {
- $func = self::$functionMap[$host];
- return call_user_func($func, $url, $params);
+ common_log(LOG_INFO, 'Checking for oEmbed data for ' . $url);
+
+ // You can fiddle with the order of discovery -- either skipping
+ // some types or re-ordering them.
+
+ $order = common_config('oembed', 'order');
+
+ foreach ($order as $method) {
+
+ switch ($method) {
+ case 'built-in':
+ common_log(LOG_INFO, 'Considering built-in oEmbed methods...');
+ // Blacklist: systems with no oEmbed API of their own, which are
+ // either missing from or broken on noembed.com's proxy.
+ // we know how to look data up in another way...
+ if (array_key_exists($host, self::$functionMap)) {
+ common_log(LOG_INFO, 'We have a built-in method for ' . $host);
+ $func = self::$functionMap[$host];
+ return call_user_func($func, $url, $params);
+ }
+ break;
+ case 'well-known':
+ common_log(LOG_INFO, 'Considering well-known oEmbed endpoints...');
+ // Whitelist: known API endpoints for sites that don't provide discovery...
+ if (array_key_exists($host, self::$apiMap)) {
+ $api = self::$apiMap[$host];
+ common_log(LOG_INFO, 'Using well-known endpoint "' . $api . '" for "' . $host . '"');
+ break 2;
+ }
+ break;
+ case 'discovery':
+ try {
+ common_log(LOG_INFO, 'Trying to discover an oEmbed endpoint using link headers.');
+ $api = self::discover($url);
+ common_log(LOG_INFO, 'Found API endpoint ' . $api . ' for URL ' . $url);
+ break 2;
+ } catch (Exception $e) {
+ common_log(LOG_INFO, 'Could not find an oEmbed endpoint using link headers.');
+ // Just ignore it!
+ }
+ break;
+ case 'service':
+ $api = common_config('oembed', 'endpoint');
+ common_log(LOG_INFO, 'Using service API endpoint ' . $api);
+ break 2;
+ break;
+ }
}
- // Whitelist: known API endpoints for sites that don't provide discovery...
- if (array_key_exists($host, self::$apiMap)) {
- $api = self::$apiMap[$host];
- common_log(LOG_INFO, 'QQQ: going to: ' . $api);
- } else {
- $api = false;
- common_log(LOG_INFO, 'QQQ: no map for ' . $host);
+ if (empty($api)) {
+ // TRANS: Server exception thrown in oEmbed action if no API endpoint is available.
+ throw new ServerException(_('No oEmbed API endpoint available.'));
}
+
return self::getObjectFrom($api, $url, $params);
}
/**
- * Actually do an oEmbed lookup to a particular API endpoint,
- * or to the autodiscovered target, or to oohembed.
+ * Perform basic discovery.
+ * @return string
+ */
+ static function discover($url)
+ {
+ // @fixme ideally skip this for non-HTML stuff!
+ $body = self::http($url);
+ return self::discoverFromHTML($url, $body);
+ }
+
+ /**
+ * Partially ripped from OStatus' FeedDiscovery class.
*
- * @param mixed $api string or false: oEmbed API endpoint URL
+ * @param string $url source URL, used to resolve relative links
+ * @param string $body HTML body text
+ * @return mixed string with URL or false if no target found
+ */
+ static function discoverFromHTML($url, $body)
+ {
+ // DOMDocument::loadHTML may throw warnings on unrecognized elements,
+ // and notices on unrecognized namespaces.
+ $old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE));
+ $dom = new DOMDocument();
+ $ok = $dom->loadHTML($body);
+ error_reporting($old);
+
+ if (!$ok) {
+ throw new oEmbedHelper_BadHtmlException();
+ }
+
+ // Ok... now on to the links!
+ $feeds = array(
+ 'application/json+oembed' => false,
+ );
+
+ $nodes = $dom->getElementsByTagName('link');
+ for ($i = 0; $i < $nodes->length; $i++) {
+ $node = $nodes->item($i);
+ if ($node->hasAttributes()) {
+ $rel = $node->attributes->getNamedItem('rel');
+ $type = $node->attributes->getNamedItem('type');
+ $href = $node->attributes->getNamedItem('href');
+ if ($rel && $type && $href) {
+ $rel = array_filter(explode(" ", $rel->value));
+ $type = trim($type->value);
+ $href = trim($href->value);
+
+ if (in_array('alternate', $rel) && array_key_exists($type, $feeds) && empty($feeds[$type])) {
+ // Save the first feed found of each type...
+ $feeds[$type] = $href;
+ }
+ }
+ }
+ }
+
+ // Return the highest-priority feed found
+ foreach ($feeds as $type => $url) {
+ if ($url) {
+ return $url;
+ }
+ }
+
+ throw new oEmbedHelper_DiscoveryException();
+ }
+
+ /**
+ * Actually do an oEmbed lookup to a particular API endpoint.
+ *
+ * @param string $api oEmbed API endpoint URL
* @param string $url target URL to look up info about
* @param array $params
* @return object
*/
- static protected function getObjectFrom($api, $url, $params=array())
+ static function getObjectFrom($api, $url, $params=array())
{
- $options = array();
- if ($api) {
- $options[Services_oEmbed::OPTION_API] = $api;
+ $params['url'] = $url;
+ $params['format'] = 'json';
+ $key=common_config('oembed','apikey');
+ if(isset($key)) {
+ $params['key'] = common_config('oembed','apikey');
}
+ $data = self::json($api, $params);
+ return self::normalize($data);
+ }
- try {
- $oEmbed = new Services_oEmbed_Tweaked($url, $options);
- } catch (Services_oEmbed_Exception_NoSupport $e) {
- // Discovery failed... fall back to oohembed if enabled.
- $oohembed = common_config('oohembed', 'endpoint');
- if ($oohembed) {
- $options[Services_oEmbed::OPTION_API] = $oohembed;
- $oEmbed = new Services_oEmbed_Tweaked($url, $options);
- } else {
- throw $e;
+ /**
+ * Normalize oEmbed format.
+ *
+ * @param object $orig
+ * @return object
+ */
+ static function normalize($orig)
+ {
+ $data = clone($orig);
+
+ if (empty($data->type)) {
+ throw new Exception('Invalid oEmbed data: no type field.');
+ }
+
+ if ($data->type == 'image') {
+ // YFrog does this.
+ $data->type = 'photo';
+ }
+
+ if (isset($data->thumbnail_url)) {
+ if (!isset($data->thumbnail_width)) {
+ // !?!?!
+ $data->thumbnail_width = common_config('attachments', 'thumb_width');
+ $data->thumbnail_height = common_config('attachments', 'thumb_height');
}
}
- // And.... let's go look it up!
- return $oEmbed->getObject($params);
+ return $data;
}
/**
}
}
-class Services_oEmbed_Tweaked extends Services_oEmbed
+class oEmbedHelper_Exception extends Exception
{
- protected function discover($url)
+ public function __construct($message = "", $code = 0, $previous = null)
{
- $api = parent::discover($url);
- if (strpos($api, '?') !== false) {
- // Services_oEmbed doesn't expect to find existing params
- // on its API endpoint, which may surprise you since the
- // spec says discovery URLs should include parameters... :)
- //
- // Appending a '&' on the end keeps the later-appended '?'
- // from breaking whatever the first parameters was.
- return $api . '&';
- }
- return $api;
+ parent::__construct($message, $code);
}
+}
- public function getObject(array $params = array())
+class oEmbedHelper_BadHtmlException extends oEmbedHelper_Exception
+{
+ function __construct($previous=null)
{
- $api = $this->options[self::OPTION_API];
- if (strpos($api, '?') !== false) {
- // The Services_oEmbed code appends a '?' on the end, which breaks
- // the next parameter which may be something important like
- // maxwidth.
- //
- // Sticking this bogus entry into our parameters gets us past it.
- $params = array_merge(array('statusnet' => 1), $params);
- }
- return parent::getObject($params);
+ return parent::__construct('Bad HTML in discovery data.', 0, $previous);
}
+}
-}
\ No newline at end of file
+class oEmbedHelper_DiscoveryException extends oEmbedHelper_Exception
+{
+ function __construct($previous=null)
+ {
+ return parent::__construct('No oEmbed discovery data.', 0, $previous);
+ }
+}