Swap the Services_oEmbed wrapper in oEmbedHelper out for doing it ourselves...
- workaround for providers that are skimpy on their data, such as missing width/height or thumbnail_width/thumbnail_height - workaround for YFrog listing "image" instead of "photo" type - generally more lax about formatting: if it comes back and looks kinda ok, we'll take it. - discovery uses system HTML parser, should be more robust if the links include things like ampersands with proper HTML-level escaping
This commit is contained in:
		@@ -20,11 +20,10 @@
 | 
				
			|||||||
if (!defined('STATUSNET')) {
 | 
					if (!defined('STATUSNET')) {
 | 
				
			||||||
    exit(1);
 | 
					    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:
 | 
					 * Blacklisted hosts will use an alternate lookup method:
 | 
				
			||||||
 *  - Twitpic
 | 
					 *  - Twitpic
 | 
				
			||||||
@@ -89,43 +88,134 @@ class oEmbedHelper
 | 
				
			|||||||
            $api = self::$apiMap[$host];
 | 
					            $api = self::$apiMap[$host];
 | 
				
			||||||
            common_log(LOG_INFO, 'QQQ: going to: ' . $api);
 | 
					            common_log(LOG_INFO, 'QQQ: going to: ' . $api);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            $api = false;
 | 
					 | 
				
			||||||
            common_log(LOG_INFO, 'QQQ: no map for ' . $host);
 | 
					            common_log(LOG_INFO, 'QQQ: no map for ' . $host);
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                $api = self::discover($url);
 | 
				
			||||||
 | 
					            } catch (Exception $e) {
 | 
				
			||||||
 | 
					                // Discovery failed... fall back to oohembed if enabled.
 | 
				
			||||||
 | 
					                $oohembed = common_config('oohembed', 'endpoint');
 | 
				
			||||||
 | 
					                if ($oohembed) {
 | 
				
			||||||
 | 
					                    $api = $oohembed;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    throw $e;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            common_log(LOG_INFO, 'QQQ: going to: ' . $api);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return self::getObjectFrom($api, $url, $params);
 | 
					        return self::getObjectFrom($api, $url, $params);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Actually do an oEmbed lookup to a particular API endpoint,
 | 
					     * Perform basic discovery.
 | 
				
			||||||
     * or to the autodiscovered target, or to oohembed.
 | 
					     * @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;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					        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 string $url target URL to look up info about
 | 
				
			||||||
     * @param array $params
 | 
					     * @param array $params
 | 
				
			||||||
     * @return object
 | 
					     * @return object
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    static protected function getObjectFrom($api, $url, $params=array())
 | 
					    static function getObjectFrom($api, $url, $params=array())
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $options = array();
 | 
					        $params['url'] = $url;
 | 
				
			||||||
        if ($api) {
 | 
					        $params['format'] = 'json';
 | 
				
			||||||
            $options[Services_oEmbed::OPTION_API] = $api;
 | 
					        $data = self::json($api, $params);
 | 
				
			||||||
 | 
					        return self::normalize($data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					    /**
 | 
				
			||||||
            $oEmbed = new Services_oEmbed_Tweaked($url, $options);
 | 
					     * Normalize oEmbed format.
 | 
				
			||||||
        } catch (Services_oEmbed_Exception_NoSupport $e) {
 | 
					     *
 | 
				
			||||||
            // Discovery failed... fall back to oohembed if enabled.
 | 
					     * @param object $orig
 | 
				
			||||||
            $oohembed = common_config('oohembed', 'endpoint');
 | 
					     * @return object
 | 
				
			||||||
            if ($oohembed) {
 | 
					     */
 | 
				
			||||||
                $options[Services_oEmbed::OPTION_API] = $oohembed;
 | 
					    static function normalize($orig)
 | 
				
			||||||
                $oEmbed = new Services_oEmbed_Tweaked($url, $options);
 | 
					    {
 | 
				
			||||||
            } else {
 | 
					        $data = clone($orig);
 | 
				
			||||||
                throw $e;
 | 
					
 | 
				
			||||||
 | 
					        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 $data;
 | 
				
			||||||
        return $oEmbed->getObject($params);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -212,35 +302,22 @@ class oEmbedHelper
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Services_oEmbed_Tweaked extends Services_oEmbed
 | 
					class oEmbedHelper_Exception extends Exception
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected function discover($url)
 | 
					}
 | 
				
			||||||
    {
 | 
					
 | 
				
			||||||
        $api = parent::discover($url);
 | 
					class oEmbedHelper_BadHtmlException extends oEmbedHelper_Exception
 | 
				
			||||||
        if (strpos($api, '?') !== false) {
 | 
					{
 | 
				
			||||||
            // Services_oEmbed doesn't expect to find existing params
 | 
					    function __construct($previous=null)
 | 
				
			||||||
            // on its API endpoint, which may surprise you since the
 | 
					    {
 | 
				
			||||||
            // spec says discovery URLs should include parameters... :)
 | 
					        return parent::__construct('Bad HTML in discovery data.', 0, $previous);
 | 
				
			||||||
            //
 | 
					    }
 | 
				
			||||||
            // Appending a '&' on the end keeps the later-appended '?'
 | 
					}
 | 
				
			||||||
            // from breaking whatever the first parameters was.
 | 
					
 | 
				
			||||||
            return $api . '&';
 | 
					class oEmbedHelper_DiscoveryException extends oEmbedHelper_Exception
 | 
				
			||||||
        }
 | 
					{
 | 
				
			||||||
        return $api;
 | 
					    function __construct($previous=null)
 | 
				
			||||||
    }
 | 
					    {
 | 
				
			||||||
 | 
					        return parent::__construct('No oEmbed discovery data.', 0, $previous);
 | 
				
			||||||
    public function getObject(array $params = array())
 | 
					    }
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $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);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user