Merge pull request #15220 from annando/embed

Embedding Support for Twitter videos / automatic height adjust
This commit is contained in:
Philipp 2025-10-09 21:36:23 +02:00 committed by GitHub
commit c00dc37769
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 308 additions and 85 deletions

View file

@ -1,6 +1,6 @@
-- ------------------------------------------
-- Friendica 2025.07-rc (Interrupted Fern)
-- DB_UPDATE_VERSION 1581
-- DB_UPDATE_VERSION 1582
-- ------------------------------------------
@ -1432,6 +1432,10 @@ CREATE TABLE IF NOT EXISTS `post-media` (
`player-url` varbinary(383) COMMENT 'URL of the embedded player for this media',
`player-height` smallint unsigned COMMENT 'Height of the embedded player',
`player-width` smallint unsigned COMMENT 'Width of the embedded player',
`embed-type` varchar(10) COMMENT 'Type of the embed (e.g. rich or video)',
`embed-html` text COMMENT 'HTML embed code for this media',
`embed-height` smallint unsigned COMMENT 'Height of the embed',
`embed-width` smallint unsigned COMMENT 'Width of the embed',
`language` char(3) COMMENT 'Language information about this media in the ISO 639 format',
`published` datetime COMMENT 'Publification date of this media',
`modified` datetime COMMENT 'Modification date of this media',

View file

@ -33,6 +33,10 @@ Fields
| player-url | URL of the embedded player for this media | varbinary(383) | YES | | NULL | |
| player-height | Height of the embedded player | smallint unsigned | YES | | NULL | |
| player-width | Width of the embedded player | smallint unsigned | YES | | NULL | |
| embed-type | Type of the embed (e.g. rich or video) | varchar(10) | YES | | NULL | |
| embed-html | HTML embed code for this media | text | YES | | NULL | |
| embed-height | Height of the embed | smallint unsigned | YES | | NULL | |
| embed-width | Width of the embed | smallint unsigned | YES | | NULL | |
| language | Language information about this media in the ISO 639 format | char(3) | YES | | NULL | |
| published | Publification date of this media | datetime | YES | | NULL | |
| modified | Modification date of this media | datetime | YES | | NULL | |

View file

@ -38,6 +38,10 @@ use Psr\Http\Message\UriInterface;
* @property-read ?UriInterface $playerUrl
* @property-read ?int $playerWidth
* @property-read ?int $playerHeight
* @property-read ?string $embedType
* @property-read ?string $embedHtml
* @property-read ?int $embedWidth
* @property-read ?int $embedHeight
* @property-read ?int $attachId
* @property-read ?string $language
* @property-read ?string $published
@ -119,6 +123,14 @@ class PostMedia extends BaseEntity
protected $published;
/** @var ?string (Datetime) */
protected $modified;
/** @var ?string */
protected $embedType;
/** @var ?string */
protected $embedHtml;
/** @var ?int In pixels */
protected $embedWidth;
/** @var ?int In pixels */
protected $embedHeight;
public function __construct(
int $uriId,
@ -148,7 +160,11 @@ class PostMedia extends BaseEntity
?int $attachId = null,
?string $language = null,
?string $published = null,
?string $modified = null
?string $modified = null,
?string $embedType = null,
?string $embedHtml = null,
?int $embedWidth = null,
?int $embedHeight = null
) {
$this->uriId = $uriId;
$this->url = $url;
@ -178,6 +194,10 @@ class PostMedia extends BaseEntity
$this->language = $language;
$this->published = $published;
$this->modified = $modified;
$this->embedType = $embedType;
$this->embedHtml = $embedHtml;
$this->embedWidth = $embedWidth;
$this->embedHeight = $embedHeight;
}
@ -290,6 +310,10 @@ class PostMedia extends BaseEntity
$this->language,
$this->published,
$this->modified,
$this->embedType,
$this->embedHtml,
$this->embedWidth,
$this->embedHeight
);
}
@ -324,6 +348,10 @@ class PostMedia extends BaseEntity
$this->language,
$this->published,
$this->modified,
$this->embedType,
$this->embedHtml,
$this->embedWidth,
$this->embedHeight
);
}

