]> git.mxchange.org Git - friendica.git/commitdiff
API: The media upload (audio, video) is now possible
authorMichael <heluecht@pirati.ca>
Wed, 10 Jul 2024 20:23:11 +0000 (20:23 +0000)
committerMichael <heluecht@pirati.ca>
Tue, 16 Jul 2024 20:08:24 +0000 (20:08 +0000)
src/Factory/Api/Mastodon/Attachment.php
src/Model/Attach.php
src/Model/Post/Media.php
src/Module/Api/Mastodon/InstanceV2.php
src/Module/Api/Mastodon/Media.php
src/Module/Api/Mastodon/Statuses.php
src/Module/Media/Attachment/Upload.php
src/Object/Api/Mastodon/InstanceV2/MediaAttachmentsConfig.php
static/routes.config.php

index 18207fa1beb77ec7c9396837f6c52decb45f5b1e..f60ed89377ecce1788f84ab3298034aeb673a5cb 100644 (file)
@@ -23,6 +23,7 @@ namespace Friendica\Factory\Api\Mastodon;
 
 use Friendica\App\BaseURL;
 use Friendica\BaseFactory;
+use Friendica\Model\Attach;
 use Friendica\Model\Photo;
 use Friendica\Network\HTTPException;
 use Friendica\Model\Post;
@@ -144,4 +145,37 @@ class Attachment extends BaseFactory
                $object = new \Friendica\Object\Api\Mastodon\Attachment($attachment, 'image', $url, $preview_url, '');
                return $object->toArray();
        }
+
+       /**
+        * @param int $id id of the attachment
+        *
+        * @return array
+        * @throws HTTPException\InternalServerErrorException
+        */
+       public function createFromAttach(int $id): array
+       {
+               $media = Attach::selectFirst(['id', 'filetype'], ['id' => $id]);
+               if (empty($media)) {
+                       return [];
+               }
+               $attachment = [
+                       'id'          => 'attach:' . $media['id'],
+                       'description' => null,
+                       'blurhash'    => null,
+               ];
+
+               $types = [Post\Media::AUDIO => 'audio', Post\Media::VIDEO => 'video', Post\Media::IMAGE => 'image'];
+
+               $type = Post\Media::getType($media['filetype']);
+
+               $url = $this->baseUrl . '/attach/' . $id;
+
+               $object = new \Friendica\Object\Api\Mastodon\Attachment($attachment, $types[$type] ?? 'unknown', $url, '', '');
+               return $object->toArray();
+       }
+
+       public function isAttach(string $id): bool
+       {
+               return substr($id, 0, 7) == 'attach:';
+       }
 }
index 9b7962f0b1e2ea244f6cb320a3aaab6a13425d05..c245def1e2aeec8356b0df6304ca75e6e5315035 100644 (file)
@@ -245,6 +245,7 @@ class Attach
         * @param string $src Source file name
         * @param int    $uid User id
         * @param string $filename Optional file name
+        * @param string $filetype Optional file type
         * @param string $allow_cid
         * @param string $allow_gid
         * @param string $deny_cid
@@ -252,7 +253,7 @@ class Attach
         * @return boolean|int Insert id or false on failure
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
-       public static function storeFile(string $src, int $uid, string $filename = '', string $allow_cid = '', string $allow_gid = '', string $deny_cid = '', string $deny_gid = '')
+       public static function storeFile(string $src, int $uid, string $filename = '', string $filetype = '', string $allow_cid = '', string $allow_gid = '', string $deny_cid = '', string $deny_gid = '')
        {
                if ($filename === '') {
                        $filename = basename($src);
@@ -260,7 +261,7 @@ class Attach
 
                $data = @file_get_contents($src);
 
-               return self::store($data, $uid, $filename, '', null, $allow_cid, $allow_gid,  $deny_cid, $deny_gid);
+               return self::store($data, $uid, $filename, $filetype, null, $allow_cid, $allow_gid,  $deny_cid, $deny_gid);
        }
 
 
@@ -345,6 +346,16 @@ class Attach
                }
        }
 
+       public static function setPermissionForId(int $id, int $uid, string $str_contact_allow, string $str_circle_allow, string $str_contact_deny, string $str_circle_deny)
+       {
+               $fields = [
+                       'allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow,
+                       'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny,
+               ];
+
+               self::update($fields, ['id' => $id, 'uid' => $uid]);
+       }
+
        public static function addAttachmentToBody(string $body, int $uid): string
        {
                preg_match_all("/\[attachment\](.*?)\[\/attachment\]/ism", $body, $matches, PREG_SET_ORDER);
index 7159591821fdf0beb70986fdca16092d06566b53..7f71cc32ff566879d91b0bc242fc5b0726de10a7 100644 (file)
@@ -444,42 +444,46 @@ class Media
                        return $data;
                }
 
