Proyectos de Subversion LeadersLinked - Backend

Rev

Autoría | Ultima modificación | Ver Log |

<?php

namespace LeadersLinked\Library;

use LeadersLinked\Model\User;
use LeadersLinked\Model\UserProfile;
use LeadersLinked\Model\Company;
use LeadersLinked\Model\Group;

class Storage
{

    const FILE_TYPE_IMAGE = 'image';

    const FILE_TYPE_VIDEO = 'video';

    const FILE_TYPE_DOCUMENT = 'document';

    const TYPE_DEFAULT = 'default';

    const TYPE_CHAT = 'chat';

    const TYPE_GROUP = 'group';

    const TYPE_USER = 'user';

    const TYPE_IMAGE = 'image';

    const TYPE_JOB = 'job';

    const TYPE_MEDIA = 'media';

    const TYPE_COMPANY = 'company';

    const TYPE_FEED = 'feed';

    const TYPE_POST = 'post';

    const TYPE_MICROLEARNING_TOPIC = 'topic';

    const TYPE_MICROLEARNING_CAPSULE = 'capsule';

    const TYPE_MICROLEARNING_SLIDE = 'slide';

    const TYPE_JOB_DESCRIPTION = 'jobdesc';

    const TYPE_SELF_EVALUATION = 'selfval';

    const TYPE_PERFORMANCE_EVALUATION = 'perfeva';

    const TYPE_RECRUITMENT_SELECTION = 'recrsel';

    const TYPE_PLANNING_OBJECTIVES_AND_GOALS = 'plannig';

    const TYPE_MESSAGE = 'message';

    const TYPE_SURVEY = 'survey';

    const TYPE_NETWORK = 'network';

    const TYPE_DAILY_PULSE = 'dailpuls';

    const TYPE_ENGAGEMENT_REWARD = 'engarewr';

    const TYPE_KNOWLEDGE_AREA = 'knowarea';

    const TYPE_MY_COACH = 'mycoach';

    const TYPE_HABIT_EMOJI = 'habit-emoji';

    const TYPE_HABIT_CONTENT = 'habit-content';

    /**
     *
     * @var\LeadersLinked\Library\Storage
     */
    private static $_instance;

    /**
     *
     * @var array
     */
    private $config;

    /**
     *
     * @var \Laminas\Db\Adapter\AdapterInterface
     */
    private $adapter;

    /**
     *
     * @var string
     */
    private $tempPath;

    /**
     *
     * @var string
     */
    private $storagePath;

    /**
     *
     * @var array
     */
    private $currentFile;


    /**
     * 
     * @var array
     */
    private $files;

    /**
     *
     * @param array $config
     * @param \Laminas\Db\Adapter\AdapterInterface $adapter
     */
    private function __construct($config, $adapter)
    {
        $this->config = $config;
        $this->adapter = $adapter;
        $this->currentFile = [];

        $this->storagePath = 'data' . DIRECTORY_SEPARATOR . 'storage';
        if (! file_exists($this->storagePath)) {
            mkdir($this->storagePath, 0775, true);
        }

        $this->tempPath = 'data' . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'tmp';

        if (! file_exists($this->tempPath)) {
            mkdir($this->tempPath, 0775, true);
        }
    }

    /**
     *
     * @param array $config
     * @param \Laminas\Db\Adapter\AdapterInterface $adapter
     * @return \LeadersLinked\Library\Storage
     */
    public static function getInstance($config, $adapter)
    {
        if (self::$_instance == null) {
            self::$_instance = new Storage($config, $adapter);
        }

        return self::$_instance;
    }

    /**
     *
     * @return string
     */
    public function getFullPathImageDefault()
    {
        return $this->storagePath . DIRECTORY_SEPARATOR . 'no-image.jpg';
    }

    /**
     *
     * @return string
     */
    public function getFullPathImageUserDefault()
    {
        return $this->storagePath . DIRECTORY_SEPARATOR . 'no-image-user.jpg';
    }