View file

@ -65,6 +65,10 @@ class PostMedia extends BaseFactory implements ICanCreateFromTableRow
$row['language'],
$row['published'],
$row['modified'],
$row['embed-type'],
$row['embed-html'],
$row['embed-width'],
$row['embed-height']
);
}
@ -133,6 +137,10 @@ class PostMedia extends BaseFactory implements ICanCreateFromTableRow
'player-url' => $attachment['player_url'] ?? null,
'player-width' => $attachment['player_width'] ?? null,
'player-height' => $attachment['player_height'] ?? null,
'embed-type' => $attachment['embed_type'] ?? null,
'embed-html' => $attachment['embed_html'] ?? null,
'embed-width' => $attachment['embed_width'] ?? null,
'embed-height' => $attachment['embed_height'] ?? null,
'attach-id' => null,
'language' => null,
'published' => null,
@ -178,6 +186,10 @@ class PostMedia extends BaseFactory implements ICanCreateFromTableRow
'player-url' => $data['player']['embed'] ?? null,
'player-width' => $data['player']['width'] ?? null,
'player-height' => $data['player']['height'] ?? null,
'embed-type' => $data['embed']['type'] ?? null,
'embed-html' => $data['embed']['html'] ?? null,
'embed-width' => $data['embed']['width'] ?? null,
'embed-height' => $data['embed']['height'] ?? null,
'attach-id' => null,
'language' => $data['language'] ?? null,
'published' => $data['published'] ?? null,

View file

