Add something related with videos

- Теперь видосы работают как аудио, пользователи могут добавлять и удалять видео из коллекции. Но, правда, после обновления пользователи потеряют все свои видео, потом подумаю как исправить
- Ещё теперь видео можно загружать в группу, жесть. И на странице группы теперь показывается 2 случайных видео из группы
- Возможно, исправлена загрузка видео под виндовс (а может я её сломал)
- У видосов теперь сохраняется ширина и высота, а так же длина
- У прикреплённого видео рядом с названием показывается его длина
- Видео теперь размещаются в masonry layout. Если помимо видео у поста есть другие фотографии или другие видео, то показывается только обложка видео и кнопка проигрывания
- В класс video в api добавлена поддержка просмотра видеозаписей из групп
This commit is contained in:
lalka2018 2023-11-21 20:14:38 +03:00
parent 93bfe6e886
commit 791c36416d
20 changed files with 365 additions and 69 deletions

View file

@ -36,21 +36,22 @@ final class Video extends VKAPIRequestHandler
];
} else {
if ($owner_id > 0)
$user = (new UsersRepo)->get($owner_id);
$owner = (new UsersRepo)->get($owner_id);
else
$this->fail(1, "Not implemented");
$owner = (new ClubsRepo)->get(abs($owner_id));
if(!$user->getPrivacyPermission('videos.read', $this->getUser())) {
if(!$owner)
$this->fail(20, "Invalid user");
if($owner_id > 0 && !$owner->getPrivacyPermission('videos.read', $this->getUser()))
$this->fail(20, "Access denied: this user chose to hide his videos");
}
$videos = (new VideosRepo)->getByUser($user, $offset + 1, $count);
$videosCount = (new VideosRepo)->getUserVideosCount($user);
$videos = (new VideosRepo)->getByEntityId($owner_id, $offset, $count);
$videosCount = (new VideosRepo)->getVideosCountByEntityId($owner_id);
$items = [];
foreach ($videos as $video) {
foreach ($videos as $video)
$items[] = $video->getApiStructure($this->getUser());
}
return (object) [
"count" => $videosCount,

View file

@ -414,6 +414,11 @@ class Club extends RowModel
return (bool) $this->getRecord()->everyone_can_upload_audios;
}
function isEveryoneCanUploadVideos(): bool
{
return false;
}
function canUploadAudio(?User $user): bool
{
if(!$user)
@ -422,6 +427,14 @@ class Club extends RowModel
return $this->isEveryoneCanUploadAudios() || $this->canBeModifiedBy($user);
}
function canUploadVideo(?User $user): bool
{
if(!$user)
return NULL;
return $this->isEveryoneCanUploadAudios() || $this->canBeModifiedBy($user);
}
function getAudiosCollectionSize()
{
return (new \openvk\Web\Models\Repositories\Audios)->getClubCollectionSize($this);

View file

@ -1,6 +1,6 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Entities\Traits;
use openvk\Web\Models\Entities\{Attachable, Photo};
use openvk\Web\Models\Entities\{Attachable, Photo, Video};
use openvk\Web\Util\Makima\Makima;
use Chandler\Database\DatabaseConnection;
@ -36,10 +36,10 @@ trait TAttachmentHost
if($h < 0)
$h = $w;
$children = $this->getChildren();
$children = iterator_to_array($this->getChildren());
$skipped = $photos = $result = [];
foreach($children as $child) {
if($child instanceof Photo) {
if($child instanceof Photo || $child instanceof Video && $child->getDimensions()) {
$photos[] = $child;
continue;
}
@ -68,6 +68,7 @@ trait TAttachmentHost
"height" => $height . "px",
"tiles" => $result,
"extras" => $skipped,
"count" => sizeof($children),
];
}

View file

@ -4,6 +4,7 @@ use openvk\Web\Util\Shell\Shell;
use openvk\Web\Util\Shell\Exceptions\{ShellUnavailableException, UnknownCommandException};
use openvk\Web\Models\VideoDrivers\VideoDriver;
use Nette\InvalidStateException as ISE;
use Chandler\Database\DatabaseConnection;
define("VIDEOS_FRIENDLY_ERROR", "Uploads are disabled on this instance :<", false);
@ -34,9 +35,24 @@ class Video extends Media
if(sizeof($durations[1]) === 0)
throw new \DomainException("$filename does not contain any meaningful video streams");
foreach($durations[1] as $duration)
if(floatval($duration) < 1.0)
$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("width", $height[1]);
}
try {
if(!is_dir($dirId = dirname($this->pathFromHash($hash))))
@ -45,7 +61,11 @@ class Video extends Media
$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
if($cmd == "bash")
Shell::$cmd(__DIR__ . "/../shell/processVideo.$ext", OPENVK_ROOT, $filename, $dir, $hash)->start(); # async :DDD
else
Shell::$cmd(__DIR__ . "/../shell/processVideo.$ext", OPENVK_ROOT, $filename, $dir, $hash)->execute($err); # под виндой только execute
} catch(ShellUnavailableException $suex) {
exit(OPENVK_ROOT_CONF["openvk"]["debug"] ? "Shell is unavailable" : VIDEOS_FRIENDLY_ERROR);
} catch(UnknownCommandException $ucex) {
@ -118,11 +138,12 @@ class Video extends Media
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_like" => 1,
"can_repost" => 1,
"can_subscribe" => 1,
"can_add_to_faves" => 0,
@ -130,7 +151,7 @@ class Video extends Media
"comments" => $this->getCommentsCount(),
"date" => $this->getPublicationTime()->timestamp(),
"description" => $this->getDescription(),
"duration" => 0, // я хуй знает как получить длину видео
"duration" => $this->getLength(),
"image" => [
[
"url" => $this->getThumbnailURL(),
@ -139,8 +160,8 @@ class Video extends Media
"with_padding" => 1
]
],
"width" => 640,
"height" => 480,
"width" => $dimensions ? NULL : $dimensions[0],
"height" => $dimensions ? NULL : $dimensions[1],
"id" => $this->getVirtualId(),
"owner_id" => $this->getOwner()->getId(),
"user_id" => $this->getOwner()->getId(),
@ -194,10 +215,7 @@ class Video extends Media
function isDeleted(): bool
{
if ($this->getRecord()->deleted == 1)
return TRUE;
else
return FALSE;
return $this->getRecord()->deleted == 1;
}
function deleteVideo(): void
@ -205,6 +223,8 @@ class Video extends Media
$this->setDeleted(1);
$this->unwire();
$this->save();
$ctx->table("video_relations")->where("video", $this->getId())->delete();
}
static function fastMake(int $owner, string $name = "Unnamed Video.ogv", string $description = "", array $file, bool $unlisted = true, bool $anon = false): Video
@ -243,4 +263,126 @@ class Video extends Media
return $res;
}
function isInLibraryOf($entity): bool
{
return sizeof(DatabaseConnection::i()->getContext()->table("video_relations")->where([
"entity" => $entity->getId() * ($entity instanceof Club ? -1 : 1),
"video" => $this->getId(),
])) != 0;
}
function add($entity): bool
{
if($this->isInLibraryOf($entity))
return false;
$entityId = $entity->getId() * ($entity instanceof Club ? -1 : 1);
$audioRels = DatabaseConnection::i()->getContext()->table("video_relations");
$audioRels->insert([
"entity" => $entityId,
"video" => $this->getId(),
]);
return true;
}
function remove($entity): bool
{
if(!$this->isInLibraryOf($entity))
return false;
DatabaseConnection::i()->getContext()->table("video_relations")->where([
"entity" => $entity->getId() * ($entity instanceof Club ? -1 : 1),
"video" => $this->getId(),
])->delete();
return true;
}
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 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);
#exit(var_dump($path));
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 NULL;
$width = $this->getRecord()->width;
$height = $this->getRecord()->height;
if(!$width) $this->fillDimensions();
return $width != 0 ? [$width, $height] : NULL;
}
function delete(bool $softly = true): void
{
$ctx = DatabaseConnection::i()->getContext();
$ctx->table("video_relations")->where("video", $this->getId())->delete();
parent::delete($softly);
}
}