    /**
     *
     * @return string
     */
    public function getFullPathImageUserPofileDefault()
    {
        return $this->storagePath . DIRECTORY_SEPARATOR . 'no-image-user-profile.png';
    }

    /**
     *
     * @return string
     */
    public function getFullPathImageUserCoverDefault()
    {
        return $this->storagePath . DIRECTORY_SEPARATOR . 'no-image-cover.jpg';
    }

    /**
     *
     * @return string
     */
    public function getFullPathImageCompanyDefault()
    {
        return $this->storagePath . DIRECTORY_SEPARATOR . 'no-image-company.jpg';
    }

    /**
     *
     * @return string
     */
    public function getFullPathImageCompanyCoverDefault()
    {
        return $this->storagePath . DIRECTORY_SEPARATOR . 'no-image-cover.jpg';
    }

    /**
     *
     * @return string
     */
    public function getFullPathImageGroupDefault()
    {
        return $this->storagePath . DIRECTORY_SEPARATOR . 'no-image-group.jpg';
    }

    /**
     *
     * @return string
     */
    public function getFullPathImageGroupCoverDefault()
    {
        return $this->storagePath . DIRECTORY_SEPARATOR . 'no-image-cover.jpg';
    }

    /**
     *
     * @return string
     */
    public function getFullPathImageCompanyHeaderPdfDefault()
    {
        return $this->storagePath . DIRECTORY_SEPARATOR . 'default' . DIRECTORY_SEPARATOR . 'company-header.png';
    }

    /**
     *
     * @return string
     */
    public function getFullPathImageCompanyFooterPdfDefault()
    {
        return $this->storagePath . DIRECTORY_SEPARATOR . 'default' . DIRECTORY_SEPARATOR . 'company-footer.png';
    }

    /**
     *
     * @return string
     */
    public function getStoagePath()
    {
        return $this->storagePath;
    }

    /**
     *
     * @return string
     */
    public function getTempPath()
    {
        return $this->tempPath;
    }

    /**
     *
     * @return array
     */
    public function getCurrentFile()
    {
        return $this->currentFile;
    }

    /**
     *
     * @param array $currentFile
     */
    public function setCurrentFile($currentFile)
    {
        $this->currentFile = $currentFile;
    }

    /**
     *
     * @param array $filename
     */
    public function setCurrentFilename($filename)
    {
        if (empty($this->files)) {
            if (isset($_FILES[$filename]) && empty($_FILES[$filename]['error'])) {
                $this->currentFile = $_FILES[$filename];
            } else {
                $this->currentFile = [];
            }
        } else {
            if (isset($this->files[$filename]) && empty($this->files[$filename]['error'])) {
                $this->currentFile = $this->files[$filename];
            } else {
                $this->currentFile = [];
            }
        }

        return !empty($this->currentFile);
    }

    /**
     * 
     * @return boolean
     */
    public function hasFile()
    {
        return !empty($this->currentFile);
    }

    /**
     * 
     * @param array $files
     */
    public function setFiles($files)
    {
        $this->files = $files;
    }

    /**
     *
     * @param string $type
     * @param string $code
     * @param string $filename
     * @return string
     */
    public function composePathToFilename($type, $code, $filename)
    {
        $path = $this->getPathByType($type);
        $path = $path . DIRECTORY_SEPARATOR . $code;
        if (! file_exists($path)) {
            mkdir($path, 0775, true);
        }
        return $path . DIRECTORY_SEPARATOR . $filename;
    }

    /**
     *
     * @param string $type
     * @param string $code
     * @return string
     */
    public function composePathToDirectory($type, $code)
    {
        $path = $this->getPathByType($type);
        $path = $path . DIRECTORY_SEPARATOR . $code;
        if (! file_exists($path)) {
            mkdir($path, 0775, true);
        }
        return $path;
    }

