diff --git a/database.sql b/database.sql index 16a030696d..45cb0c7274 100644 --- a/database.sql +++ b/database.sql @@ -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', diff --git a/doc/database/db_post-media.md b/doc/database/db_post-media.md index 66abde57f3..85ae290806 100644 --- a/doc/database/db_post-media.md +++ b/doc/database/db_post-media.md @@ -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 | | diff --git a/src/Content/Post/Entity/PostMedia.php b/src/Content/Post/Entity/PostMedia.php index bd864d67b8..1d4b9c09e2 100644 --- a/src/Content/Post/Entity/PostMedia.php +++ b/src/Content/Post/Entity/PostMedia.php @@ -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 ); } diff --git a/src/Content/Post/Factory/PostMedia.php b/src/Content/Post/Factory/PostMedia.php index 53b682f45a..af45a5c8a3 100644 --- a/src/Content/Post/Factory/PostMedia.php +++ b/src/Content/Post/Factory/PostMedia.php @@ -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, diff --git a/src/Content/Post/Repository/PostMedia.php b/src/Content/Post/Repository/PostMedia.php index fb108bb3ee..eae63e11b6 100644 --- a/src/Content/Post/Repository/PostMedia.php +++ b/src/Content/Post/Repository/PostMedia.php @@ -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 = ''; } 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 = '
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; } } diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 70f2b9865d..baace5452d 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -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"], diff --git a/static/defaults.config.php b/static/defaults.config.php index 081750616f..94a1a12176 100644 --- a/static/defaults.config.php +++ b/static/defaults.config.php @@ -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. diff --git a/tests/src/Content/Text/BBCodeTest.php b/tests/src/Content/Text/BBCodeTest.php index a2bc724384..8c0e9c1a5d 100644 --- a/tests/src/Content/Text/BBCodeTest.php +++ b/tests/src/Content/Text/BBCodeTest.php @@ -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 ', - 'data' => [ + 'expected' => 'text', + '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' . "\n" . +' ' . "\n" . +"\n" . +'' . "\n" . +'', + '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' => '', + '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); diff --git a/view/templates/content/embed-iframe-resize.tpl b/view/templates/content/embed-iframe-resize.tpl new file mode 100644 index 0000000000..2fb7ba728d --- /dev/null +++ b/view/templates/content/embed-iframe-resize.tpl @@ -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 + *}} + + diff --git a/view/templates/content/embed-iframe.tpl b/view/templates/content/embed-iframe.tpl new file mode 100644 index 0000000000..5186721ef9 --- /dev/null +++ b/view/templates/content/embed-iframe.tpl @@ -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 + *}} + diff --git a/view/templates/content/iframe.tpl b/view/templates/content/iframe.tpl new file mode 100644 index 0000000000..5732e40d81 --- /dev/null +++ b/view/templates/content/iframe.tpl @@ -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 + *}} ++ +