View file

@ -1,6 +1,6 @@
<?php declare(strict_types=1);
namespace openvk\Web\Models\Repositories;
use openvk\Web\Models\Entities\User;
use openvk\Web\Models\Entities\{User, Club};
use openvk\Web\Models\Entities\Video;
use Chandler\Database\DatabaseConnection;
@ -8,11 +8,13 @@ class Videos
{
private $context;
private $videos;
private $rels;
function __construct()
{
$this->context = DatabaseConnection::i()->getContext();
$this->videos = $this->context->table("videos");
$this->rels = $this->context->table("video_relations");
}
function get(int $id): ?Video
@ -34,16 +36,65 @@ class Videos
return new Video($videos);
}
function getByEntityId(int $entity, int $offset = 0, ?int $limit = NULL): \Traversable
{
$limit ??= OPENVK_DEFAULT_PER_PAGE;
$iter = $this->rels->where("entity", $entity)->limit($limit, $offset)->order("id DESC");
foreach($iter as $rel) {
$vid = $this->get($rel->video);
if(!$vid || $vid->isDeleted()) {
continue;
}
yield $vid;
}
}
function getVideosCountByEntityId(int $id)
{
return sizeof($this->rels->where("entity", $id));
}
function getByUser(User $user, int $page = 1, ?int $perPage = NULL): \Traversable
{
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
foreach($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0])->page($page, $perPage)->order("created DESC") as $video)
yield new Video($video);
return $this->getByEntityId($user->getId(), ($perPage * ($page - 1)), $perPage);
}
function getByClub(Club $club, int $page = 1, ?int $perPage = NULL): \Traversable
{
return $this->getByEntityId($club->getRealId(), ($perPage * ($page - 1)), $perPage);
}
function getUserVideosCount(User $user): int
{
return sizeof($this->videos->where("owner", $user->getId())->where(["deleted" => 0, "unlisted" => 0]));
return sizeof($this->rels->where("entity", $user->getId()));
}
function getRandomTwoVideosByEntityId(int $id): Array
{
$iter = $this->rels->where("entity", $id);
$ids = [];
foreach($iter as $it)
$ids[] = $it->video;
$shuffleSeed = openssl_random_pseudo_bytes(6);
$shuffleSeed = hexdec(bin2hex($shuffleSeed));
$ids = knuth_shuffle($ids, $shuffleSeed);
$ids = array_slice($ids, 0, 3);
$videos = [];
foreach($ids as $id) {
$video = $this->get((int)$id);
if(!$video || $video->isDeleted())
continue;
$videos[] = $video;
}
return $videos;
}
function find(string $query = "", array $pars = [], string $sort = "id"): Util\EntityStream