    /**
     * Private helper to get a presigned URL for an entity's image/cover or its default.
     *
     * @param string $entityType The type of the entity (e.g., self::TYPE_USER).
     * @param string $entityUuid The UUID of the entity.
     * @param string|null $imageFilename The filename of the image/cover, if it exists.
     * @param string $defaultImageMethodName The name of the method in this class to get the full path to the default image.
     * @return string The presigned URL.
     */
    private function _getProcessedImageOrCoverUrl($entityType, $entityUuid, $imageFilename, $defaultImageMethodName)
    {
        if ($imageFilename) {
            $remoto = $this->composePathToFilename($entityType, $entityUuid, $imageFilename);
            if (file_exists($remoto)) {
                return $this->getPresignedUrl($remoto);
            }
        }
        // If imageFilename is null/empty, or the specific file doesn't exist, use the default.
        $defaultFullPath = $this->$defaultImageMethodName();
        return $this->getPresignedUrl($defaultFullPath);
    }

    /**
     *
     * @param User $user
     * @return string
     */
    public function getUserImage($user)
    {
        return $this->_getProcessedImageOrCoverUrl(self::TYPE_USER, $user->uuid, $user->image, 'getFullPathImageUserDefault');
    }

    /**
     *
     * @param User $user
     * @return string
     */
    public function getUserImageForCodeAndFilename($code, $filename)
    {
        return $this->_getProcessedImageOrCoverUrl(self::TYPE_USER, $code, $filename, 'getFullPathImageUserDefault');
    }

    /**
     *
     * @param User $user
     * @param UserProfile $userProfile
     * @return string
     */
    public function getUserProfileImage($user, $userProfile)
    {
        return $this->_getProcessedImageOrCoverUrl(self::TYPE_USER, $user->uuid, $userProfile->image, 'getFullPathImageUserPofileDefault');
    }

    /**
     *
     * @param User $user
     * @param UserProfile $userProfile
     * @return string
     */
    public function getUserProfileCover($user, $userProfile)
    {
        return $this->_getProcessedImageOrCoverUrl(self::TYPE_USER, $user->uuid, $userProfile->cover, 'getFullPathImageUserCoverDefault');
    }

    /**
     *
     * @param Company $company
     * @return string
     */
    public function getCompanyImage($company)
    {
        return $this->_getProcessedImageOrCoverUrl(self::TYPE_COMPANY, $company->uuid, $company->image, 'getFullPathImageCompanyDefault');
    }

    /**
     *
     * @param string $code
     * @param string $filename
     * @return string
     */
    public function getCompanyImageForCodeAndFilename($code, $filename)
    {
        return $this->_getProcessedImageOrCoverUrl(self::TYPE_COMPANY, $code, $filename, 'getFullPathImageCompanyDefault');
    }

    /**
     *
     * @param Company $company
     * @return string
     */
    public function getCompanyCover($company)
    {
        return $this->_getProcessedImageOrCoverUrl(self::TYPE_COMPANY, $company->uuid, $company->cover, 'getFullPathImageCompanyCoverDefault');
    }

    /**
     *
     * @param Group $group
     * @return string
     */
    public function getGroupImage($group)
    {
        return $this->_getProcessedImageOrCoverUrl(self::TYPE_GROUP, $group->uuid, $group->image, 'getFullPathImageGroupDefault');
    }

    /**
     *
     * @param string $code
     * @param string $filename
     * @return string
     */
    public function getGroupImageForCodeAndFilename($code, $filename)
    {
        return $this->_getProcessedImageOrCoverUrl(self::TYPE_COMPANY, $code, $filename, 'getFullPathImageGroupDefault');
    }

    /**
     *
     * @param Group $group
     * @return string
     */
    public function getGroupCover($group)
    {
        return $this->_getProcessedImageOrCoverUrl(self::TYPE_COMPANY, $group->uuid, $group->cover, 'getFullPathImageGroupCoverDefault');
    }

    /**
     *
     * @param Group $group
     * @return string
     */
    public function getGenericImage($path, $code, $filename)
    {
        $remoto = $path . DIRECTORY_SEPARATOR . $code . DIRECTORY_SEPARATOR . $filename;

        if (file_exists($remoto)) {
            return $this->getPresignedUrl($remoto);
        }

        $remoto = $this->getFullPathImageDefault();
        return $this->getPresignedUrl($remoto);
    }

