]> git.mxchange.org Git - friendica.git/commitdiff
Add DDD classes for post media entities
authorHypolite Petovan <hypolite@mrpetovan.com>
Fri, 29 Sep 2023 01:19:02 +0000 (21:19 -0400)
committerHypolite Petovan <hypolite@mrpetovan.com>
Tue, 3 Oct 2023 23:58:50 +0000 (19:58 -0400)
src/Content/Post/Collection/PostMedias.php [new file with mode: 0644]
src/Content/Post/Entity/PostMedia.php [new file with mode: 0644]
src/Content/Post/Factory/PostMedia.php [new file with mode: 0644]
src/Content/Post/Repository/PostMedia.php [new file with mode: 0644]

diff --git a/src/Content/Post/Collection/PostMedias.php b/src/Content/Post/Collection/PostMedias.php
new file mode 100644 (file)
index 0000000..5e75d90
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2023, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Content\Post\Collection;
+
+use Friendica\BaseCollection;
+use Friendica\Content\Post\Entity;
+
+class PostMedias extends BaseCollection
+{
+       /**
+        * @param Entity\PostMedia[] $entities
+        * @param int|null                   $totalCount
+        */
+       public function __construct(array $entities = [], int $totalCount = null)
+       {
+               parent::__construct($entities, $totalCount);
+       }
+
+       /**
+        * @return Entity\PostMedia
+        */
+       public function current(): Entity\PostMedia
+       {
+               return parent::current();
+       }
+}
diff --git a/src/Content/Post/Entity/PostMedia.php b/src/Content/Post/Entity/PostMedia.php
new file mode 100644 (file)
index 0000000..ca064d4
--- /dev/null
@@ -0,0 +1,220 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2023, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Content\Post\Entity;
+
+use Friendica\BaseEntity;
+use Friendica\Network\Entity\MimeType;
+use Friendica\Util\Proxy;
+use Psr\Http\Message\UriInterface;
+
+
+/**
+ * @property-read int $id
+ * @property-read int $uriId
+ * @property-read ?int $activityUriId
+ * @property-read UriInterface $url
+ * @property-read int $type
+ * @property-read MimeType $mimetype
+ * @property-read ?int $width
+ * @property-read ?int $height
+ * @property-read ?int $size
+ * @property-read ?UriInterface $preview
+ * @property-read ?int $previewWidth
+ * @property-read ?int $previewHeight
+ * @property-read ?string $description
+ * @property-read ?string $name
+ * @property-read ?UriInterface $authorUrl
+ * @property-read ?string $authorName
+ * @property-read ?UriInterface $authorImage
+ * @property-read ?UriInterface $publisherUrl
+ * @property-read ?string $publisherName
+ * @property-read ?UriInterface $publisherImage
+ * @property-read ?string $blurhash
+ */
+class PostMedia extends BaseEntity
+{
+       const TYPE_UNKNOWN     = 0;
+       const TYPE_IMAGE       = 1;
+       const TYPE_VIDEO       = 2;
+       const TYPE_AUDIO       = 3;
+       const TYPE_TEXT        = 4;
+       const TYPE_APPLICATION = 5;
+       const TYPE_TORRENT     = 16;
+       const TYPE_HTML        = 17;
+       const TYPE_XML         = 18;
+       const TYPE_PLAIN       = 19;
+       const TYPE_ACTIVITY    = 20;
+       const TYPE_ACCOUNT     = 21;
+       const TYPE_DOCUMENT    = 128;
+
+       /** @var int */
+       protected $id;
+       /** @var int */
+       protected $uriId;
+       /** @var UriInterface */
+       protected $url;
+       /** @var int One of TYPE_* */
+       protected $type;
+       /** @var MimeType */
+       protected $mimetype;
+       /** @var ?int */
+       protected $activityUriId;
+       /** @var ?int In pixels */
+       protected $width;
+       /** @var ?int In pixels */
+       protected $height;
+       /** @var ?int In bytes */
+       protected $size;
+       /** @var ?UriInterface Preview URL */
+       protected $preview;
+       /** @var ?int In pixels */
+       protected $previewWidth;
+       /** @var ?int In pixels */
+       protected $previewHeight;
+       /** @var ?string Alternative text like for images */
+       protected $description;
+       /** @var ?string */
+       protected $name;
+       /** @var ?UriInterface */
+       protected $authorUrl;
+       /** @var ?string */
+       protected $authorName;
+       /** @var ?UriInterface Image URL */
+       protected $authorImage;
+       /** @var ?UriInterface */
+       protected $publisherUrl;
+       /** @var ?string */
+       protected $publisherName;
+       /** @var ?UriInterface Image URL */
+       protected $publisherImage;
+       /** @var ?string Blurhash string representation for images
+        * @see https://github.com/woltapp/blurhash
+        * @see https://blurha.sh/
+        */
+       protected $blurhash;
+
+       public function __construct(
+               int $uriId,
+               UriInterface $url,
+               int $type,
+               MimeType $mimetype,
+               ?int $activityUriId,
+               ?int $width = null,
+               ?int $height = null,
+               ?int $size = null,
+               ?UriInterface $preview = null,
+               ?int $previewWidth = null,
+               ?int $previewHeight = null,
+               ?string $description = null,
+               ?string $name = null,
+               ?UriInterface $authorUrl = null,
+               ?string $authorName = null,
+               ?UriInterface $authorImage = null,
+               ?UriInterface $publisherUrl = null,
+               ?string $publisherName = null,
+               ?UriInterface $publisherImage = null,
+               ?string $blurhash = null,
+               int $id = null
+       )
+       {
+               $this->uriId          = $uriId;
+               $this->url            = $url;
+               $this->type           = $type;
+               $this->mimetype       = $mimetype;
+               $this->activityUriId  = $activityUriId;
+               $this->width          = $width;
+               $this->height         = $height;
+               $this->size           = $size;
+               $this->preview        = $preview;
+               $this->previewWidth   = $previewWidth;
+               $this->previewHeight  = $previewHeight;
+               $this->description    = $description;
+               $this->name           = $name;
+               $this->authorUrl      = $authorUrl;
+               $this->authorName     = $authorName;
+               $this->authorImage    = $authorImage;
+               $this->publisherUrl   = $publisherUrl;
+               $this->publisherName  = $publisherName;
+               $this->publisherImage = $publisherImage;
+               $this->blurhash       = $blurhash;
+               $this->id             = $id;
+       }
+
+
+       /**
+        * Get media link for given media id
+        *
+        * @param string  $size One of the Proxy::SIZE_* constants
+        * @return string media link
+        */
+       public function getPhotoPath(string $size = ''): string
+       {
+               $url = '/photo/media/';
+               switch ($size) {
+                       case Proxy::SIZE_MICRO:
+                               $url .= Proxy::PIXEL_MICRO . '/';
+                               break;
+                       case Proxy::SIZE_THUMB:
+                               $url .= Proxy::PIXEL_THUMB . '/';
+                               break;
+                       case Proxy::SIZE_SMALL:
+                               $url .= Proxy::PIXEL_SMALL . '/';
+                               break;
+                       case Proxy::SIZE_MEDIUM:
+                               $url .= Proxy::PIXEL_MEDIUM . '/';
+                               break;
+                       case Proxy::SIZE_LARGE:
+                               $url .= Proxy::PIXEL_LARGE . '/';
+                               break;
+               }
+               return $url . $this->id;
+       }
+
+       /**
+        * Get preview path for given media id relative to the base URL
+        *
+        * @param string  $size One of the Proxy::SIZE_* constants
+        * @return string preview link
+        */
+       public function getPreviewPath(string $size = ''): string
+       {
+               $url = '/photo/preview/';
+               switch ($size) {
+                       case Proxy::SIZE_MICRO:
+                               $url .= Proxy::PIXEL_MICRO . '/';
+                               break;
+                       case Proxy::SIZE_THUMB:
+                               $url .= Proxy::PIXEL_THUMB . '/';
+                               break;
+                       case Proxy::SIZE_SMALL:
+                               $url .= Proxy::PIXEL_SMALL . '/';
+                               break;
+                       case Proxy::SIZE_MEDIUM:
+                               $url .= Proxy::PIXEL_MEDIUM . '/';
+                               break;
+                       case Proxy::SIZE_LARGE:
+                               $url .= Proxy::PIXEL_LARGE . '/';
+                               break;
+               }
+               return $url . $this->id;
+       }
+}
diff --git a/src/Content/Post/Factory/PostMedia.php b/src/Content/Post/Factory/PostMedia.php
new file mode 100644 (file)
index 0000000..fe71b21
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2023, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Content\Post\Factory;
+
+use Friendica\BaseFactory;
+use Friendica\Capabilities\ICanCreateFromTableRow;
+use Friendica\Content\Post\Entity;
+use Friendica\Network;
+use GuzzleHttp\Psr7\Uri;
+use Psr\Log\LoggerInterface;
+use stdClass;
+
+class PostMedia extends BaseFactory implements ICanCreateFromTableRow
+{
+       /** @var Network\Factory\MimeType */
+       private $mimeTypeFactory;
+
+       public function __construct(Network\Factory\MimeType $mimeTypeFactory, LoggerInterface $logger)
+       {
+               parent::__construct($logger);
+
+               $this->mimeTypeFactory = $mimeTypeFactory;
+       }
+
+       /**
+        * @inheritDoc
+        */
+       public function createFromTableRow(array $row)
+       {
+               return new Entity\PostMedia(
+                       $row['uri-id'],
+                       $row['url'] ? new Uri($row['url']) : null,
+                       $row['type'],
+                       $this->mimeTypeFactory->createFromContentType($row['mimetype']),
+                       $row['media-uri-id'],
+                       $row['width'],
+                       $row['height'],
+                       $row['size'],
+                       $row['preview'] ? new Uri($row['preview']) : null,
+                       $row['preview-width'],
+                       $row['preview-height'],
+                       $row['description'],
+                       $row['name'],
+                       $row['author-url'] ? new Uri($row['author-url']) : null,
+                       $row['author-name'],
+                       $row['author-image'] ? new Uri($row['author-image']) : null,
+                       $row['publisher-url'] ? new Uri($row['publisher-url']) : null,
+                       $row['publisher-name'],
+                       $row['publisher-image'] ? new Uri($row['publisher-image']) : null,
+                       $row['blurhash'],
+                       $row['id']
+               );
+       }
+
+       public function createFromBlueskyImageEmbed(int $uriId, stdClass $image): Entity\PostMedia
+       {
+               return new Entity\PostMedia(
+                       $uriId,
+                       new Uri($image->fullsize),
+                       Entity\PostMedia::TYPE_IMAGE,
+                       new Network\Entity\MimeType('unkn', 'unkn'),
+                       null,
+                       null,
+                       null,
+                       null,
+                       new Uri($image->thumb),
+                       null,
+                       null,
+                       $image->alt,
+               );
+       }
+
+
+       public function createFromBlueskyExternalEmbed(int $uriId, stdClass $external): Entity\PostMedia
+       {
+               return new Entity\PostMedia(
+                       $uriId,
+                       new Uri($external->uri),
+                       Entity\PostMedia::TYPE_HTML,
+                       new Network\Entity\MimeType('text', 'html'),
+                       null,
+                       null,
+                       null,
+                       null,
+                       null,
+                       null,
+                       null,
+                       $external->description,
+                       $external->title
+               );
+       }
+
+       public function createFromAttachment(int $uriId, array $attachment)
+       {
+               $attachment['uri-id'] = $uriId;
+               return $this->createFromTableRow($attachment);
+       }
+}
diff --git a/src/Content/Post/Repository/PostMedia.php b/src/Content/Post/Repository/PostMedia.php
new file mode 100644 (file)
index 0000000..405d9eb
--- /dev/null
@@ -0,0 +1,204 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2023, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Content\Post\Repository;
+
+use Friendica\BaseCollection;
+use Friendica\BaseRepository;
+use Friendica\Content\Post\Collection;
+use Friendica\Content\Post\Entity;
+use Friendica\Content\Post\Factory;
+use Friendica\Database\Database;
+use Friendica\Util\Strings;
+use Psr\Log\LoggerInterface;
+
+class PostMedia extends BaseRepository
+{
+       protected static $table_name = 'post-media';
+
+       public function __construct(Database $database, LoggerInterface $logger, Factory\PostMedia $factory)
+       {
+               parent::__construct($database, $logger, $factory);
+       }
+
+       protected function _select(array $condition, array $params = []): BaseCollection
+       {
+               $rows = $this->db->selectToArray(static::$table_name, [], $condition, $params);
+
+               $Entities = new Collection\PostMedias();
+               foreach ($rows as $fields) {
+                       $Entities[] = $this->factory->createFromTableRow($fields);
+               }
+
+               return $Entities;
+       }
+
+       public function selectOneById(int $postMediaId): Entity\PostMedia
+       {
+               return $this->_selectOne(['id' => $postMediaId]);
+       }
+
+       public function selectByUriId(int $uriId): Collection\PostMedias
+       {
+               return $this->_select(['uri-id' => $uriId]);
+       }
+
+       public function save(Entity\PostMedia $PostMedia): Entity\PostMedia
+       {
+               $fields = [
+                       'uri-id'          => $PostMedia->uriId,
+                       'url'             => $PostMedia->url->__toString(),
+                       'type'            => $PostMedia->type,
+                       'mimetype'        => $PostMedia->mimetype->__toString(),
+                       'height'          => $PostMedia->height,
+                       'width'           => $PostMedia->width,
+                       'size'            => $PostMedia->size,
+                       'preview'         => $PostMedia->preview ? $PostMedia->preview->__toString() : null,
+                       'preview-height'  => $PostMedia->previewHeight,
+                       'preview-width'   => $PostMedia->previewWidth,
+                       'description'     => $PostMedia->description,
+                       'name'            => $PostMedia->name,
+                       'author-url'      => $PostMedia->authorUrl ? $PostMedia->authorUrl->__toString() : null,
+                       'author-name'     => $PostMedia->authorName,
+                       'author-image'    => $PostMedia->authorImage ? $PostMedia->authorImage->__toString() : null,
+                       'publisher-url'   => $PostMedia->publisherUrl ? $PostMedia->publisherUrl->__toString() : null,
+                       'publisher-name'  => $PostMedia->publisherName,
+                       'publisher-image' => $PostMedia->publisherImage ? $PostMedia->publisherImage->__toString() : null,
+                       'media-uri-id'    => $PostMedia->activityUriId,
+                       'blurhash'        => $PostMedia->blurhash,
+               ];
+
+               if ($PostMedia->id) {
+                       $this->db->update(self::$table_name, $fields, ['id' => $PostMedia->id]);
+               } else {
+                       $this->db->insert(self::$table_name, $fields, Database::INSERT_IGNORE);
+
+                       $newPostMediaId = $this->db->lastInsertId();
+
+                       $PostMedia = $this->selectOneById($newPostMediaId);
+               }
+
+               return $PostMedia;
+       }
+
+
+       /**
+        * Split the attachment media in the three segments "visual", "link" and "additional"
+        *
+        * @param int    $uri_id URI id
+        * @param array  $links list of links that shouldn't be added
+        * @param bool   $has_media
+        * @return Collection\PostMedias[] Three collections in "visual", "link" and "additional" keys
+        */
+       public function splitAttachments(int $uri_id, array $links = [], bool $has_media = true): array
+       {
+               $attachments = [
+                       'visual'     => new Collection\PostMedias(),
+                       'link'       => new Collection\PostMedias(),
+                       'additional' => new Collection\PostMedias(),
+               ];
+
+               if (!$has_media) {
+                       return $attachments;
+               }
+
+               $PostMedias = $this->selectByUriId($uri_id);
+               if (!count($PostMedias)) {
+                       return $attachments;
+               }
+
+               $heights = [];
+               $selected = '';
+               $previews = [];
+
+               foreach ($PostMedias as $PostMedia) {
+                       foreach ($links as $link) {
+                               if (Strings::compareLink($link, $PostMedia->url)) {
+                                       continue 2;
+                               }
+                       }
+
+                       // Avoid adding separate media entries for previews
+                       foreach ($previews as $preview) {
+                               if (Strings::compareLink($preview, $PostMedia->url)) {
+                                       continue 2;
+                               }
+                       }
+
+                       // Currently these two types are ignored here.
+                       // Posts are added differently and contacts are not displayed as attachments.
+                       if (in_array($PostMedia->type, [Entity\PostMedia::TYPE_ACCOUNT, Entity\PostMedia::TYPE_ACTIVITY])) {
+                               continue;
+                       }
+
+                       if (!empty($PostMedia->preview)) {
+                               $previews[] = $PostMedia->preview;
+                       }
+
+                       //$PostMedia->filetype = $filetype;
+                       //$PostMedia->subtype = $subtype;
+
+                       if ($PostMedia->type == Entity\PostMedia::TYPE_HTML || ($PostMedia->mimetype->type == 'text' && $PostMedia->mimetype->subtype == 'html')) {
+                               $attachments['link'][] = $PostMedia;
+                               continue;
+                       }
+
+                       if (
+                               in_array($PostMedia->type, [Entity\PostMedia::TYPE_AUDIO, Entity\PostMedia::TYPE_IMAGE]) ||
+                               in_array($PostMedia->mimetype->type, ['audio', 'image'])
+                       ) {
+                               $attachments['visual'][] = $PostMedia;
+                       } elseif (($PostMedia->type == Entity\PostMedia::TYPE_VIDEO) || ($PostMedia->mimetype->type == 'video')) {
+                               if (!empty($PostMedia->height)) {
+                                       // Peertube videos are delivered in many different resolutions. We pick a moderate one.
+                                       // Since only Peertube provides a "height" parameter, this wouldn't be executed
+                                       // when someone for example on Mastodon was sharing multiple videos in a single post.
+                                       $heights[$PostMedia->height] = (string)$PostMedia->url;
+                                       $video[(string) $PostMedia->url] = $PostMedia;
+                               } else {
+                                       $attachments['visual'][] = $PostMedia;
+                               }
+                       } else {
+                               $attachments['additional'][] = $PostMedia;
+                       }
+               }
+
+               if (!empty($heights)) {
+                       ksort($heights);
+                       foreach ($heights as $height => $url) {
+                               if (empty($selected) || $height <= 480) {
+                                       $selected = $url;
+                               }
+                       }
+
+                       if (!empty($selected)) {
+                               $attachments['visual'][] = $video[$selected];
+                               unset($video[$selected]);
+                               foreach ($video as $element) {
+                                       $attachments['additional'][] = $element;
+                               }
+                       }
+               }
+
+               return $attachments;
+       }
+
+}