@ -146,6 +146,10 @@ class PostMedia extends BaseRepository
'player-url' => $PostMedia->playerUrl,
'player-height' => $PostMedia->playerHeight,
'player-width' => $PostMedia->playerWidth,
'embed-type' => $PostMedia->embedType,
'embed-html' => $PostMedia->embedHtml,
'embed-height' => $PostMedia->embedHeight,
'embed-width' => $PostMedia->embedWidth,
'attach-id' => $PostMedia->attachId,
'language' => $PostMedia->language,
'published' => $PostMedia->published,
@ -365,6 +369,8 @@ class PostMedia extends BaseRepository
$player = $this->getVideoAttachment($media, $uid);
} elseif ($allow_embed && !empty($media->playerUrl)) {
$player = $this->getPlayerIframe($media);
} elseif ($allow_embed && !empty($media->embedHtml)) {
$player = '<span class="embedded-media">' . $this->getEmbedIframe($media) . '</span>';
} else {
$player = $this->getLinkAttachment($media);
}
@ -395,7 +401,7 @@ class PostMedia extends BaseRepository
}
if (($postMedia->height ?? 0) > ($postMedia->width ?? 0)) {
$height = min($this->config->get('system', 'max_video_height') ?: '100%', $postMedia->height);
$height = min($this->config->get('system', 'max_height') ?: '100%', $postMedia->height);
$width = 'auto';
} else {
$height = 'auto';
@ -404,6 +410,8 @@ class PostMedia extends BaseRepository
if ($this->pConfig->get($uid, 'system', 'embed_media', false) && ($postMedia->playerUrl != '') && ($postMedia->playerHeight > 0)) {
$media = $this->getPlayerIframe($postMedia);
} elseif ($this->pConfig->get($uid, 'system', 'embed_media', false) && ($postMedia->embedHtml != '')) {
$media = $this->getEmbedIframe($postMedia);
} else {
/// @todo Move the template to /content as well
$media = Renderer::replaceMacros(Renderer::getMarkupTemplate($postMedia->type == Post\Media::HLS ? 'hls_top.tpl' : 'video_top.tpl'), [
@ -428,26 +436,38 @@ class PostMedia extends BaseRepository
return '';
}
$attributes = ' src="' . $postMedia->playerUrl . '"';
$max_height = $this->config->get('system', 'max_video_height') ?: $postMedia->playerHeight;
$div_style = '';
$iframe_style = '';
$height = min($this->config->get('system', 'max_height'), $postMedia->playerHeight);
if ($postMedia->playerWidth != 0 && $postMedia->playerHeight != 0) {
if ($postMedia->playerHeight > $postMedia->playerWidth) {
$factor = 100;
$height_attr = min($max_height, $postMedia->playerHeight);
} else {
$factor = round($postMedia->playerHeight / $postMedia->playerWidth, 2) * 100;
$height_attr = '100%';
}
$attributes .= ' height="' . $height_attr. '" style="position:absolute;left:0px;top:0px"';
$return = '<div style="position:relative;padding-bottom:' . $factor . '%;margin-bottom:1em">';
} else {
$attributes .= ' height="' . min($max_height, $postMedia->playerHeight) . '"';
$return = '<div style="position:relative">';
if ($postMedia->playerWidth != 0 && $postMedia->playerHeight != 0 && $postMedia->playerWidth > $this->config->get('system', 'max_width') && $postMedia->playerWidth > $postMedia->playerHeight) {
$factor = round($postMedia->playerHeight / $postMedia->playerWidth, 2) * 100;
$height = '100%';
$iframe_style .= 'position:absolute;left:0px;top:0px;';
$div_style .= 'position:relative;padding-bottom:' . $factor . '%;';
}
$return .= '<iframe ' . $attributes . ' width="100%" frameborder="0" allow="fullscreen, picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen sandbox="allow-same-origin allow-scripts"></iframe></div>';
return $return;
return Renderer::replaceMacros(Renderer::getMarkupTemplate('content/iframe.tpl'), [
'src' => $postMedia->playerUrl,
'height' => $height,
'width' => $postMedia->embedWidth && $postMedia->embedWidth <= $this->config->get('system', 'max_width') ? $postMedia->embedWidth : '100%',
'div_style' => $div_style,
'iframe_style' => $iframe_style,
]);
}
public function getEmbedIframe(PostMediaEntity $postMedia): string
{
if ($postMedia->embedHtml == '') {
return '';
}
return Renderer::replaceMacros(Renderer::getMarkupTemplate($postMedia->embedHeight ? 'content/embed-iframe.tpl' : 'content/embed-iframe-resize.tpl'), [
'id' => 'iframe-' . hash('md5', $postMedia->embedHtml),
'src' => $postMedia->embedHtml,
'height' => $postMedia->embedHeight + 20,
'width' => $postMedia->embedWidth && $postMedia->embedWidth <= $this->config->get('system', 'max_width') ? $postMedia->embedWidth : '100%',
]);
}
public function getAudioAttachment(PostMediaEntity $postMedia): string

View file

@ -443,10 +443,19 @@ class BBCode
$return = sprintf('<div class="type-%s">', $data['type']);
}
if ($embed && $data['player_url'] != '' && $data['player_height'] != 0) {
if ($embed && (($data['player_url'] != '' && $data['player_height'] != 0) || $data['embed_html'] != '')) {
$media = DI::postMediaFactory()->createFromAttachment($data, $uriid);
$return .= DI::postMediaRepository()->getPlayerIframe($media);
if ($data['player_url'] != '' && $data['player_height'] != 0) {
$return .= DI::postMediaRepository()->getPlayerIframe($media);
} else {
$return .= DI::postMediaRepository()->getEmbedIframe($media);
}
$preview_mode = self::PREVIEW_NO_IMAGE;
unset($data['title']);
unset($data['url']);
unset($data['description']);
unset($data['provider_url']);
unset($data['provider_name']);
}
if ($preview_mode == self::PREVIEW_NO_IMAGE) {

View file

@ -3405,6 +3405,10 @@ class Item
'player_url' => (string)$attachment->playerUrl,
'player_width' => $attachment->playerWidth,
'player_height' => $attachment->playerHeight,
'embed_type' => $attachment->embedType,
'embed_html' => $attachment->embedHtml,
'embed_width' => $attachment->embedWidth,
'embed_height' => $attachment->embedHeight,
];
if ($preview && $attachment->preview) {

View file

@ -475,6 +475,10 @@ class Media
$media['player-url'] = $data['player']['embed'] ?? null;
$media['player-height'] = $data['player']['height'] ?? null;
$media['player-width'] = $data['player']['width'] ?? null;
$media['embed-type'] = $data['embed']['type'] ?? null;
$media['embed-html'] = $data['embed']['html'] ?? null;
$media['embed-height'] = $data['embed']['height'] ?? null;
$media['embed-width'] = $data['embed']['width'] ?? null;
$media['language'] = $data['language'] ?? null;
$media['published'] = $data['published'] ?? null;
$media['modified'] = $data['modified'] ?? null;

View file

@ -1926,14 +1926,17 @@ class Receiver
$attachment['player-height'] = $player['height'] ?? null;
$attachment['player-width'] = $player['width'] ?? null;
foreach ($attachments as $media) {
if (isset($media['height']) && isset($media['width'])) {
if ($media['height'] > $attachment['player-height'] || $media['width'] > $attachment['player-width']) {
$attachment['player-height'] = $media['height'];
$attachment['player-width'] = $media['width'];
if (is_null($attachment['player-height']) && is_null($attachment['player-width'])) {
foreach ($attachments as $media) {
if (isset($media['height']) && isset($media['width'])) {
if ($media['height'] > $attachment['player-height'] || $media['width'] > $attachment['player-width']) {
$attachment['player-height'] = $media['height'];
$attachment['player-width'] = $media['width'];
}
}
}
}
if (!$height && !$width) {
$attachment['height'] = $attachment['player-height'];
$attachment['width'] = $attachment['player-width'];

View file

@ -494,8 +494,6 @@ class ParseUrl
}
}
$siteinfo = self::getOembedInfo($xpath, $siteinfo);
$list = $xpath->query("//script[@type='application/ld+json']");
foreach ($list as $node) {
if (!empty($node->nodeValue)) {
@ -506,6 +504,8 @@ class ParseUrl
}
}
$siteinfo = self::getOembedInfo($xpath, $siteinfo);
if (!empty($siteinfo['player']['stream'])) {
// Only add player data to media arrays if there is no duplicate
$content_urls = array_merge(array_column($siteinfo['audio'] ?? [], 'content'), array_column($siteinfo['video'] ?? [], 'content'));
@ -1299,50 +1299,58 @@ class ParseUrl
}
/**
* Fetch additional information via oEmbed
* Fetch oEmbed data
*
* @param DOMXPath $xpath
* @param array $siteinfo
* @param string $url
*
* @return array siteinfo
* @return array oEmbed data
*/
private static function getOembedInfo(DOMXPath $xpath, array $siteinfo): array
private static function getOembedData(DOMXPath $xpath, string $url): array
{
$oembed = '';
foreach ($xpath->query("//link[@type='application/json+oembed']") as $link) {
/** @var DOMElement $link */
$href = $link->getAttributeNode('href')->nodeValue;
$oembed = $href;
DI::logger()->debug('Found oEmbed JSON', ['url' => $href]);
$data = [];
if (in_array(parse_url(Strings::normaliseLink($url), PHP_URL_HOST), ['twitter.com', 'x.com'])) {
$oembed = 'https://publish.twitter.com/oembed?url=' . urlencode($url) . '&dnt=true';
$systemLanguage = DI::config()->get('system', 'language');
if ($systemLanguage) {
$oembed .= '&lang=' . $systemLanguage;
}
DI::logger()->debug('Using Twitter oEmbed', ['url' => $url, 'oembed' => $oembed]);
}
if (empty($oembed)) {
$embera = new Embera();
$urldata = $embera->getUrlData([$siteinfo['url']]);
if (empty($urldata)) {
return $siteinfo;
}
$data = current($urldata);
DI::logger()->debug('Found oEmbed JSON from Embera', ['url' => $siteinfo['url']]);
} else {
$result = DI::httpClient()->get($oembed, HttpClientAccept::DEFAULT, [HttpClientOptions::REQUEST => HttpClientRequest::SITEINFO]);
if (!$result->isSuccess()) {
return $siteinfo;
}
$json_string = $result->getBodyString();
if (empty($json_string)) {
return $siteinfo;
if (!$oembed) {
foreach ($xpath->query("//link[@type='application/json+oembed']") as $link) {
/** @var DOMElement $link */
$oembed = $link->getAttributeNode('href')->nodeValue;
DI::logger()->debug('Found oEmbed JSON from page', ['url' => $url, 'oembed' => $oembed]);
}
}
$data = json_decode($json_string, true);
if ($oembed) {
$oembed .= '&maxwidth=' . DI::config()->get('system', 'max_width') . '&maxheight=' . DI::config()->get('system', 'max_height') . '&format=json';
$result = DI::httpClient()->get($oembed, HttpClientAccept::DEFAULT, [HttpClientOptions::REQUEST => HttpClientRequest::SITEINFO]);
if ($result->isSuccess() && $result->getBodyString()) {
$data = json_decode($result->getBodyString(), true);
}
}
if (empty($data) || !is_array($data)) {
return $siteinfo;
$embera = new Embera(['maxwidth' => DI::config()->get('system', 'max_width'), 'maxheight' => DI::config()->get('system', 'max_height')]);
$urldata = $embera->getUrlData($url);
if (empty($urldata)) {
return [];
}
$data = current($urldata);
DI::logger()->debug('Found oEmbed JSON from Embera', ['url' => $url]);
}
return $data;
}
DI::logger()->debug('Got oEmbed data', ['url' => $siteinfo['url'], 'type' => $data['type'], 'data' => $data]);
private static function getSiteinfoFromoEmbed(array $siteinfo, array $data): array
{
// Youtube provides only basic information to some IP ranges.
// Dailymotion only provices "Dailymotion" as title in their meta tags, so oEmbed is better
// @todo We have to decide if we always trust oEmbed more than the meta tags
@ -1382,6 +1390,8 @@ class ParseUrl
if (isset($data[$key]) && (empty($siteinfo[$value]) || $overwrite)) {
if ($value == 'published') {
$siteinfo[$value] = DateTimeFormat::utc($data[$key]);
} elseif (is_string($value)) {
$siteinfo[$value] = trim(strip_tags(html_entity_decode($data[$key], ENT_COMPAT, 'UTF-8')));
} else {
$siteinfo[$value] = $data[$key];
}
@ -1391,26 +1401,36 @@ class ParseUrl
if (!empty($unknown_fields)) {
DI::logger()->debug('Unknown oEmbed fields', ['url' => $siteinfo['url'], 'fields' => $unknown_fields]);
}
return $siteinfo;
}
if (!empty($data['html']) && empty($siteinfo['player'])) {
$siteinfo = self::setPlayer($data, $siteinfo);
private static function getOembedInfo(DOMXPath $xpath, array $siteinfo): array
{
$data = self::getOembedData($xpath, $siteinfo['url']);
if (empty($data) || !is_array($data)) {
return $siteinfo;
}
$siteinfo = self::getSiteinfoFromoEmbed($siteinfo, $data);
if ($data['type'] == 'video' & empty($siteinfo['player']) && ($data['provider_url'] ?? '') == 'https://www.tiktok.com' && isset($data['embed_product_id']) && isset($data['thumbnail_width']) && isset($data['thumbnail_height'])) {
$siteinfo['embed']['type'] = $data['type'];
$siteinfo['embed']['html'] = trim($data['html']);
$siteinfo['embed']['width'] = is_numeric($data['width'] ?? '') ? $data['width'] : $data['thumbnail_width'];
$siteinfo['embed']['height'] = is_numeric($data['height'] ?? '') ? $data['height'] : $data['thumbnail_height'];
$siteinfo['player']['embed'] = 'https://www.tiktok.com/player/v1/' . $data['embed_product_id'] . '?description=1&rel=0';
$siteinfo['player']['width'] = $data['thumbnail_width'];
$siteinfo['player']['height'] = $data['thumbnail_height'];
$siteinfo['player']['width'] = $siteinfo['embed']['width'];
$siteinfo['player']['height'] = $siteinfo['embed']['height'];
return $siteinfo;
}
if (!empty($siteinfo['player'])) {
foreach (['width' => 'width', 'height' => 'height', 'fixedWidth' => 'width'] as $key => $value) {
if (empty($siteinfo['player'][$value]) && !empty($data[$key])) {
$siteinfo['player'][$value] = $data[$key];
}
}
if (!isset($data['html'])) {
return $siteinfo;
}
if ($data['type'] == 'rich' && isset($data['html']) && !isset($siteinfo['text'])) {
unset($siteinfo['player']);
if ($data['type'] == 'rich' && !isset($siteinfo['text'])) {
$bbcode = HTML::toBBCode($data['html'] ?? '');
$bbcode = preg_replace("(\[url\](.*?)\[\/url\])ism", "", $bbcode);
@ -1418,6 +1438,46 @@ class ParseUrl
DI::logger()->debug('Text is fetched from oEmbed HTML', ['url' => $siteinfo['url'], 'text' => $siteinfo['text']]);
}
$siteinfo = self::setPlayer($data, $siteinfo);
if (!empty($siteinfo['player'])) {
$siteinfo['embed']['type'] = $data['type'];
$siteinfo['embed']['html'] = trim($data['html']);
$siteinfo['embed']['width'] = $siteinfo['player']['width'];
$siteinfo['embed']['height'] = $siteinfo['player']['height'];
return $siteinfo;
}
if (($data['provider_url'] ?? '') == 'https://twitter.com') {
if (preg_match_all('#https?://t\.co/[a-zA-Z0-9]+#', $data['html'], $matches)) {
$links = array_unique($matches[0]);
foreach ($links as $link) {
$curlResult = DI::httpClient()->head($link);
$redirect = $curlResult->getRedirectUrl();
if (preg_match('#/(video|broadcasts)/#', $redirect)) {
$siteinfo['embed']['type'] = $data['type'];
$siteinfo['embed']['html'] = trim(str_replace('<blockquote class="twitter-tweet"', '<blockquote class="twitter-tweet" data-media-max-width="560"', $data['html']));
$siteinfo['embed']['width'] = is_numeric($data['width'] ?? '') ? $data['width'] : null;
$siteinfo['embed']['height'] = is_numeric($data['height'] ?? '') ? $data['height'] : null;
DI::logger()->debug('Fetched Twitter video oEmbed HTML', ['url' => $siteinfo['url']]);
return $siteinfo;
}
}
}
// We don't embed regular Twitter posts, since this doesn't add any additional value
return $siteinfo;
}
if ($data['type'] != 'video' && ($siteinfo['pagetype'] ?? '') != 'video') {
return $siteinfo;
}
$siteinfo['embed']['type'] = $data['type'];
$siteinfo['embed']['html'] = trim($data['html']);
$siteinfo['embed']['width'] = is_numeric($data['width'] ?? '') ? $data['width'] : null;
$siteinfo['embed']['height'] = is_numeric($data['height'] ?? '') ? $data['height'] : null;
DI::logger()->debug('Fetched oEmbed HTML', ['provider' => $data['provider_url'], 'url' => $siteinfo['url']]);
return $siteinfo;
}
@ -1431,13 +1491,11 @@ class ParseUrl
*/
private static function setPlayer(array $data, array $siteinfo): array
{
// iframe content is most likely not a video but an embedded website
if (($data['type'] == 'rich') && ($data['asset_type'] ?? '') == 'article') {
return $siteinfo;
}
if (isset($data['iframe_url'])) {
$siteinfo['player']['embed'] = $data['iframe_url'];
$siteinfo['player']['embed'] = $data['iframe_url'];
$siteinfo['player']['width'] = is_numeric($data['width'] ?? '') ? $data['width'] : null;
$siteinfo['player']['height'] = is_numeric($data['height'] ?? '') ? $data['height'] : null;
DI::logger()->debug('Found oEmbed iframe_url parameter', ['embed' => $siteinfo['player']['embed'], 'width' => $siteinfo['player']['width'], 'height' => $siteinfo['player']['height']]);
return $siteinfo;
}
@ -1464,7 +1522,9 @@ class ParseUrl
return $siteinfo;
}
$siteinfo['player']['embed'] = $src;
$siteinfo['player']['embed'] = $src;
$siteinfo['player']['width'] = is_numeric($data['width'] ?? '') ? $data['width'] : null;
$siteinfo['player']['height'] = is_numeric($data['height'] ?? '') ? $data['height'] : null;
$width = $iframe->getAttributeNode('width')->nodeValue ?? null;
if (!empty($width) && is_numeric($width)) {
@ -1476,8 +1536,7 @@ class ParseUrl
$siteinfo['player']['height'] = $height;
}
DI::logger()->debug('Found oEmbed iframe', ['embed' => $siteinfo['player']['embed'] ?? '', 'width' => $siteinfo['player']['width'] ?? '', 'height' => $siteinfo['player']['height'] ?? '']);
DI::logger()->debug('Found oEmbed iframe', ['embed' => $siteinfo['player']['embed'], 'width' => $siteinfo['player']['width'], 'height' => $siteinfo['player']['height']]);
return $siteinfo;
}
}

View file

@ -44,7 +44,7 @@ use Friendica\Database\DBA;
// This file is required several times during the test in DbaDefinition which justifies this condition
if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1581);
define('DB_UPDATE_VERSION', 1582);
}
return [
@ -1437,6 +1437,10 @@ return [
"player-url" => ["type" => "varbinary(383)", "comment" => "URL of the embedded player for this media"],
"player-height" => ["type" => "smallint unsigned", "comment" => "Height of the embedded player"],
"player-width" => ["type" => "smallint unsigned", "comment" => "Width of the embedded player"],
"embed-type" => ["type" => "varchar(10)", "comment" => "Type of the embed (e.g. rich or video)"],
"embed-html" => ["type" => "text", "comment" => "HTML embed code for this media"],
"embed-height" => ["type" => "smallint unsigned", "comment" => "Height of the embed"],
"embed-width" => ["type" => "smallint unsigned", "comment" => "Width of the embed"],
"language" => ["type" => "char(3)", "comment" => "Language information about this media in the ISO 639 format"],
"published" => ["type" => "datetime", "comment" => "Publification date of this media"],
"modified" => ["type" => "datetime", "comment" => "Modification date of this media"],

View file

@ -393,6 +393,10 @@ return [
// Maximum number of "fetchreplies" activities in the callstack. The higher, the more complete a thread will be.
'max_fetchreplies_depth' => 2,
// max_height (Integer)
// Maximum height for OEmbed and videos
'max_height' => 620,
// max_image_length (Integer)
// An alternate way of limiting picture upload sizes.
// Specify the maximum pixel length that pictures are allowed to be (for non-square pictures, it will apply to the longest side).
@ -420,9 +424,9 @@ return [
// Maximum recursion depth when fetching posts until the job is delegated to a worker task or finished.
'max_recursion_depth' => 50,
// max_video_height (Integer)
// Maximum height of videos in portrait mode.
'max_video_height' => 640,
// max_width (Integer)
// Maximum width for OEmbed and videos
'max_width' => 620,
// memcache_host (String)
// Host name of the memcache daemon.

View file

@ -8,6 +8,7 @@
namespace Friendica\Test\src\Content\Text;
use Friendica\Content\Text\BBCode;
use Friendica\Core\Renderer;
use Friendica\DI;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Test\FixtureTestCase;
@ -726,8 +727,11 @@ Lucas: For the right price, yes.[/share]',
{
return [
'player' => [
'expected' => 'text <div class="type-link"><div style="position:relative;padding-bottom:75%;margin-bottom:1em"><iframe src="http://domain.tld/player" height="100%" style="position:absolute;left:0px;top:0px" width="100%" frameborder="0" allow="fullscreen, picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen sandbox="allow-same-origin allow-scripts"></iframe></div><h4><a href="http://domain.tld/page" target="_blank" rel="noopener noreferrer">title</a></h4><blockquote>description</blockquote><sup><a href="http://domain.tld/provider_url" target="_blank" rel="noopener noreferrer">author_name (provider_name)</a></sup></div>',
'data' => [
'expected' => 'text <div class="type-link"><div style="">' . "\n" .
' <iframe src="http://domain.tld/player" style="" height="480" width="100%" frameborder="0" allow="fullscreen, picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen sandbox="allow-same-origin allow-scripts"></iframe>' . "\n" .
"</div>\n" .
'</div>',
'data' => [
'author_name' => 'author_name',
'author_url' => 'http://domain.tld/author_url',
'description' => 'description',
@ -740,10 +744,31 @@ Lucas: For the right price, yes.[/share]',
'type' => 'link',
'url' => 'http://domain.tld/page',
'player_url' => 'http://domain.tld/player',
'player_width' => 640,
'player_width' => 620,
'player_height' => 480,
],
],
'embed' => [
'expected' => 'text <div class="type-link"><iframe srcdoc="&lt;iframe src=&quot;http://domain.tld/player&quot;&gt;&lt;/iframe&gt;" height="500" width="620" frameborder="0" allow="fullscreen, picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen sandbox="allow-same-origin allow-scripts"></iframe>' . "\n" .
'</div>',
'data' => [
'author_name' => 'author_name',
'author_url' => 'http://domain.tld/author_url',
'description' => 'description',
'image' => 'http://domain.tld/image',
'preview' => 'http://domain.tld/preview',
'provider_name' => 'provider_name',
'provider_url' => 'http://domain.tld/provider_url',
'text' => 'text',
'title' => 'title',
'type' => 'link',
'url' => 'http://domain.tld/page',
'player_url' => '',
'embed_html' => '<iframe src="http://domain.tld/player"></iframe>',
'embed_width' => 620,
'embed_height' => 480,
]
]
];
}
@ -755,6 +780,8 @@ Lucas: For the right price, yes.[/share]',
*/
public function testConvertAttachment(string $expected, array $data)
{
Renderer::registerTemplateEngine('Friendica\Render\FriendicaSmartyEngine');
$actual = BBCode::convertAttachment('', BBCode::INTERNAL, $data, 0, BBCode::PREVIEW_LARGE, true);
self::assertEquals($expected, $actual);

View file

@ -0,0 +1,25 @@
{{*
* Copyright (C) 2010-2024, the Friendica project
* SPDX-FileCopyrightText: 2010-2024 the Friendica project
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*}}
<iframe id="{{$id}}" srcdoc="{{$src}}&lt;script&gt;
function sendResize() {
parent.postMessage({type:'resize',height:document.body.scrollHeight},'*');
}
window.onload = function() {
sendResize();
setTimeout(sendResize, 2000);
};
&lt;/script&gt;" width="{{$width}}" frameborder="0" allow="fullscreen, picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen sandbox="allow-same-origin allow-scripts"></iframe>
<script>
window.addEventListener('message', function(event) {
if (event.data && event.data.type === 'resize') {
var iframe = document.getElementById('{{$id}}');
if (iframe) {
iframe.style.height = (event.data.height + 20) + 'px';
}
}
});
</script>

View file

@ -0,0 +1,7 @@
{{*
* Copyright (C) 2010-2024, the Friendica project
* SPDX-FileCopyrightText: 2010-2024 the Friendica project
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*}}
<iframe srcdoc="{{$src}}" height="{{$height}}" width="{{$width}}" frameborder="0" allow="fullscreen, picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen sandbox="allow-same-origin allow-scripts"></iframe>

View file

@ -0,0 +1,9 @@
{{*
* Copyright (C) 2010-2024, the Friendica project
* SPDX-FileCopyrightText: 2010-2024 the Friendica project
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*}}
<div style="{{$div_style}}">
<iframe src="{{$src}}" style="{{$iframe_style}}" height="{{$height}}" width="{{$width}}" frameborder="0" allow="fullscreen, picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen sandbox="allow-same-origin allow-scripts"></iframe>
</div>