    /**
     *
     * @param string $path
     * @param string $code,
     * @param string $filename
     * @return string
     */
    public function getGenericFile($path, $code, $filename)
    {
        $remoto = $path . DIRECTORY_SEPARATOR . $code . DIRECTORY_SEPARATOR . $filename;



        if (file_exists($remoto)) {
            if ($code == 'd8e6c5de-52a6-4c28-bdce-4ba2823ba841') {

                error_log('getGenericFile = ' . $remoto);
            }


            return $this->getPresignedUrl($remoto);
        }
        return;
    }

    public function getPathDefault()
    {
        return $this->getPathByType(self::TYPE_DEFAULT);
    }

    public function getPathChat()
    {
        return $this->getPathByType(self::TYPE_CHAT);
    }

    public function getPathGroup()
    {
        return $this->getPathByType(self::TYPE_GROUP);
    }

    public function getPathUser()
    {
        return $this->getPathByType(self::TYPE_USER);
    }

    public function getPathImage()
    {
        return $this->getPathByType(self::TYPE_IMAGE);
    }

    public function getPathJob()
    {
        return $this->getPathByType(self::TYPE_JOB);
    }

    public function getPathCompany()
    {
        return $this->getPathByType(self::TYPE_COMPANY);
    }


    public function getPathFeed()
    {
        return $this->getPathByType(self::TYPE_FEED);
    }

    public function getPathMedia()
    {
        return $this->getPathByType(self::TYPE_MEDIA);
    }

    public function getPathPost()
    {
        return $this->getPathByType(self::TYPE_POST);
    }


    public function getPathMicrolearningTopic()
    {
        return $this->getPathByType(self::TYPE_MICROLEARNING_TOPIC);
    }

    public function getPathMicrolearningCapsule()
    {
        return $this->getPathByType(self::TYPE_MICROLEARNING_CAPSULE);
    }

    public function getPathMicrolearningSlide()
    {
        return $this->getPathByType(self::TYPE_MICROLEARNING_SLIDE);
    }

    public function getPathJobDescription()
    {
        return $this->getPathByType(self::TYPE_JOB_DESCRIPTION);
    }

    public function getPathSelfEvaluation()
    {
        return $this->getPathByType(self::TYPE_SELF_EVALUATION);
    }

    public function getPathPerformanceEvaluation()
    {
        return $this->getPathByType(self::TYPE_PERFORMANCE_EVALUATION);
    }


    public function getPathRecruitmentSelection()
    {
        return $this->getPathByType(self::TYPE_RECRUITMENT_SELECTION);
    }


    public function getPathPlanningObjectivesAndGoals()
    {
        return $this->getPathByType(self::TYPE_PLANNING_OBJECTIVES_AND_GOALS);
    }

    public function getPathMessage()
    {
        return $this->getPathByType(self::TYPE_MESSAGE);
    }


    public function getPathSurvey()
    {
        return $this->getPathByType(self::TYPE_SURVEY);
    }


    public function getPathNetwork()
    {
        return $this->getPathByType(self::TYPE_NETWORK);
    }


    public function getPathDailyPulse()
    {
        return $this->getPathByType(self::TYPE_DAILY_PULSE);
    }


    public function getPathEngagementReward()
    {
        return $this->getPathByType(self::TYPE_ENGAGEMENT_REWARD);
    }

    public function getPathKnowledgeArea()
    {
        return $this->getPathByType(self::TYPE_KNOWLEDGE_AREA);
    }

    public function getPathMyCoach()
    {
        return $this->getPathByType(self::TYPE_MY_COACH);
    }

    public function getPathHabitEmoji()
    {
        return $this->getPathByType(self::TYPE_HABIT_EMOJI);
    }

    public function getPathHabitContent()
    {
        return $this->getPathByType(self::TYPE_HABIT_CONTENT);
    }


