mirror of
https://github.com/openvk/openvk
synced 2024-12-23 09:01:15 +03:00
nsfw cover change & add masonry video support
This commit is contained in:
parent
ad61a89812
commit
873f00788d
12 changed files with 184 additions and 53 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -34,9 +34,23 @@ 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("height", $height[1]);
|
||||
}
|
||||
|
||||
try {
|
||||
if(!is_dir($dirId = dirname($this->pathFromHash($hash))))
|
||||
|
@ -118,6 +132,7 @@ class Video extends Media
|
|||
function getApiStructure(?User $user = NULL): object
|
||||
{
|
||||
$fromYoutube = $this->getType() == Video::TYPE_EMBED;
|
||||
$dimensions = $this->getDimensions();
|
||||
$res = (object)[
|
||||
"type" => "video",
|
||||
"video" => [
|
||||
|
@ -130,7 +145,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 +154,8 @@ class Video extends Media
|
|||
"with_padding" => 1
|
||||
]
|
||||
],
|
||||
"width" => 640,
|
||||
"height" => 480,
|
||||
"width" => $dimensions ? $dimensions[0] : 640,
|
||||
"height" => $dimensions ? $dimensions[1] : 480,
|
||||
"id" => $this->getVirtualId(),
|
||||
"owner_id" => $this->getOwner()->getId(),
|
||||
"user_id" => $this->getOwner()->getId(),
|
||||
|
@ -224,6 +239,74 @@ class Video extends Media
|
|||
|
||||
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
|
||||
{
|
||||
|
@ -248,7 +331,7 @@ class Video extends Media
|
|||
$res->owner_id = $this->getOwner()->getId();
|
||||
$res->title = $this->getName();
|
||||
$res->description = $this->getDescription();
|
||||
$res->duration = "22";
|
||||
$res->duration = $this->getLength();
|
||||
$res->link = "/video".$this->getOwner()->getId()."_".$this->getVirtualId();
|
||||
$res->image = $this->getThumbnailURL();
|
||||
$res->date = $this->getPublicationTime()->timestamp();
|
||||
|
|
|
@ -10,23 +10,37 @@
|
|||
</a>
|
||||
{/if}
|
||||
{elseif $attachment instanceof \openvk\Web\Models\Entities\Video}
|
||||
{if $attachment->getType() === 0}
|
||||
<div class="bsdn media" data-name="{$attachment->getName()}" data-author="{$attachment->getOwner()->getCanonicalName()}">
|
||||
<video class="media" src="{$attachment->getURL()}"></video>
|
||||
{if $tilesCount <= 1}
|
||||
{if $attachment->getType() === 0}
|
||||
<div class="bsdn media" data-name="{$attachment->getName()}" data-author="{$attachment->getOwner()->getCanonicalName()}">
|
||||
<video class="media" src="{$attachment->getURL()}"></video>
|
||||
</div>
|
||||
{else}
|
||||
{var $driver = $attachment->getVideoDriver()}
|
||||
{if !$driver}
|
||||
<span style="color:red;">{_version_incompatibility}</span>
|
||||
{else}
|
||||
{$driver->getEmbed("100%")|noescape}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<div class="video-wowzer">
|
||||
<div class="small-video-ico"></div>
|
||||
<a href="/video{$attachment->getPrettyId()}" id="videoOpen" data-id="{$attachment->getId()}">{$attachment->getName()}</a>
|
||||
<span class="video-wowzer-length" n:if="$attachment->getLength() != NULL">({$attachment->getFormattedLength()})</span>
|
||||
</div>
|
||||
{else}
|
||||
{var $driver = $attachment->getVideoDriver()}
|
||||
{if !$driver}
|
||||
<span style="color:red;">{_version_incompatibility}</span>
|
||||
{else}
|
||||
{$driver->getEmbed("100%")|noescape}
|
||||
{/if}
|
||||
{/if}
|
||||
<a class='compact_video' id='videoOpen' data-id='{$attachment->getId()}' href="/video{$attachment->getPrettyId()}">
|
||||
<div class='play-button'>
|
||||
<div class='play-button-ico'></div>
|
||||
</div>
|
||||
<div class='video-length' n:if="$attachment->getLength() != NULL">
|
||||
{$attachment->getFormattedLength()}
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<img class="media media_makima" src="{$attachment->getThumbnailURL()}" loading=lazy />
|
||||
</a>
|
||||
{/if}
|
||||
{elseif $attachment instanceof \openvk\Web\Models\Entities\Poll}
|
||||
{presenter "openvk!Poll->view", $attachment->getId()}
|
||||
{elseif $attachment instanceof \openvk\Web\Models\Entities\Note}
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
{if $post->getTargetWall() < 0 && $wallOwner->canBeModifiedBy($thisUser)}data-fromgroup="{(int)$post->isPostedOnBehalfOfGroup()}"{/if}></a>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="post-content" id="{$post->getPrettyId()}">
|
||||
<div class="post-content" id="{$post->getPrettyId()}" data-localized-nsfw-text="{_nsfw_warning}">
|
||||
<div class="text" id="text{$post->getPrettyId()}">
|
||||
<span data-text="{$post->getText(false)}" class="really_text">{$post->getText()|noescape}</span>
|
||||
|
||||
|
@ -84,8 +84,8 @@
|
|||
{/if}
|
||||
{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"}
|
||||
<div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};">
|
||||
{include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post", tilesCount => sizeof($attachmentsLayout->tiles)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
</a>
|
||||
</a>
|
||||
</div>
|
||||
<div class="post-content" id="{$post->getPrettyId()}">
|
||||
<div class="post-content" id="{$post->getPrettyId()}" data-localized-nsfw-text="{_nsfw_warning}">
|
||||
<div class="text" id="text{$post->getPrettyId()}">
|
||||
{var $owner = $author->getId()}
|
||||
|
||||
|
@ -79,8 +79,8 @@
|
|||
{/if}
|
||||
{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"}
|
||||
<div class="attachment" n:foreach="$attachmentsLayout->tiles as $attachment" style="float: {$attachment[3]|noescape}; width: {$attachment[0]|noescape}; height: {$attachment[1]|noescape};">
|
||||
{include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post", tilesCount => sizeof($attachmentsLayout->tiles)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -2441,7 +2441,7 @@ a.poll-retract-vote {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.post-horizontal .upload-item .play-button {
|
||||
.post-horizontal .upload-item .play-button, .compact_video .play-button {
|
||||
position: absolute;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
|
@ -2452,7 +2452,7 @@ a.poll-retract-vote {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.post-horizontal .upload-item .play-button .play-button-ico {
|
||||
.post-horizontal .upload-item .play-button .play-button-ico, .compact_video .play-button .play-button-ico {
|
||||
background: url(/assets/packages/static/openvk/img/wall.png) no-repeat 1px 0;
|
||||
display: inline-block;
|
||||
height: 15px;
|
||||
|
@ -2648,12 +2648,13 @@ a.poll-retract-vote {
|
|||
padding: 3px 0;
|
||||
}
|
||||
|
||||
.video-wowzer a::before {
|
||||
content: "b";
|
||||
color: transparent;
|
||||
width: 12px;
|
||||
background-image: url(/assets/packages/static/openvk/img/videoico.png);
|
||||
display: none;
|
||||
.video-wowzer .small-video-ico {
|
||||
vertical-align: bottom;
|
||||
display: inline-block;
|
||||
width: 11px;
|
||||
height: 14px;
|
||||
background: url(/assets/packages/static/openvk/img/wall.png?v=2);
|
||||
background-position: -87px 0px;
|
||||
}
|
||||
|
||||
/* Da search */
|
||||
|
@ -3511,3 +3512,26 @@ hr {
|
|||
width: 30px;
|
||||
height: 7px;
|
||||
}
|
||||
|
||||
.compact_video {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.compact_video .video-length {
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
width: 34px;
|
||||
height: 14px;
|
||||
bottom: 5px;
|
||||
right: 5px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
.post-nsfw .post-content .media {
|
||||
filter: saturate(0.8) blur(15px);
|
||||
}
|
||||
|
||||
.post-nsfw .post-content .attachment {
|
||||
overflow: hidden;
|
||||
.post-nsfw .post-content {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.post-nsfw .post-content .attachment:active .media {
|
||||
filter: none;
|
||||
.post-nsfw .post-content .text {
|
||||
filter: saturate(0.8) blur(28px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.post-nsfw .post-content .attachment::after {
|
||||
.post-nsfw .post-content::after {
|
||||
position: absolute;
|
||||
top: calc(50% - 16px);
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 8px 0;
|
||||
background-color: hsla(0, 0%, 0%, .5);
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
background-color: hsla(0, 0%, 0%, .7);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
content: attr(data-localized-nsfw-text);
|
||||
}
|
||||
|
||||
.post-nsfw .post-content .attachment:active::after {
|
||||
display: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 130 B |
Binary file not shown.
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2.1 KiB |
|
@ -486,6 +486,7 @@ async function __uploadToTextarea(file, textareaNode) {
|
|||
})
|
||||
const json_response = await res.json()
|
||||
if(!json_response.success) {
|
||||
u(`#temp_filler${rand}`).remove()
|
||||
fastError((tr("error_uploading_photo") + json_response.flash.message))
|
||||
return
|
||||
}
|
||||
|
@ -1025,6 +1026,13 @@ u(document).on('click', '.post-buttons .upload-item', (e) => {
|
|||
e.preventDefault()
|
||||
})
|
||||
|
||||
u(document).on('click', '.post.post-nsfw .post-content', (e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
u(e.target).closest('.post-nsfw').removeClass('post-nsfw')
|
||||
})
|
||||
|
||||
async function repost(id, repost_type = 'post') {
|
||||
const repostsCount = u(`#repostsCount${id}`)
|
||||
const previousVal = repostsCount.length > 0 ? Number(repostsCount.html()) : 0;
|
||||
|
|
2
install/sqls/000XX-new-video-fields.sql
Normal file
2
install/sqls/000XX-new-video-fields.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
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`;
|
Loading…
Reference in a new issue