-               $type = explode('/', current(explode(';', $data['mimetype'])));
+               $data['type'] = self::getType($data['mimetype']);
+               return $data;
+       }
+
+       public static function getType(string $mimeType): int
+       {
+               $type = explode('/', current(explode(';', $mimeType)));
                if (count($type) < 2) {
-                       Logger::info('Unknown MimeType', ['type' => $type, 'media' => $data]);
-                       $data['type'] = self::UNKNOWN;
-                       return $data;
+                       Logger::info('Unknown MimeType', ['type' => $type, 'media' => $mimeType]);
+                       return self::UNKNOWN;
                }
 
                $filetype = strtolower($type[0]);
                $subtype = strtolower($type[1]);
 
                if ($filetype == 'image') {
-                       $data['type'] = self::IMAGE;
+                       $type = self::IMAGE;
                } elseif ($filetype == 'video') {
-                       $data['type'] = self::VIDEO;
+                       $type = self::VIDEO;
                } elseif ($filetype == 'audio') {
-                       $data['type'] = self::AUDIO;
+                       $type = self::AUDIO;
                } elseif (($filetype == 'text') && ($subtype == 'html')) {
-                       $data['type'] = self::HTML;
+                       $type = self::HTML;
                } elseif (($filetype == 'text') && ($subtype == 'xml')) {
-                       $data['type'] = self::XML;
+                       $type = self::XML;
                } elseif (($filetype == 'text') && ($subtype == 'plain')) {
-                       $data['type'] = self::PLAIN;
+                       $type = self::PLAIN;
                } elseif ($filetype == 'text') {
-                       $data['type'] = self::TEXT;
+                       $type = self::TEXT;
                } elseif (($filetype == 'application') && ($subtype == 'x-bittorrent')) {
-                       $data['type'] = self::TORRENT;
+                       $type = self::TORRENT;
                } elseif ($filetype == 'application') {
-                       $data['type'] = self::APPLICATION;
+                       $type = self::APPLICATION;
                } else {
-                       $data['type'] = self::UNKNOWN;
-                       Logger::info('Unknown type', ['filetype' => $filetype, 'subtype' => $subtype, 'media' => $data]);
-                       return $data;
+                       $type = self::UNKNOWN;
+                       Logger::info('Unknown type', ['filetype' => $filetype, 'subtype' => $subtype, 'media' => $mimeType]);
                }
 
-               Logger::debug('Detected type', ['filetype' => $filetype, 'subtype' => $subtype, 'media' => $data]);
-               return $data;
+               Logger::debug('Detected type', ['filetype' => $filetype, 'subtype' => $subtype, 'media' => $mimeType]);
+               return $type;           
        }
 
        /**
index 137a2d31c27949c407cbd0001885fa000d1cedef..f247a7a0217f9eb1b01d3b1457d12b8c230b1a5b 100644 (file)
@@ -131,12 +131,19 @@ class InstanceV2 extends BaseApi
 
                return new InstanceEntity\Configuration(
                        $statuses_config,
-                       new InstanceEntity\MediaAttachmentsConfig(Images::supportedMimeTypes(), $image_size_limit, $image_matrix_limit),
+                       new InstanceEntity\MediaAttachmentsConfig($this->supportedMimeTypes(), $image_size_limit, $image_matrix_limit),
                        new InstanceEntity\Polls(),
                        new InstanceEntity\Accounts(),
                );
        }
 
+       private function supportedMimeTypes(): array
+       {
+               $mimetypes = ['audio/aac', 'audio/flac', 'audio/mpeg', 'audio/mp4', 'audio/ogg', 'audio/wav',
+                       'audio/webm', 'video/mp4', 'video/ogg', 'video/webm'];
+               return array_merge(Images::supportedMimeTypes(), $mimetypes);
+       }
+
        private function buildContactInfo(): InstanceEntity\Contact
        {
                $email         = implode(',', User::getAdminEmailList());
index 529b5c83fbc486d768087f79dfc75f7cf527b4c0..4e762fa87aed016d3ab17a798c7563bab14b200d 100644 (file)
@@ -22,8 +22,9 @@
 namespace Friendica\Module\Api\Mastodon;
 
 use Friendica\Core\Logger;
-use Friendica\Core\System;
 use Friendica\DI;
+use Friendica\Model\Attach;
+use Friendica\Model\Contact;
 use Friendica\Model\Photo;
 use Friendica\Model\Post;
 use Friendica\Module\BaseApi;
@@ -51,14 +52,38 @@ class Media extends BaseApi
                        $this->logAndJsonError(422, $this->errorFactory->UnprocessableEntity());
                }
 
-               $media = Photo::upload($uid, $_FILES['file'], '', null, null, '', '', $request['description']);
-               if (empty($media)) {
-                       $this->logAndJsonError(422, $this->errorFactory->UnprocessableEntity());
-               }
+               $type = Post\Media::getType($_FILES['file']['type']);
+
+               if (in_array($type, [Post\Media::IMAGE, Post\Media::UNKNOWN])) {
+                       $media = Photo::upload($uid, $_FILES['file'], '', null, null, '', '', $request['description']);
+                       if (empty($media)) {
+                               $this->logAndJsonError(422, $this->errorFactory->UnprocessableEntity());
+                       }
 
-               Logger::info('Uploaded photo', ['media' => $media]);
+                       Logger::info('Uploaded photo', ['media' => $media]);
 
-               $this->jsonExit(DI::mstdnAttachment()->createFromPhoto($media['id']));
+                       $this->jsonExit(DI::mstdnAttachment()->createFromPhoto($media['id']));
+               } else {
+                       $tempFileName = $_FILES['file']['tmp_name'];
+                       $fileName     = basename($_FILES['file']['name']);
+                       $fileSize     = intval($_FILES['file']['size']);
+                       $maxFileSize  = DI::config()->get('system', 'maxfilesize');
+
+                       if ($fileSize <= 0) {
+                               @unlink($tempFileName);
+                               $this->logAndJsonError(422, $this->errorFactory->UnprocessableEntity());
+                       }
+
+                       if ($maxFileSize && $fileSize > $maxFileSize) {
+                               @unlink($tempFileName);
+                               $this->logAndJsonError(422, $this->errorFactory->UnprocessableEntity());
+                       }
+
+                       $id = Attach::storeFile($tempFileName, self::getCurrentUserID(), $fileName, $_FILES['file']['type'], '<' . Contact::getPublicIdByUserId(self::getCurrentUserID()) . '>');
+                       @unlink($tempFileName);
+                       Logger::info('Uploaded media', ['id' => $id]);
+                       $this->jsonExit(DI::mstdnAttachment()->createFromAttach($id));
+               }
        }
 
        public function put(array $request = [])
@@ -77,6 +102,10 @@ class Media extends BaseApi
                        $this->logAndJsonError(422, $this->errorFactory->UnprocessableEntity());
                }
 
+               if (DI::mstdnAttachment()->isAttach($this->parameters['id']) && Attach::exists(['id' => substr($this->parameters['id'], 7)])) {
+                       $this->jsonExit(DI::mstdnAttachment()->createFromAttach(substr($this->parameters['id'], 7)));
+               }
+
                $photo = Photo::selectFirst(['resource-id'], ['id' => $this->parameters['id'], 'uid' => $uid]);
                if (empty($photo['resource-id'])) {
                        $media = Post\Media::getById($this->parameters['id']);
@@ -108,10 +137,15 @@ class Media extends BaseApi
                }
 
                $id = $this->parameters['id'];
-               if (!Photo::exists(['id' => $id, 'uid' => $uid])) {
-                       $this->logAndJsonError(404, $this->errorFactory->RecordNotFound());
+
+               if (Photo::exists(['id' => $id, 'uid' => $uid])) {
+                       $this->jsonExit(DI::mstdnAttachment()->createFromPhoto($id));
+               }
+
+               if (DI::mstdnAttachment()->isAttach($id) && Attach::exists(['id' => substr($id, 7)])) {
+                       $this->jsonExit(DI::mstdnAttachment()->createFromAttach(substr($id, 7)));
                }
 
-               $this->jsonExit(DI::mstdnAttachment()->createFromPhoto($id));
+               $this->logAndJsonError(404, $this->errorFactory->RecordNotFound());
        }
 }
index 2c5e706e4712e3d6cc86a223463ffff5e4d311af..a9e2baa1147ea49024dbeb9e13eb684e106d2cfa 100644 (file)
@@ -28,6 +28,7 @@ use Friendica\Core\Protocol;
 use Friendica\Core\Worker;
 use Friendica\Database\DBA;
 use Friendica\DI;
+use Friendica\Model\Attach;
 use Friendica\Model\Contact;
 use Friendica\Model\Circle;
 use Friendica\Model\Item;
@@ -397,6 +398,20 @@ class Statuses extends BaseApi
                $item['attachments'] = [];
 
                foreach ($media_ids as $id) {
+                       if (DI::mstdnAttachment()->isAttach($id) && Attach::exists(['id' => substr($id, 7)])) {
+                               $attach = Attach::selectFirst([], ['id' => substr($id, 7)]);
+                               $attachment = [
+                                       'type'     => Post\Media::getType($attach['filetype']),
+                                       'mimetype' => $attach['filetype'],
+                                       'url'      => DI::baseUrl() . '/attach/' . substr($id, 7),
+                                       'size'     => $attach['filetype'],
+                                       'name'     => $attach['filename']
+                               ];
+                               $item['attachments'][] = $attachment;
+                               Attach::setPermissionForId(substr($id, 7), $item['uid'], $item['allow_cid'], $item['allow_gid'], $item['deny_cid'], $item['deny_gid']);
+                               continue;
+                       }
+
                        $media = DBA::toArray(DBA::p("SELECT `resource-id`, `scale`, `type`, `desc`, `filename`, `datasize`, `width`, `height` FROM `photo`
                                        WHERE `resource-id` IN (SELECT `resource-id` FROM `photo` WHERE `id` = ?) AND `photo`.`uid` = ?
                                        ORDER BY `photo`.`width` DESC LIMIT 2", $id, $item['uid']));
@@ -409,13 +424,16 @@ class Statuses extends BaseApi
 
                        $ext = Images::getExtensionByMimeType($media[0]['type']);
 
-                       $attachment = ['type' => Post\Media::IMAGE, 'mimetype' => $media[0]['type'],
-                               'url' => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . $ext,
-                               'size' => $media[0]['datasize'],
-                               'name' => $media[0]['filename'] ?: $media[0]['resource-id'],
+                       $attachment = [
+                               'type'        => Post\Media::IMAGE,
+                               'mimetype'    => $media[0]['type'],
+                               'url'         => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . $ext,
+                               'size'        => $media[0]['datasize'],
+                               'name'        => $media[0]['filename'] ?: $media[0]['resource-id'],
                                'description' => $media[0]['desc'] ?? '',
-                               'width' => $media[0]['width'],
-                               'height' => $media[0]['height']];
+                               'width'       => $media[0]['width'],
+                               'height'      => $media[0]['height']
+                       ];
 
                        if (count($media) > 1) {
                                $attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . $ext;
index 042c2162909c04783ef40a0567f29f84d1148d3f..f257fb245c6110b3b9b11af50463d2d18d3db6a2 100644 (file)
@@ -106,7 +106,7 @@ class Upload extends \Friendica\BaseModule
                        $this->return(401, $msg);
                }
 
-               $newid = Attach::storeFile($tempFileName, $owner['uid'], $fileName, '<' . $owner['id'] . '>');
+               $newid = Attach::storeFile($tempFileName, $_FILES['userfile']['type'] ?? '', $owner['uid'], $fileName, '<' . $owner['id'] . '>');
 
                @unlink($tempFileName);
 
index cfdb36342412b5741744e6d56cb07f348d05e121..07a47f7df6ed945a51ab0bcf0f053f46177c717a 100644 (file)
@@ -39,7 +39,7 @@ class MediaAttachmentsConfig extends BaseDataTransferObject
        /** @var int */
        protected $video_size_limit = 0;
        /** @var int */