    public function getPathByType($type)
    {
        switch ($type) {
            case self::TYPE_DEFAULT:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'default';
                break;

            case self::TYPE_CHAT:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'chat';
                break;

            case self::TYPE_GROUP:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'group';
                break;

            case self::TYPE_USER:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'user';
                break;

            case self::TYPE_IMAGE:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'image';
                break;

            case self::TYPE_JOB:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'job';
                break;

            case self::TYPE_COMPANY:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'company';
                break;

            case self::TYPE_FEED:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'feed';
                break;

            case self::TYPE_MEDIA:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'media';
                break;

            case self::TYPE_POST:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'post';
                break;

            case self::TYPE_MICROLEARNING_TOPIC:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'microlearning' . DIRECTORY_SEPARATOR . 'topic';
                break;

            case self::TYPE_MICROLEARNING_CAPSULE:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'microlearning' . DIRECTORY_SEPARATOR . 'capsule';
                break;

            case self::TYPE_MICROLEARNING_SLIDE:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'microlearning' . DIRECTORY_SEPARATOR . 'slide';
                break;

            case self::TYPE_JOB_DESCRIPTION:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'job-description';
                break;

            case self::TYPE_SELF_EVALUATION:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'self-evaluation';
                break;

            case self::TYPE_PERFORMANCE_EVALUATION:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'performance-evaluation';
                break;

            case self::TYPE_RECRUITMENT_SELECTION:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'recruitment-selection';
                break;

            case self::TYPE_PLANNING_OBJECTIVES_AND_GOALS:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'planning-objectives-and-goals';
                break;

            case self::TYPE_MESSAGE:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'message';
                break;

            case self::TYPE_SURVEY:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'survey';
                break;

            case self::TYPE_NETWORK:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'network';
                break;

            case self::TYPE_DAILY_PULSE:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'daily-pulse';
                break;

            case self::TYPE_ENGAGEMENT_REWARD:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'engagement-reward';
                break;

            case self::TYPE_KNOWLEDGE_AREA:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'knowledge-area';
                break;

            case self::TYPE_MY_COACH:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'my-coach';
                break;

            case self::TYPE_HABIT_EMOJI:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'habit' . DIRECTORY_SEPARATOR . 'emoji';
                break;

            case self::TYPE_HABIT_CONTENT:
                $path = $this->storagePath . DIRECTORY_SEPARATOR . 'habit' . DIRECTORY_SEPARATOR . 'content';
                break;

            default:
                $path = $this->storagePath;
                break;
        }

        if (! file_exists($path)) {
            mkdir($path, 0775, true);
        }

        return $path;
    }

    /**
     *
     * @param string $path
     * @param string $code
     * @param string $filename
     * @return string
     */
    public function deleteFile($path, $code, $filename)
    {
        $remoto = $path . DIRECTORY_SEPARATOR . $code . DIRECTORY_SEPARATOR . $filename;
        if (file_exists($remoto)) {
            return unlink($remoto);
        } else {
            return true;
        }
    }

    /**
     *
     * @param string $type
     * @param string $code
     * @return boolean
     */
    public function deleteDirectory($type, $code)
    {
        $path = $this->getPathByType($type);
        $remoto = $path . DIRECTORY_SEPARATOR . $code;
        if (file_exists($remoto)) {
            $this->deleteDirectoryRecursive($remoto);
            return true;
        } else {
            return true;
        }
    }

    /**
     *
     * @param string $dir
     */
    private function deleteDirectoryRecursive($dir)
    {
        if (is_dir($dir)) {
            $objects = scandir($dir);
            foreach ($objects as $object) {
                if ($object != '.' && $object != '..') {
                    $s = $dir . DIRECTORY_SEPARATOR . $object;
                    if (is_dir($s) && ! is_link($s)) {
                        $this->deleteDirectoryRecursive($s);
                    } else {
                        unlink($s);
                    }
                }
            }
            rmdir($dir);
        }
    }

    /**
     *
     * @param string $path
     * @param string $code
     * @param string $local_filename
     * @return boolean
     */
    public function putFile($source_file, $target_file)
    {
        return rename($source_file, $target_file);
    }

