Hello XGM!
Давно не писал я в своем бложике, но вот мне что-то захотелось.
Давно не писал я в своем бложике, но вот мне что-то захотелось.
Введение
Написали в тех. поддержку сайта с просьбой добавить интеграцию со SketchFab'ом для отображения моделей прямо на сайте.
Начал изучать, что и как можно было сделать. Так как форматирование txt2 основано на простоте и удобстве форматирования, то использование bb-кодов и html-вставок было не возможным. Классически все интеграции делаются по принципу - вижу ссылку, заменил.
Этап первый
Первым делом пошел по накатанной, начал смотреть как строится код для отображения проигрывателя моделей. Способ для отображения моделей из голой ссылки нашел, а вот для плейлистов - увы...
Почему так? А потому что ссылка модели содержит нужный идентификатор, который потом используется в отображении самой модели. А вот для плейлиста были случаи, когда указывался либо идентификатор, либо имя плейлиста. Из-за этого возникали проблемы, первое что пришло в голову - надо запрашивать этот самый айдишник через Data API, который я нашел на SketchFab'е.
Почему так? А потому что ссылка модели содержит нужный идентификатор, который потом используется в отображении самой модели. А вот для плейлиста были случаи, когда указывался либо идентификатор, либо имя плейлиста. Из-за этого возникали проблемы, первое что пришло в голову - надо запрашивать этот самый айдишник через Data API, который я нашел на SketchFab'е.
Этап второй
Думая дальше, анализируя примеры, нашел в документации сервиса для девелоперов такой вот интересный пункт - oEmbed. Открыл, смотрю:
- Сделайте запрос на наш сервис с параметром в виде ссылки
- Получите в ответ html код для вставки на сайте, а так же дополнительную информацию о предоставляемом контенте
Причем поддерживается не только отображение моделей, но и каталогов с моделями, а значит это то, что мне надо. Немного пошаманив с кодом парсера txt2, выделил ссылку на модель сайта SketchFab, отправил запрос ждя получение контента, используя cURL. И все стало выглядеть примерно так:
function txt2_sketchfab_link($url) {
	$targetUrl = 'https://sketchfab.com/oembed?url=' . $url;
	// Request a oEmbed content
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $targetUrl);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    $result = curl_exec($ch);
    // Verify response
    $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($status_code != 200) {
    	return null;
    }
	// Parse json response
    $response = json_decode($result);
    if ($response == null) {
        return null;
    }
	return $response['html];
}После этого все ссылки ведущие на SketchFab стали заменяться на плеер моделей. И смотрелось это так:
Этап третий
Немного еще погуглив на тему oEmbed протокола оказалось, что этот протокол был специально разработан для вставки медиа контента в виде ссылок. На официальном сайте данного протокола я нашел большой список поставщиков контента, таких как (sketchfab.com, devianart.com, soundcloud.com). И я подумал, раз все так просто, то почему бы и не прикрутить обработку других поставщиков медиа-контента. Для этого пришлось немного поработать над решением получения медиа контента, сделать его более универсальным. По спецификации все было достаточно просто:
- Провайдер имеет свой уникальный идентификатор
- Провайдер имеет точку вызова для получения данных (EndPoint)
- Провайдер имеет список правил для ссылок, которые он может обрабатывать
И поэтому для создания универсального решения требовалось реализовать небольшую библиотечку, в которой бы были реализованы следующие функции:
- Получение кода Embed компонента для указанный ссылки с использованием определенного провайдера (OEmbedProvider)
- Получение провайдера по ссылке (OEmbedProviderFactory)
Класс OEmbedProviderFactory будет заниматься созданием определенного провайдера медиа-контента, чтобы потом в итоге мы могли бы получить данные для конкретной ссылки. Информацию о разрешенных провайдерах будет храниться в специальном конфиге, где будет содержаться вся необходимая информация, указанная выше.
Ну и для этого конечно же написал немного кода, примерно такого
<?php
/**
 * @author Alexey Prey Mulyukin
 * Date: 08.09.2015
 * Time: 0:42
 */
class OEmbedData {
    public $html;
}
/**
 * Class OEmbedProviderFactory
 *
 * Provider configuration Example:
 * {
 *      'name': string,
 *      'endpoint': endpoint,
 *      'schemas': [
 *          url-schema,
 *          url-schema
 *      ]
 * }
 */
