mirror of
https://github.com/openvk/openvk
synced 2024-12-22 16:42:32 +03:00
Add something related with videos
- Теперь видосы работают как аудио, пользователи могут добавлять и удалять видео из коллекции. Но, правда, после обновления пользователи потеряют все свои видео, потом подумаю как исправить - Ещё теперь видео можно загружать в группу, жесть. И на странице группы теперь показывается 2 случайных видео из группы - Возможно, исправлена загрузка видео под виндовс (а может я её сломал) - У видосов теперь сохраняется ширина и высота, а так же длина - У прикреплённого видео рядом с названием показывается его длина - Видео теперь размещаются в masonry layout. Если помимо видео у поста есть другие фотографии или другие видео, то показывается только обложка видео и кнопка проигрывания - В класс video в api добавлена поддержка просмотра видеозаписей из групп
This commit is contained in:
parent
93bfe6e886
commit
791c36416d
20 changed files with 365 additions and 69 deletions
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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">
|
||||
|
|
||||
<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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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()}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
BIN
Web/static/img/video_controls.png
Normal file
BIN
Web/static/img/video_controls.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
10
install/sqls/00043-better-videos.sql
Normal file
10
install/sqls/00043-better-videos.sql
Normal 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`;
|
|
@ -749,6 +749,8 @@
|
|||
"no_videos" = "У вас нет видео.";
|
||||
"no_videos_results" = "Нет результатов.";
|
||||
|
||||
"group_genetive" = "группы";
|
||||
|
||||
/* Audios */
|
||||
|
||||
"audios" = "Аудиозаписи";
|
||||
|
|
Loading…
Reference in a new issue