    /**
     *
     * @param string $tempfile
     * @param string $path
     * @param string $code
     * @param string $filename
     * @return boolean
     */
    public function moveUploadedFile($source_file, $target_file)
    {
        return move_uploaded_file($source_file, $target_file);
    }

    /**
     *
     * @param string $tempfile
     * @param string $path
     * @param string $code
     * @param string $filename
     * @return boolean
     */
    public function copyFile($source_file, $target_file)
    {
        return copy($source_file, $target_file);
    }


    /**
     *
     * @param string $url
     * @return string
     */
    public function getPresignedUrl($url)
    {
        $code = hash('sha256', $url);

        $storageFileMapper = \LeadersLinked\Mapper\StorageFileMapper::getInstance($this->adapter);
        $storageFile = $storageFileMapper->fetchOneByCode($code);
        if (! $storageFile) {
            $storageFile = new \LeadersLinked\Model\StorageFile();
            $storageFile->code = $code;
            $storageFile->path = $url;
            $storageFile->salt = \LeadersLinked\Library\Functions::generatePassword(8);



            $storageFileMapper->insert($storageFile);
        }


        $url = 'https://' . $_SERVER['SERVER_NAME'] . '/storage/' . $code;

        return $url;
    }

    /**
     *
     * @return @return void|string
     */
    public function getFilenamePNG()
    {
        if ($this->currentFile) {
            $filename = $this->normalizeStringFilename($this->currentFile['name']);
            $path_parts = pathinfo($filename);
            return $path_parts['filename'] . '.png';
        } else {
            return;
        }
    }

    /**
     *
     * @return @return void|string
     */
    public function getTmpFilename()
    {
        if ($this->currentFile) {
            return $this->currentFile['tmp_name'];
        } else {
            return;
        }
    }

    /**
     *
     * @return @return void|string
     */
    public function getFilename()
    {
        if ($this->currentFile) {
            return $this->normalizeStringFilename($this->currentFile['name']);
        } else {
            return;
        }
    }

    /**
     *
     * @return void|string
     */
    public function getFileType()
    {
        if ($this->currentFile) {
            $tmp_name = $this->currentFile['tmp_name'];
            $mime_type = mime_content_type($tmp_name);
            if ($mime_type == 'image/jpg' || $mime_type == 'image/jpeg' || $mime_type == 'image/png') {
                return self::FILE_TYPE_IMAGE;
            } else if ($mime_type == 'video/quicktime' || $mime_type == 'video/webm' || $mime_type == 'video/mpeg' || $mime_type == 'video/mpg' || $mime_type == 'video/mp4') {
                return self::FILE_TYPE_VIDEO;
            } else if ($mime_type == 'application/pdf') {
                return self::FILE_TYPE_DOCUMENT;
            } else {
                return;
            }
        } else {
            return;
        }
    }

    /**
     *
     * @return void|string
     */
    public function getExtension()
    {
        if ($this->currentFile) {
            $path_parts = pathinfo($this->currentFile['name']);
            $ext = $path_parts['extension'];
            return $ext;
        } else {
            return;
        }
    }

    /**
     * Creates a new GD image resource with transparency settings for PNG.
     * @param int $width The width of the new image.
     * @param int $height The height of the new image.
     * @return \GdImage|false The image resource or false on failure.
     */
    private function _createImageCanvas($width, $height)
    {
        $new_image = imageCreateTrueColor($width, $height);
        if ($new_image === false) {
            error_log("_createImageCanvas: imageCreateTrueColor failed for dimensions {$width}x{$height}.");
            return false;
        }
        imageAlphaBlending($new_image, False);
        imageSaveAlpha($new_image, True);
        $transparent = imageColorAllocateAlpha($new_image, 0, 0, 0, 127);
        if ($transparent === false) {
            error_log("_createImageCanvas: imageColorAllocateAlpha failed.");
            imagedestroy($new_image);
            return false;
        }
        imagefill($new_image, 0, 0, $transparent);
        return $new_image;
    }