-       protected $video_frame_rate_limit = 0;
+       protected $video_frame_rate_limit = 60;
        /** @var int */
        protected $video_matrix_limit = 0;
 
@@ -51,5 +51,7 @@ class MediaAttachmentsConfig extends BaseDataTransferObject
                $this->supported_mime_types = $supported_mime_types;
                $this->image_size_limit     = $image_size_limit;
                $this->image_matrix_limit   = $image_matrix_limit;
+               $this->video_size_limit     = $image_size_limit;
+               $this->video_matrix_limit   = $image_matrix_limit;
        }
 }
index e9065e863ec8c40fd398342e7ebc32bc9cb03178..cec1b9eded0099c9c695c9695536141453537232 100644 (file)
@@ -261,7 +261,7 @@ return [
                        '/lists/{id:\d+}'                    => [Module\Api\Mastodon\Lists::class,                    [R::GET, R::PUT, R::DELETE]],
                        '/lists/{id:\d+}/accounts'           => [Module\Api\Mastodon\Lists\Accounts::class,           [R::GET, R::POST, R::DELETE]],
                        '/markers'                           => [Module\Api\Mastodon\Markers::class,                  [R::GET, R::POST]],
-                       '/media/{id:\d+}'                    => [Module\Api\Mastodon\Media::class,                    [R::GET, R::PUT ]],
+                       '/media/{id}'                        => [Module\Api\Mastodon\Media::class,                    [R::GET, R::PUT ]],
                        '/mutes'                             => [Module\Api\Mastodon\Mutes::class,                    [R::GET         ]],
                        '/notifications'                     => [Module\Api\Mastodon\Notifications::class,            [R::GET         ]],
                        '/notifications/{id:\d+}'            => [Module\Api\Mastodon\Notifications::class,            [R::GET         ]],