getThemeInfoValue('videowidth') . ':' . $embedurl; $condition = ['url' => Strings::normaliseLink($embedurl), 'maxwidth' => $appHelper->getThemeInfoValue('videowidth')]; $oembed_record = DBA::selectFirst('oembed', ['content'], $condition); if (DBA::isResult($oembed_record)) { $json_string = $oembed_record['content']; } else { $json_string = DI::cache()->get($cache_key); } // These media files should now be caught in bbcode.php // left here as a fallback in case this is called from another source $noexts = ['mp3', 'mp4', 'ogg', 'ogv', 'oga', 'ogm', 'webm']; $ext = pathinfo(strtolower($embedurl), PATHINFO_EXTENSION); $oembed = new \Friendica\Object\OEmbed($embedurl); if ($json_string) { $oembed->parseJSON($json_string); } else { $json_string = ''; if (!in_array($ext, $noexts)) { // try oembed autodiscovery $html_text = DI::httpClient()->fetch($embedurl, HttpClientAccept::HTML, 15, '', HttpClientRequest::SITEINFO); if (!empty($html_text)) { $dom = new DOMDocument(); if (@$dom->loadHTML($html_text)) { $xpath = new DOMXPath($dom); foreach ( $xpath->query("//link[@type='application/json+oembed'] | //link[@type='text/json+oembed']") as $link) { /** @var DOMElement $link */ $href = $link->getAttributeNode('href')->nodeValue; // Both Youtube and Vimeo output OEmbed endpoint URL with HTTP // but their OEmbed endpoint is only accessible by HTTPS ¯\_(ツ)_/¯ $href = str_replace(['http://www.youtube.com/', 'http://player.vimeo.com/'], ['https://www.youtube.com/', 'https://player.vimeo.com/'], $href); $result = DI::httpClient()->get($href . '&maxwidth=' . $appHelper->getThemeInfoValue('videowidth'), HttpClientAccept::DEFAULT, [HttpClientOptions::REQUEST => HttpClientRequest::SITEINFO]); if ($result->isSuccess()) { $json_string = $result->getBodyString(); break; } } } } } $json_string = trim($json_string); if (!$json_string || $json_string[0] != '{') { $json_string = '{"type":"error"}'; } $oembed->parseJSON($json_string); if (!empty($oembed->type) && $oembed->type != 'error') { DBA::insert('oembed', [ 'url' => Strings::normaliseLink($embedurl), 'maxwidth' => $appHelper->getThemeInfoValue('videowidth'), 'content' => $json_string, 'created' => DateTimeFormat::utcNow() ], Database::INSERT_UPDATE); $cache_ttl = Duration::DAY; } else { $cache_ttl = Duration::FIVE_MINUTES; } DI::cache()->set($cache_key, $json_string, $cache_ttl); } // Always embed the SSL version if (!empty($oembed->html)) { $oembed->html = str_replace(['http://www.youtube.com/', 'http://player.vimeo.com/'], ['https://www.youtube.com/', 'https://player.vimeo.com/'], $oembed->html); } // Improve the OEmbed data with data from OpenGraph, Twitter cards and other sources $data = ParseUrl::getSiteinfoCached($embedurl); if (($oembed->type == 'error') && empty($data['title']) && empty($data['text'])) { return $oembed; } if (!self::isAllowedURL($embedurl) || ($oembed->type == 'error')) { $oembed->html = ''; $oembed->type = $data['type']; if ($oembed->type == 'photo') { if (!empty($data['images'])) { $oembed->url = $data['images'][0]['src']; $oembed->width = $data['images'][0]['width']; $oembed->height = $data['images'][0]['height']; } else { $oembed->type = 'link'; } } } if (!empty($data['title'])) { $oembed->title = $data['title']; } if (!empty($data['text'])) { $oembed->description = $data['text']; } if (!empty($data['publisher_name'])) { $oembed->provider_name = $data['publisher_name']; } if (!empty($data['publisher_url'])) { $oembed->provider_url = $data['publisher_url']; } if (!empty($data['author_name'])) { $oembed->author_name = $data['author_name']; } if (!empty($data['author_url'])) { $oembed->author_url = $data['author_url']; } if (!empty($data['images']) && ($oembed->type != 'photo')) { $oembed->thumbnail_url = $data['images'][0]['src']; $oembed->thumbnail_width = $data['images'][0]['width']; $oembed->thumbnail_height = $data['images'][0]['height']; } Hook::callAll('oembed_fetch_url', $embedurl); return $oembed; } /** * Returns a formatted string from OEmbed object * * @param \Friendica\Object\OEmbed $oembed * @param int $uriid * @return string */ private static function formatObject(\Friendica\Object\OEmbed $oembed, int $uriid): string { $ret = '
'; $test = Proxy::proxifyHtml($ret, $uriid); return str_replace("\n", "", $ret); } /** * Converts BBCode to HTML code * * @param string $text * @param int $uriid * @return string */ public static function BBCode2HTML(string $text, int $uriid): string { if (!preg_match_all("/\[embed\](.+?)\[\/embed\]/is", $text, $matches, PREG_SET_ORDER)) { return $text; } foreach ($matches as $match) { $data = self::fetchURL($match[1]); $text = str_replace($match[0], self::formatObject($data, $uriid), $text); } return $text; } /** * Determines if rich content OEmbed is allowed for the provided URL * * @param string $url * @return boolean * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public static function isAllowedURL(string $url): bool { if (!DI::config()->get('system', 'no_oembed_rich_content')) { return true; } $domain = parse_url($url, PHP_URL_HOST); if (empty($domain)) { return false; } $allowed = DI::config()->get('system', 'allowed_oembed', ''); if (empty($allowed)) { return false; } return Network::isDomainMatch($domain, explode(',', $allowed)); } /** * Returns a formatted HTML code from given URL and sets optional title * * @param string $url URL to fetch * @param string $title title (default: what comes from OEmbed object) * @param int $uriid * @return string Formatted HTML */ public static function getHTML(string $url, string $title, int $uriid): string { $o = self::fetchURL($url); if (!is_object($o) || property_exists($o, 'type') && $o->type == 'error') { throw new Exception('OEmbed failed for URL: ' . $url); } if (!empty($title)) { $o->title = $title; } $html = self::formatObject($o, $uriid); return $html; } }