execute($error); if($error !== 0) throw new \DomainException("$filename is not a valid video file"); else if(empty($streams) || ctype_space($streams)) throw new \DomainException("$filename does not contain any video streams"); $durations = []; preg_match_all('%duration=([0-9\.]++)%', $streams, $durations); if(sizeof($durations[1]) === 0) throw new \DomainException("$filename does not contain any meaningful video streams"); $length = 0; foreach($durations[1] as $duration) { $duration = floatval($duration); if($duration < 1.0) throw new \DomainException("$filename does not contain any meaningful video streams"); else $length = max($length, $duration); } $this->stateChanges("length", (int) round($length, 0, PHP_ROUND_HALF_EVEN)); preg_match('%width=([0-9\.]++)%', $streams, $width); preg_match('%height=([0-9\.]++)%', $streams, $height); if(!empty($width) && !empty($height)) { $this->stateChanges("width", $width[1]); $this->stateChanges("height", $height[1]); } try { if(!is_dir($dirId = dirname($this->pathFromHash($hash)))) mkdir($dirId); $dir = $this->getBaseDir(); $ext = Shell::isPowershell() ? "ps1" : "sh"; $cmd = Shell::isPowershell() ? "powershell" : "bash"; Shell::$cmd(__DIR__ . "/../shell/processVideo.$ext", OPENVK_ROOT, $filename, $dir, $hash)->start(); #async :DDD } catch(ShellUnavailableException $suex) { exit(OPENVK_ROOT_CONF["openvk"]["debug"] ? "Shell is unavailable" : VIDEOS_FRIENDLY_ERROR); } catch(UnknownCommandException $ucex) { exit(OPENVK_ROOT_CONF["openvk"]["debug"] ? "bash is not installed" : VIDEOS_FRIENDLY_ERROR); } usleep(200100); return true; } protected function checkIfFileIsProcessed(): bool { if($this->getType() != Video::TYPE_DIRECT) return true; if(!file_exists($this->getFileName())) { if((time() - $this->getRecord()->last_checked) > 3600) { # TODO notify that video processor is probably dead } return false; } return true; } function getName(): string { return $this->getRecord()->name; } function getType(): int { if(!is_null($this->getRecord()->hash)) return Video::TYPE_DIRECT; else if(!is_null($this->getRecord()->link)) return Video::TYPE_EMBED; } function getVideoDriver(): ?VideoDriver { if($this->getType() !== Video::TYPE_EMBED) return NULL; [$videoDriver, $pointer] = explode(":", $this->getRecord()->link); $videoDriver = "openvk\\Web\\Models\\VideoDrivers\\$videoDriver" . "VideoDriver"; if(!class_exists($videoDriver)) return NULL; return new $videoDriver($pointer); } function getThumbnailURL(): string { if($this->getType() === Video::TYPE_DIRECT) { if(!$this->isProcessed()) return "/assets/packages/static/openvk/video/rendering.apng"; return preg_replace("%\.[A-z0-9]++$%", ".gif", $this->getURL()); } else { return $this->getVideoDriver()->getThumbnailURL(); } } function getOwnerVideo(): int { return $this->getRecord()->owner; } function getApiStructure(?User $user = NULL): object { $fromYoutube = $this->getType() == Video::TYPE_EMBED; $dimensions = $this->getDimensions(); $res = (object)[ "type" => "video", "video" => [ "can_comment" => 1, "can_like" => 1, // we don't h-have wikes in videos "can_repost" => 1, "can_subscribe" => 1, "can_add_to_faves" => 0, "can_add" => 0, "comments" => $this->getCommentsCount(), "date" => $this->getPublicationTime()->timestamp(), "description" => $this->getDescription(), "duration" => $this->getLength(), "image" => [ [ "url" => $this->getThumbnailURL(), "width" => 320, "height" => 240, "with_padding" => 1 ] ], "width" => $dimensions ? $dimensions[0] : 640, "height" => $dimensions ? $dimensions[1] : 480, "id" => $this->getVirtualId(), "owner_id" => $this->getOwner()->getId(), "user_id" => $this->getOwner()->getId(), "title" => $this->getName(), "is_favorite" => false, "player" => !$fromYoutube ? $this->getURL() : $this->getVideoDriver()->getURL(), "files" => !$fromYoutube ? [ "mp4_480" => $this->getURL() ] : NULL, "platform" => $fromYoutube ? "youtube" : NULL, "added" => 0, "repeat" => 0, "type" => "video", "views" => 0, "is_processed" => $this->isProcessed(), "reposts" => [ "count" => 0, "user_reposted" => 0 ] ] ]; if(!is_null($user)) { $res->video["likes"] = [ "count" => $this->getLikesCount(), "user_likes" => $this->hasLikeFrom($user) ]; } return $res; } function toVkApiStruct(?User $user): object { return $this->getApiStructure($user); } function setLink(string $link): string { if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/youtube.txt"), $link, $matches)) { $pointer = "YouTube:$matches[1]"; /*} else if(preg_match(file_get_contents(__DIR__ . "/../VideoDrivers/regex/vimeo.txt"), $link, $matches)) { $pointer = "Vimeo:$matches[1]";*/ } else { throw new ISE("Invalid link"); } $this->stateChanges("link", $pointer); return $pointer; } function isDeleted(): bool { if ($this->getRecord()->deleted == 1) return TRUE; else return FALSE; } function deleteVideo(): void { $this->setDeleted(1); $this->unwire(); $this->save(); } static function fastMake(int $owner, string $name = "Unnamed Video.ogv", string $description = "", array $file, bool $unlisted = true, bool $anon = false): Video { if(OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading']) exit(VIDEOS_FRIENDLY_ERROR); $video = new Video; $video->setOwner($owner); $video->setName(ovk_proc_strtr($name, 61)); $video->setDescription(ovk_proc_strtr($description, 300)); $video->setAnonymous($anon); $video->setCreated(time()); $video->setFile($file); $video->setUnlisted($unlisted); $video->save(); return $video; } function fillDimensions() { $hash = $this->getRecord()->hash; $path = $this->pathFromHash($hash); if(!file_exists($path)) { $this->stateChanges("width", 0); $this->stateChanges("height", 0); $this->stateChanges("length", 0); $this->save(); return false; } $streams = Shell::ffprobe("-i", $path, "-show_streams", "-select_streams v", "-loglevel error")->execute($error); $durations = []; preg_match_all('%duration=([0-9\.]++)%', $streams, $durations); $length = 0; foreach($durations[1] as $duration) { $duration = floatval($duration); if($duration < 1.0) continue; else $length = max($length, $duration); } $this->stateChanges("length", (int) round($length, 0, PHP_ROUND_HALF_EVEN)); preg_match('%width=([0-9\.]++)%', $streams, $width); preg_match('%height=([0-9\.]++)%', $streams, $height); if(!empty($width) && !empty($height)) { $this->stateChanges("width", $width[1]); $this->stateChanges("height", $height[1]); } $this->save(); return true; } function getDimensions() { if($this->getType() == Video::TYPE_EMBED) return [320, 180]; $width = $this->getRecord()->width; $height = $this->getRecord()->height; if(!$width) return NULL; return $width != 0 ? [$width, $height] : NULL; } function getLength() { return $this->getRecord()->length; } function getFormattedLength(): string { $len = $this->getLength(); if(!$len) return "00:00"; $mins = floor($len / 60); $secs = $len - ($mins * 60); return ( str_pad((string) $mins, 2, "0", STR_PAD_LEFT) . ":" . str_pad((string) $secs, 2, "0", STR_PAD_LEFT) ); } function canBeViewedBy(?User $user = NULL): bool { if($this->isDeleted() || $this->getOwner()->isDeleted()) { return false; } if(get_class($this->getOwner()) == "openvk\\Web\\Models\\Entities\\User") { return $this->getOwner()->canBeViewedBy($user) && $this->getOwner()->getPrivacyPermission('videos.read', $user); } else { # Groups doesn't have videos but ok return $this->getOwner()->canBeViewedBy($user); } } function toNotifApiStruct() { $fromYoutube = $this->getType() == Video::TYPE_EMBED; $res = (object)[]; $res->id = $this->getVirtualId(); $res->owner_id = $this->getOwner()->getId(); $res->title = $this->getName(); $res->description = $this->getDescription(); $res->duration = $this->getLength(); $res->link = "/video".$this->getOwner()->getId()."_".$this->getVirtualId(); $res->image = $this->getThumbnailURL(); $res->date = $this->getPublicationTime()->timestamp(); $res->views = 0; $res->player = !$fromYoutube ? $this->getURL() : $this->getVideoDriver()->getURL(); return $res; } }