class OEmbedProviderFactory {
    private static $instance;
    public static function &GetInst() {
        return self::$instance;
    }
    private $schemaRegexStorage = array();
    private $providerStorage = array();
    private $providerConfiguration;
    public function __construct($providerConfiguration) {
        if (self::$instance != null) {
            throw new ErrorException("OEmbedProviderFactory already exists in this application context!");
        }
        $this->providerConfiguration = $providerConfiguration;
        self::$instance = &$this;
    }
    private function GetSchemaRegex($schema) {
        if (is_null($this->schemaRegexStorage[$schema])) {
            $regex = preg_replace(
                array("/\*/", "/\//", "/\.\*\./"),
                array(".*", "\/", ".*"),
                $schema
            );
            $regex = "/" . $regex . "/";
            $this->schemaRegexStorage[$schema] = $regex;
        }
        return $this->schemaRegexStorage[$schema];
    }
    private function ValidateSchema($schema, $url) {
        $regex = self::GetSchemaRegex($schema);
        return preg_match($regex, $url);
    }
    private function ValidateProviderSchemas($provider, $url) {
        $schemas = $provider['schemas'];
        foreach ($schemas as $schema) {
            if ($this->ValidateSchema($schema, $url)) {
                return true;
            }
        }
        return false;
    }
    /**
     * Create new or find exists @see OEmbedProvider which can process this $url
     * @param $url
     * @return OEmbedProvider|null
     */
    public function GetProvider($url) {
        foreach($this->providerConfiguration as $provider) {
            if ($this->ValidateProviderSchemas($provider, $url)) {
                $name = $provider['name'];
                if ($this->providerStorage[$name] == null) {
                    $this->providerStorage[$name] = new OEmbedProvider($provider['endpoint']);
                }
                return $this->providerStorage[$name];
            }
        }
    }
    /**
     * Return a @see OEmbedData for this $url is possible
     * @param $url
     * @return OEmbedData|null
     */
    public function GetEmbedData($url) {
        $provider = $this->GetProvider($url);
        if ($provider == null) {
            return null;
        }
        return $provider->GetEmbedData($url);
    }
}
class OEmbedProvider {
    private $contentProviderEndpoint;
    public function __construct($contentProviderEndpoint) {
        $this->contentProviderEndpoint = $contentProviderEndpoint;
    }
    /**
     * @param $url
     * @return OEmbedData
     */
    public function GetEmbedData($url) {
        $targetUrl = $this->contentProviderEndpoint . '?format=json&url=' . $url;
        // Request a oEmbed content
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $targetUrl);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        $result = curl_exec($ch);
        // Verify response
        $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if ($status_code != 200) {
            return null;
        }
        // Parse json response
        $response = json_decode($result);
        if ($response == null) {
            return null;
        }
        // Build information about links
        $embedData = new OEmbedData();
		// Да, да, да, я знаю, что тут костыль
        if ($response->type == "photo") {
            $embedData->html =
                '<a href="' . $response->url . '" title="by ' . $response->author_name . '">' .
                    '<img src="' . $response->thumbnail_url . '" />' .
                '</a>';
            ;
        } else {
            $embedData->html = $response->html;
        }
        return $embedData;
    }
}А работать с этим примерно так:
// Инициализация
$config['OEmbedProviders'] = array(
	array(
        'name' => 'sketchfab',
        'endpoint' => 'https://sketchfab.com/oembed',
        'schemas' => array(
            'https://sketchfab.com/models/*',
            'https://sketchfab.com/*/folders/*',
            'http://sketchfab.com/*/folders/*',
            'http://sketchfab.com/models/*'
        )
    ),
);
new OEmbedProviderFactory($config['OEmbedProviders']);
// Применяемс на деле
$url = "....";
$factory = &OEmbedProviderFactory::GetInst();
echo $factory->GetEmbedData($url);В будущем хочу развивать эту библиотечку ^^ Сейчас она в очень плохом и сыром виде.
В ближайшее время мы подготовим патч для сайта, где добавится поддержка данного протокола для SketchFab'а. Ждите)
В ближайшее время мы подготовим патч для сайта, где добавится поддержка данного протокола для SketchFab'а. Ждите)
Спасибо за внимание!
Дополнительно:
- oEmbed official site - официальный сайт протокола oEmbed
- SketchFab - каталог 3д моделей с возможностью интерактивного просмотра
- GitHub: aphp-oembed - библиотека для работы с oEmbed провайдерами. Подключайтесь)
 
                        
                        
                    
 BLOG-ALEXPR…
                                BLOG-ALEXPR…
                            




 
                    
                
prog:
Ред. prog