View file

@ -9,12 +9,12 @@ $temp2 = [System.IO.Path]::GetTempFileName()
$shell = Get-WmiObject Win32_process -filter "ProcessId = $PID"
$shell.SetPriority(16384)
Remove-Item $temp
Move-Item $file $temp
# video stub logic was implicitly deprecated, so we start processing at once
ffmpeg -i $temp -ss 00:00:01.000 -vframes 1 "$dir$hashT/$hash.gif"
ffmpeg -i $temp -c:v libx264 -q:v 7 -c:a libmp3lame -q:a 4 -tune zerolatency -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y $temp2
ffmpeg -i $temp -ss 00:00:01.000 -y -vframes 1 "$dir$hashT/$hash.gif"
ffmpeg -i $temp -c:v libx264 -f mp4 -q:v 7 -c:a libmp3lame -q:a 4 -tune zerolatency -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y $temp2
Move-Item $temp2 "$dir$hashT/$hash.mp4"
Remove-Item $temp
Move-Item $temp2 "$dir$hashT/$hash.mp4" -Force
Remove-Item $temp2

View file

@ -3,7 +3,7 @@ namespace openvk\Web\Presenters;
use openvk\Web\Models\Entities\{Club, Photo, Post};
use Nette\InvalidStateException;
use openvk\Web\Models\Entities\Notifications\ClubModeratorNotification;
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics, Audios, Posts};
use openvk\Web\Models\Repositories\{Clubs, Users, Albums, Managers, Topics, Audios, Posts, Videos};
use Chandler\Security\Authenticator;
final class GroupPresenter extends OpenVKPresenter
@ -31,6 +31,8 @@ final class GroupPresenter extends OpenVKPresenter
$this->template->albumsCount = (new Albums)->getClubAlbumsCount($club);
$this->template->topics = (new Topics)->getLastTopics($club, 3);
$this->template->topicsCount = (new Topics)->getClubTopicsCount($club);
$this->template->videos = (new Videos)->getRandomTwoVideosByEntityId($club->getRealId());
$this->template->videosCount = (new Videos)->getVideosCountByEntityId($club->getRealId());
$this->template->audios = (new Audios)->getRandomThreeAudiosByEntityId($club->getRealId());
$this->template->audiosCount = (new Audios)->getClubCollectionSize($club);
}

View file

