From 873f00788d7ec8a0ea1fb0e3bc8db94bccf46cf6 Mon Sep 17 00:00:00 2001 From: mrilyew <99399973+mrilyew@users.noreply.github.com> Date: Tue, 5 Nov 2024 21:28:48 +0300 Subject: [PATCH] nsfw cover change & add masonry video support --- .../Entities/Traits/TAttachmentHost.php | 6 +- Web/Models/Entities/Video.php | 95 ++++++++++++++++-- .../templates/components/attachment.xml | 42 +++++--- .../components/post/microblogpost.xml | 6 +- .../templates/components/post/oldpost.xml | 6 +- Web/Util/Makima/Makima.php | 2 +- Web/static/css/main.css | 40 ++++++-- Web/static/css/nsfw-posts.css | 30 +++--- Web/static/img/videoico.png | Bin 130 -> 0 bytes Web/static/img/wall.png | Bin 2077 -> 2161 bytes Web/static/js/al_wall.js | 8 ++ install/sqls/000XX-new-video-fields.sql | 2 + 12 files changed, 184 insertions(+), 53 deletions(-) delete mode 100644 Web/static/img/videoico.png create mode 100644 install/sqls/000XX-new-video-fields.sql diff --git a/Web/Models/Entities/Traits/TAttachmentHost.php b/Web/Models/Entities/Traits/TAttachmentHost.php index db814cce..a574e657 100644 --- a/Web/Models/Entities/Traits/TAttachmentHost.php +++ b/Web/Models/Entities/Traits/TAttachmentHost.php @@ -1,6 +1,6 @@ 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; } diff --git a/Web/Models/Entities/Video.php b/Web/Models/Entities/Video.php index a4d0b898..e7a2d6e5 100644 --- a/Web/Models/Entities/Video.php +++ b/Web/Models/Entities/Video.php @@ -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(); diff --git a/Web/Presenters/templates/components/attachment.xml b/Web/Presenters/templates/components/attachment.xml index d084ae63..fb59a27c 100644 --- a/Web/Presenters/templates/components/attachment.xml +++ b/Web/Presenters/templates/components/attachment.xml @@ -10,23 +10,37 @@ {/if} {elseif $attachment instanceof \openvk\Web\Models\Entities\Video} - {if $attachment->getType() === 0} -
- + {if $tilesCount <= 1} + {if $attachment->getType() === 0} +
+ +
+ {else} + {var $driver = $attachment->getVideoDriver()} + {if !$driver} + {_version_incompatibility} + {else} + {$driver->getEmbed("100%")|noescape} + {/if} + {/if} + +
+
+ {$attachment->getName()} + ({$attachment->getFormattedLength()})
{else} - {var $driver = $attachment->getVideoDriver()} - {if !$driver} - {_version_incompatibility} - {else} - {$driver->getEmbed("100%")|noescape} - {/if} - {/if} + +
+
+
+
+ {$attachment->getFormattedLength()} +
-
- - {$attachment->getName()} -
+ + + {/if} {elseif $attachment instanceof \openvk\Web\Models\Entities\Poll} {presenter "openvk!Poll->view", $attachment->getId()} {elseif $attachment instanceof \openvk\Web\Models\Entities\Note} diff --git a/Web/Presenters/templates/components/post/microblogpost.xml b/Web/Presenters/templates/components/post/microblogpost.xml index a37076b1..1c42c073 100644 --- a/Web/Presenters/templates/components/post/microblogpost.xml +++ b/Web/Presenters/templates/components/post/microblogpost.xml @@ -74,7 +74,7 @@ {if $post->getTargetWall() < 0 && $wallOwner->canBeModifiedBy($thisUser)}data-fromgroup="{(int)$post->isPostedOnBehalfOfGroup()}"{/if}> {/if}
-
+
{$post->getText()|noescape} @@ -84,8 +84,8 @@ {/if} {var $attachmentsLayout = $post->getChildrenWithLayout($width)}
-
- {include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post"} +
+ {include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post", tilesCount => sizeof($attachmentsLayout->tiles)}
diff --git a/Web/Presenters/templates/components/post/oldpost.xml b/Web/Presenters/templates/components/post/oldpost.xml index 48063747..ff4ec03d 100644 --- a/Web/Presenters/templates/components/post/oldpost.xml +++ b/Web/Presenters/templates/components/post/oldpost.xml @@ -67,7 +67,7 @@
-
+
{var $owner = $author->getId()} @@ -79,8 +79,8 @@ {/if} {var $attachmentsLayout = $post->getChildrenWithLayout($width)}
-
- {include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post"} +
+ {include "../attachment.xml", attachment => $attachment[2], parent => $post, parentType => "post", tilesCount => sizeof($attachmentsLayout->tiles)}
diff --git a/Web/Util/Makima/Makima.php b/Web/Util/Makima/Makima.php index c9446826..3fc7b014 100644 --- a/Web/Util/Makima/Makima.php +++ b/Web/Util/Makima/Makima.php @@ -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; diff --git a/Web/static/css/main.css b/Web/static/css/main.css index 4e0ec10b..3725ee70 100644 --- a/Web/static/css/main.css +++ b/Web/static/css/main.css @@ -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; +} + diff --git a/Web/static/css/nsfw-posts.css b/Web/static/css/nsfw-posts.css index ed9841ca..1d946252 100644 --- a/Web/static/css/nsfw-posts.css +++ b/Web/static/css/nsfw-posts.css @@ -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; } diff --git a/Web/static/img/videoico.png b/Web/static/img/videoico.png deleted file mode 100644 index 4ebe30f3cf986b60f018409d4faa3892cbffb937..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^+#ogw8<4F3I>j1DaTa()7BevDDT6R$#Zvn+prDDT zi(^Q|t>gp+(Ji`GMNA$4A0Kn)SJ3{)zxblzCL3lw*Bd6ivwUWre9PT0qn_}|Zqj5{ ZhPsJNSCb}uz7EvS;OXk;vd$@?2>|2eCRhLf diff --git a/Web/static/img/wall.png b/Web/static/img/wall.png index 70cd42c0b883dd82499989e4e1fb0434ff2d5f86..197dca8d39ae2bce46709d3f264ab181353c787e 100644 GIT binary patch delta 2111 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 diff --git a/Web/static/js/al_wall.js b/Web/static/js/al_wall.js index d3530caf..be2e51ef 100644 --- a/Web/static/js/al_wall.js +++ b/Web/static/js/al_wall.js @@ -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; diff --git a/install/sqls/000XX-new-video-fields.sql b/install/sqls/000XX-new-video-fields.sql new file mode 100644 index 00000000..8f7c2ea0 --- /dev/null +++ b/install/sqls/000XX-new-video-fields.sql @@ -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`;