    /**
     * Saves a GD image resource as PNG, unlinks source, and cleans up image resource.
     * @param \GdImage $imageResource The GD image resource to save.
     * @param string $targetFilename The path to save the PNG file to.
     * @param string $sourceFilenameToUnlink The path to the source file to unlink.
     * @return bool True on success, false on failure.
     */
    private function _savePngAndCleanup($imageResource, $targetFilename, $sourceFilenameToUnlink)
    {
        $save_success = imagepng($imageResource, $targetFilename);
        imagedestroy($imageResource); // Clean up the image resource

        if ($save_success) {
            if ($sourceFilenameToUnlink && file_exists($sourceFilenameToUnlink)) {
                unlink($sourceFilenameToUnlink);
            }
            return true;
        } else {
            error_log("_savePngAndCleanup: imagepng failed to save image to {$targetFilename}.");
            return false;
        }
    }

    /**
     *
     * @param string $source
     * @param string $target_filename
     * @param number $target_width
     * @param number $target_height
     * @return boolean
     */
    public function uploadImageResize($source_filename, $target_filename, $target_width, $target_height)
    {
        try {
            $data = file_get_contents($source_filename);
            $img = imagecreatefromstring($data);

            if ($img) {
                list($source_width, $source_height) = getimagesize($source_filename);

                $width_ratio = $target_width / $source_width;
                $height_ratio = $target_height / $source_height;

                if ($width_ratio > $height_ratio) {
                    $resized_width = $target_width;
                    $resized_height = $source_height * $width_ratio;
                } else {
                    $resized_height = $target_height;
                    $resized_width = $source_width * $height_ratio;
                }

                $resized_width = round($resized_width);
                $resized_height = round($resized_height);

                $new_image = $this->_createImageCanvas($target_width, $target_height);
                if ($new_image === false) {
                    imagedestroy($img);
                    unlink($source_filename);
                    return false;
                }

                imageCopyResampled($new_image, $img, 0, 0, 0, 0, $resized_width, $resized_height, $source_width, $source_height);
                imagedestroy($img); // Source image resource can be destroyed after resampling

                return $this->_savePngAndCleanup($new_image, $target_filename, $source_filename);
            }

            unlink($source_filename);
            return false;
        } catch (\Throwable $e) {
            error_log($e->getTraceAsString());
            if (isset($img) && ($img instanceof \GdImage || is_resource($img))) {
                imagedestroy($img);
            }
            if (isset($new_image) && ($new_image instanceof \GdImage || is_resource($new_image))) {
                imagedestroy($new_image);
            }
            if (isset($source_filename) && file_exists($source_filename)) {
                unlink($source_filename);
            }
            return false;
        }
    }


    /**
     *
     * @param string $source
     * @param string $target_filename
     * @param number $target_width
     * @param number $target_height
 
     * @return boolean
     */
    public function uploadImageCrop($source_filename, $target_filename, $target_width, $target_height)
    {
        try {
            $data = file_get_contents($source_filename);
            $img = imagecreatefromstring($data);

            if ($img) {
                list($source_width, $source_height) = getimagesize($source_filename);

                $width_ratio = $target_width / $source_width;
                $height_ratio = $target_height / $source_height;
                if ($width_ratio > $height_ratio) {
                    $resized_width = $target_width;
                    $resized_height = $source_height * $width_ratio;
                } else {
                    $resized_height = $target_height;
                    $resized_width = $source_width * $height_ratio;
                }

                $resized_width = round($resized_width);
                $resized_height = round($resized_height);

                $offset_width = round(($target_width - $resized_width) / 2);
                $offset_height = round(($target_height - $resized_height) / 2);

                $new_image = $this->_createImageCanvas($target_width, $target_height);
                if ($new_image === false) {
                    imagedestroy($img);
                    unlink($source_filename);
                    return false;
                }

                imageCopyResampled($new_image, $img, $offset_width, $offset_height, 0, 0, $resized_width, $resized_height, $source_width, $source_height);
                imagedestroy($img);

                return $this->_savePngAndCleanup($new_image, $target_filename, $source_filename);
            }

            unlink($source_filename);
            return false;
        } catch (\Throwable $e) {
            error_log($e->getTraceAsString());
            if (isset($img) && ($img instanceof \GdImage || is_resource($img))) {
                imagedestroy($img);
            }
            if (isset($new_image) && ($new_image instanceof \GdImage || is_resource($new_image))) {
                imagedestroy($new_image);
            }
            if (isset($source_filename) && file_exists($source_filename)) {
                unlink($source_filename);
            }
            return false;
        }
    }