@ -1,7 +1,7 @@
<?php declare(strict_types=1);
namespace openvk\Web\Presenters;
use openvk\Web\Models\Entities\Video;
use openvk\Web\Models\Repositories\{Users, Videos};
use openvk\Web\Models\Repositories\{Users, Videos, Clubs};
use Nette\InvalidStateException as ISE;
final class VideosPresenter extends OpenVKPresenter
@ -20,14 +20,19 @@ final class VideosPresenter extends OpenVKPresenter
function renderList(int $id): void
{
$user = $this->users->get($id);
if(!$user) $this->notFound();
if(!$user->getPrivacyPermission('videos.read', $this->user->identity ?? NULL))
if($id > 0)
$owner = $this->users->get($id);
else
$owner = (new Clubs)->get(abs($id));
if(!$owner) $this->notFound();
if($id > 0 && !$owner->getPrivacyPermission('videos.read', $this->user->identity ?? NULL))
$this->flashFail("err", tr("forbidden"), tr("forbidden_comment"));
$this->template->user = $user;
$this->template->videos = $this->videos->getByUser($user, (int) ($this->queryParam("p") ?? 1));
$this->template->count = $this->videos->getUserVideosCount($user);
$this->template->owner = $owner;
$this->template->canUpload = $id > 0 ? $id == $this->user->id : $owner->canBeModifiedBy($this->user->identity);
$this->template->videos = $id > 0 ? $this->videos->getByUser($owner, (int) ($this->queryParam("p") ?? 1)) : $this->videos->getByClub($owner, (int) ($this->queryParam("p") ?? 1));
$this->template->count = $this->videos->getVideosCountByEntityId($owner->getRealId());
$this->template->paginatorConf = (object) [
"count" => $this->template->count,
"page" => (int) ($this->queryParam("p") ?? 1),
@ -60,6 +65,14 @@ final class VideosPresenter extends OpenVKPresenter
if(OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading'])
$this->flashFail("err", tr("error"), tr("video_uploads_disabled"));
if($this->queryParam("gid")) {
$club = (new Clubs)->get((int)$this->queryParam("gid"));
if(!$club || !$club->canUploadVideo($this->user->identity))
$this->notFound();
}
$this->template->owner = $club ?: $this->user->identity;
if($_SERVER["REQUEST_METHOD"] === "POST") {
if(!empty($this->postParam("name"))) {
$video = new Video;
@ -83,6 +96,8 @@ final class VideosPresenter extends OpenVKPresenter
$video->save();
$video->add(!isset($club) ? $this->user->identity : $club);
$this->redirect("/video" . $video->getPrettyId());
} else {
$this->flashFail("err", tr("error_video"), tr("no_name_error"));

View file

@ -299,6 +299,32 @@
</div>
</div>
</div>
<div n:if="$videosCount > 0 || ($thisUser && $club->canBeModifiedBy($thisUser))">
<div class="content_title_expanded" onclick="hidePanel(this, {$videosCount});">
{_videos}
</div>
<div>
<div class="content_subtitle">
{tr("videos", $videosCount)}
<div style="float: right;">
<a href="/videos-{$club->getId()}">{_all_title}</a>
</div>
</div>
<div style="padding: 5px;">
<div class="ovk-video" n:foreach="$videos as $video">
<a href="/video{$video->getPrettyId()}" class="preview" align="center" id="videoOpen" data-id="{$video->getId()}">
<img
src="{$video->getThumbnailURL()}"
style="max-width: 170px; max-height: 127px; margin: auto;" />
</a>
<div>
<b><a href="/video{$video->getPrettyId()}" id="videoOpen" data-id="{$video->getId()}">{ovk_proc_strtr($video->getName(), 30)}</a></b><br>
<span style="font-size: 10px;">{$video->getPublicationTime()} | {_comments} ({$video->getCommentsCount()})</span>
</div>
</div>
</div>
</div>
</div>
<div n:if="($topicsCount > 0 || $club->isEveryoneCanCreateTopics() || ($thisUser && $club->canBeModifiedBy($thisUser))) && !$club->isDisplayTopicsAboveWallEnabled()">
<div class="content_title_expanded" onclick="hidePanel(this, {$topicsCount});">
{_discussions}

View file

@ -3,10 +3,10 @@
{var $count = $paginatorConf->count}
{var $page = $paginatorConf->page}
{block title}{_videos} {$user->getCanonicalName()}{/block}
{block title}{_videos} {$owner->getRealId() > 0 ? $owner->getCanonicalName() : tr("group_genetive")}{/block}
{block header}
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a>
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
» {_videos}
{/block}
@ -14,9 +14,9 @@
<div style="padding-bottom: 0px; padding-top: 0;" class="summaryBar">
<div class="summary">
{tr("videos", $count)}
<span n:if="isset($thisUser) && $thisUser->getId() == $user->getId() && !OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading']">
<span n:if="$canUpload">
&nbsp;|&nbsp;
<a href="/videos/upload">{_upload_video}</a>
<a href="/videos/upload{if $owner->getRealId() < 0}?gid={$owner->getId()}{/if}">{_upload_video}</a>
</span>
</div>
</div>

View file

@ -2,9 +2,9 @@
{block title}{_upload_video}{/block}
{block header}
<a href="{$thisUser->getURL()}">{$thisUser->getCanonicalName()}</a>
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
»
<a href="/videos{$thisUser->getId()}">{_videos}</a>
<a href="/videos{$owner->getRealId()}">{_videos}</a>
»
{_upload_video}
{/block}

View file

@ -10,7 +10,7 @@
</a>
{/if}
{elseif $attachment instanceof \openvk\Web\Models\Entities\Video}
{if !$attachment->isDeleted()}
{if $count && $count < 2}
{if $attachment->getType() === 0}
<div class="bsdn media" data-name="{$attachment->getName()}" data-author="{$attachment->getOwner()->getCanonicalName()}">
<video class="media" src="{$attachment->getURL()}"></video>
@ -27,10 +27,14 @@
<div class="video-wowzer">
<img src="/assets/packages/static/openvk/img/videoico.png" />
<a href="/video{$attachment->getPrettyId()}" id="videoOpen" data-id="{$attachment->getId()}">{$attachment->getName()}</a>
{if $attachment->getLength()}
({$attachment->getFormattedLength()})
{/if}
</div>
{else}
<span style="color:gray;">{_video_is_deleted}</span>
<div class="compactVideo" id="videoOpen" data-id="{$attachment->getId()}">
<img src="{$attachment->getThumbnailURL()}" alt="{$attachment->getName()}"/>
</div>
{/if}
{elseif $attachment instanceof \openvk\Web\Models\Entities\Poll}
{presenter "openvk!Poll->view", $attachment->getId()}

View file

@ -25,7 +25,7 @@
{var $attachmentsLayout = $comment->getChildrenWithLayout(288)}
<div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};">
<div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};" data-localized-nsfw-text="{_nsfw_warning}">
{include "attachment.xml", attachment => $attachment[2], parent => $comment, parentType => "comment"}
{include "attachment.xml", attachment => $attachment[2], parent => $comment, parentType => "comment", count => $attachmentsLayout->count}
</div>
</div>

View file

@ -81,7 +81,7 @@
{var $attachmentsLayout = $post->getChildrenWithLayout($width)}
<div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};">
<div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};" data-localized-nsfw-text="{_nsfw_warning}">
{include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post"}
{include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post", count => $attachmentsLayout->count}
</div>
</div>

View file

@ -73,7 +73,7 @@
{var $attachmentsLayout = $post->getChildrenWithLayout($width)}
<div n:ifcontent class="attachments attachments_b" style="height: {$attachmentsLayout->height|noescape}; width: {$attachmentsLayout->width|noescape};">
<div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};" data-localized-nsfw-text="{_nsfw_warning}">
{include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post"}
{include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post", count => $attachmentsLayout->count}
</div>
</div>

View file

@ -18,7 +18,7 @@ class Makima
$this->photos = $photos;
}
private function getOrientation(Photo $photo, &$ratio): int
private function getOrientation($photo, &$ratio): int
{
[$width, $height] = $photo->getDimensions();
$ratio = $width / $height;

View file

@ -3056,3 +3056,32 @@ hr {
background-position: 50% !important;
}
.compactVideo {
object-fit: contain;
cursor: pointer;
background: black;
width: calc(100% - 2px);
height: calc(100% - 2px); /* kostyls */
display: flex;
position: relative;
min-height: 28px;
}
.compactVideo img {
object-fit: contain;
margin-left: auto;
margin-right: auto;
}
.compactVideo::before {
content: " ";
background: url("/assets/packages/static/openvk/img/video_controls.png");
width: 28px;
height: 28px;
position: absolute;
left: 0;
right: 0;
margin: auto;
top: 0;
bottom: 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -0,0 +1,10 @@
CREATE TABLE `video_relations` (
`entity` BIGINT(20) NOT NULL,
`video` BIGINT(20) UNSIGNED NOT NULL,
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE = InnoDB CHARSET=utf8mb4 COLLATE utf8mb4_unicode_520_ci;
ALTER TABLE `openvk`.`video_relations` ADD INDEX `entity_video` (`entity`, `video`);
ALTER TABLE `videos` ADD `length` SMALLINT(5) UNSIGNED NULL DEFAULT NULL AFTER `name`, ADD INDEX `length` (`length`);
ALTER TABLE `videos` ADD `height` SMALLINT(5) UNSIGNED NULL DEFAULT NULL AFTER `length`, ADD `width` SMALLINT(5) UNSIGNED NULL DEFAULT NULL AFTER `height`;

View file

@ -749,6 +749,8 @@
"no_videos" = "У вас нет видео.";
"no_videos_results" = "Нет результатов.";
"group_genetive" = "группы";
/* Audios */
"audios" = "Аудиозаписи";