    /**
     *
     * @param string $source
     * @param string $target_filename
     * @return boolean
     */
    public function uploadImageWithOutChangeSize($source_filename, $target_filename)
    {
        try {
            $data = file_get_contents($source_filename);
            $img = imagecreatefromstring($data);

            if ($img) {
                list($source_width, $source_height) = getimagesize($source_filename);

                $new_image = $this->_createImageCanvas($source_width, $source_height);
                if ($new_image === false) {
                    imagedestroy($img);
                    unlink($source_filename);
                    return false;
                }

                imageCopyResampled($new_image, $img, 0, 0, 0, 0, $source_width, $source_height, $source_width, $source_height);
                imagedestroy($img);

                return $this->_savePngAndCleanup($new_image, $target_filename, $source_filename);
            }

            unlink($source_filename);
            return false;
        } catch (\Throwable $e) {
            error_log($e->getTraceAsString());
            if (isset($img) && ($img instanceof \GdImage || is_resource($img))) {
                imagedestroy($img);
            }
            if (isset($new_image) && ($new_image instanceof \GdImage || is_resource($new_image))) {
                imagedestroy($new_image);
            }
            if (isset($source_filename) && file_exists($source_filename)) {
                unlink($source_filename);
            }
            return false;
        }
    }

    public static function extractPosterFromVideo($source_filename, $target_filename)
    {

        try {
            $cmd        = "/usr/bin/ffprobe -v error -of flat=s=_ -select_streams v:0 -show_entries stream=height,width,duration  $source_filename";
            $response   = trim(shell_exec($cmd));

            $source_duration = 0;
            $lines = explode("\n", $response);
            foreach ($lines as $line) {
                $line = trim(strtolower($line));
                if (strpos($line, 'duration') !== false) {
                    $values = explode('=', $line);
                    $source_duration = intval(str_replace($values[1], '#', ''), 10);
                }
            }


            if ($source_duration == 0) {
                $second_extract = '00:00:02';
            } else {
                if ($source_duration > 10) {
                    $second_extract = '00:00:10';
                } else {
                    $second_extract = '00:00:02';
                }
            }

            $cmd = "/usr/bin/ffmpeg -y -i $source_filename  -pix_fmt yuvj422p -an -ss $second_extract -f mjpeg -t 1 -r 1 -y  $target_filename";
            exec($cmd);

            return true;
        } catch (\Throwable $e) {
            error_log($e->getTraceAsString());

            return false;
        }
    }

    /**
     *
     * @param string $str
     * @return string
     */
    public function normalizeStringFilename($str = '')
    {
        $basename = substr($str, 0, strrpos($str, '.'));
        $basename = str_replace('.', '-', $basename);

        $extension = substr($str, strrpos($str, '.'));

        $str = $basename . $extension;

        $str = strip_tags($str);
        $str = preg_replace('/[\r\n\t ]+/', ' ', $str);
        $str = preg_replace('/[\#\"\*\/\:\<\>\?\'\|\,]+/', ' ', $str);
        $str = strtolower($str);
        $str = html_entity_decode($str, ENT_QUOTES, "utf-8");
        $str = htmlentities($str, ENT_QUOTES, "utf-8");
        $str = preg_replace("/(&)([a-z])([a-z]+;)/i", '$2', $str);
        $str = str_replace(' ', '-', $str);
        $str = rawurlencode($str);
        $str = str_replace('%', '-', $str);
        $str = str_replace([
            '-----',
            '----',
            '---',
            '--'
        ], '-', $str);
        return trim(strtolower($str));
    }
}