mirror of
https://github.com/sigmasternchen/MyTube
synced 2025-03-15 04:48:55 +00:00
view logging works
This commit is contained in:
parent
d6197a692a
commit
6e5ac1b55c
8 changed files with 385 additions and 8 deletions
89
migrations/Version20210107200117.php
Normal file
89
migrations/Version20210107200117.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20210107200117 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE "view" (id BLOB NOT NULL, video_id BLOB NOT NULL, link_id BLOB NOT NULL, timestamp DATETIME NOT NULL --(DC2Type:datetime_immutable)
|
||||
, validated DATETIME DEFAULT NULL --(DC2Type:datetime_immutable)
|
||||
, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE INDEX IDX_FEFDAB8E29C1004E ON "view" (video_id)');
|
||||
$this->addSql('CREATE INDEX IDX_FEFDAB8EADA40271 ON "view" (link_id)');
|
||||
$this->addSql('DROP INDEX UNIQ_8D93D6495E237E06');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__user AS SELECT id, password, name, roles FROM user');
|
||||
$this->addSql('DROP TABLE user');
|
||||
$this->addSql('CREATE TABLE user (id BLOB NOT NULL, password VARCHAR(255) NOT NULL COLLATE BINARY, name VARCHAR(180) NOT NULL COLLATE BINARY, roles CLOB NOT NULL COLLATE BINARY --(DC2Type:json)
|
||||
, PRIMARY KEY(id))');
|
||||
$this->addSql('INSERT INTO user (id, password, name, roles) SELECT id, password, name, roles FROM __temp__user');
|
||||
$this->addSql('DROP TABLE __temp__user');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D6495E237E06 ON user (name)');
|
||||
$this->addSql('DROP INDEX IDX_7CC7DA2C16678C77');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__video AS SELECT id, uploader_id, uploaded, name, description, tags, state, length, transcoding_progress FROM video');
|
||||
$this->addSql('DROP TABLE video');
|
||||
$this->addSql('CREATE TABLE video (id BLOB NOT NULL, uploader_id BLOB NOT NULL, uploaded DATETIME NOT NULL --(DC2Type:datetime_immutable)
|
||||
, name VARCHAR(255) NOT NULL COLLATE BINARY, description VARCHAR(1024) NOT NULL COLLATE BINARY, tags CLOB NOT NULL COLLATE BINARY --(DC2Type:array)
|
||||
, state INTEGER NOT NULL, length DOUBLE PRECISION DEFAULT NULL, transcoding_progress INTEGER NOT NULL, PRIMARY KEY(id), CONSTRAINT FK_7CC7DA2C16678C77 FOREIGN KEY (uploader_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO video (id, uploader_id, uploaded, name, description, tags, state, length, transcoding_progress) SELECT id, uploader_id, uploaded, name, description, tags, state, length, transcoding_progress FROM __temp__video');
|
||||
$this->addSql('DROP TABLE __temp__video');
|
||||
$this->addSql('CREATE INDEX IDX_7CC7DA2C16678C77 ON video (uploader_id)');
|
||||
$this->addSql('DROP INDEX IDX_313BC42D61220EA6');
|
||||
$this->addSql('DROP INDEX IDX_313BC42D29C1004E');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__video_link AS SELECT id, video_id, creator_id, created, max_views, viewable_for, viewable_until, comment FROM video_link');
|
||||
$this->addSql('DROP TABLE video_link');
|
||||
$this->addSql('CREATE TABLE video_link (id BLOB NOT NULL, video_id BLOB NOT NULL, creator_id BLOB NOT NULL, created DATETIME NOT NULL --(DC2Type:datetime_immutable)
|
||||
, max_views INTEGER DEFAULT NULL, viewable_for INTEGER DEFAULT NULL, viewable_until DATETIME DEFAULT NULL, comment VARCHAR(1024) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id), CONSTRAINT FK_313BC42D29C1004E FOREIGN KEY (video_id) REFERENCES video (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_313BC42D61220EA6 FOREIGN KEY (creator_id) REFERENCES user (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO video_link (id, video_id, creator_id, created, max_views, viewable_for, viewable_until, comment) SELECT id, video_id, creator_id, created, max_views, viewable_for, viewable_until, comment FROM __temp__video_link');
|
||||
$this->addSql('DROP TABLE __temp__video_link');
|
||||
$this->addSql('CREATE INDEX IDX_313BC42D61220EA6 ON video_link (creator_id)');
|
||||
$this->addSql('CREATE INDEX IDX_313BC42D29C1004E ON video_link (video_id)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('DROP TABLE "view"');
|
||||
$this->addSql('DROP INDEX UNIQ_8D93D6495E237E06');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__user AS SELECT id, name, roles, password FROM user');
|
||||
$this->addSql('DROP TABLE user');
|
||||
$this->addSql('CREATE TABLE user (id BLOB NOT NULL, name VARCHAR(180) NOT NULL, roles CLOB NOT NULL --(DC2Type:json)
|
||||
, password VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('INSERT INTO user (id, name, roles, password) SELECT id, name, roles, password FROM __temp__user');
|
||||
$this->addSql('DROP TABLE __temp__user');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D6495E237E06 ON user (name)');
|
||||
$this->addSql('DROP INDEX IDX_7CC7DA2C16678C77');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__video AS SELECT id, uploader_id, uploaded, name, description, tags, state, length, transcoding_progress FROM video');
|
||||
$this->addSql('DROP TABLE video');
|
||||
$this->addSql('CREATE TABLE video (id BLOB NOT NULL, uploaded DATETIME NOT NULL --(DC2Type:datetime_immutable)
|
||||
, name VARCHAR(255) NOT NULL, description VARCHAR(1024) NOT NULL, tags CLOB NOT NULL --(DC2Type:array)
|
||||
, state INTEGER NOT NULL, length DOUBLE PRECISION DEFAULT NULL, uploader_id BLOB NOT NULL, transcoding_progress INTEGER DEFAULT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('INSERT INTO video (id, uploader_id, uploaded, name, description, tags, state, length, transcoding_progress) SELECT id, uploader_id, uploaded, name, description, tags, state, length, transcoding_progress FROM __temp__video');
|
||||
$this->addSql('DROP TABLE __temp__video');
|
||||
$this->addSql('CREATE INDEX IDX_7CC7DA2C16678C77 ON video (uploader_id)');
|
||||
$this->addSql('DROP INDEX IDX_313BC42D29C1004E');
|
||||
$this->addSql('DROP INDEX IDX_313BC42D61220EA6');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__video_link AS SELECT id, video_id, creator_id, created, max_views, viewable_for, viewable_until, comment FROM video_link');
|
||||
$this->addSql('DROP TABLE video_link');
|
||||
$this->addSql('CREATE TABLE video_link (id BLOB NOT NULL, created DATETIME NOT NULL --(DC2Type:datetime_immutable)
|
||||
, max_views INTEGER DEFAULT NULL, viewable_for INTEGER DEFAULT NULL, viewable_until DATETIME DEFAULT NULL, comment VARCHAR(1024) DEFAULT NULL, video_id BLOB NOT NULL, creator_id BLOB NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('INSERT INTO video_link (id, video_id, creator_id, created, max_views, viewable_for, viewable_until, comment) SELECT id, video_id, creator_id, created, max_views, viewable_for, viewable_until, comment FROM __temp__video_link');
|
||||
$this->addSql('DROP TABLE __temp__video_link');
|
||||
$this->addSql('CREATE INDEX IDX_313BC42D29C1004E ON video_link (video_id)');
|
||||
$this->addSql('CREATE INDEX IDX_313BC42D61220EA6 ON video_link (creator_id)');
|
||||
}
|
||||
}
|
|
@ -169,6 +169,7 @@ class DashboardController extends AbstractController
|
|||
try {
|
||||
$videoId = $this->uuidMapper->fromString($videoId);
|
||||
} catch (ConversionException $e) {
|
||||
return new Response($videoId);
|
||||
return $this->redirectToRoute("app_links");
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,9 @@ namespace App\Controller;
|
|||
|
||||
use App\Entity\User;
|
||||
use App\Entity\Video;
|
||||
use App\Entity\VideoLink;
|
||||
use App\Mapper\CustomUuidMapper;
|
||||
use App\Service\LoggingService;
|
||||
use App\Service\UserService;
|
||||
use App\Service\VideoLinkService;
|
||||
use App\Service\VideoService;
|
||||
|
@ -13,6 +15,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class WatchController extends AbstractController
|
||||
|
@ -34,28 +37,26 @@ class WatchController extends AbstractController
|
|||
private $userService;
|
||||
private $videoService;
|
||||
private $videoLinkService;
|
||||
private $loggingService;
|
||||
private $uuidMapper;
|
||||
|
||||
public function __construct(
|
||||
UserService $userService,
|
||||
VideoService $videoService,
|
||||
VideoLinkService $videoLinkService,
|
||||
LoggingService $loggingService,
|
||||
CustomUuidMapper $uuidMapper
|
||||
)
|
||||
{
|
||||
$this->userService = $userService;
|
||||
$this->videoService = $videoService;
|
||||
$this->videoLinkService = $videoLinkService;
|
||||
$this->loggingService = $loggingService;
|
||||
$this->uuidMapper = $uuidMapper;
|
||||
}
|
||||
|
||||
private function isAllowed(?Video $video, ?User $user, $linkId): int
|
||||
private function isAllowed(?Video $video, ?User $user, VideoLink $link): int
|
||||
{
|
||||
if ($video->getUploader() == $user) {
|
||||
return self::IS_OWNER;
|
||||
}
|
||||
|
||||
$link = $this->videoLinkService->get($this->uuidMapper->fromString($linkId));
|
||||
if (!$link) {
|
||||
return self::NOT_ALLOWED;
|
||||
}
|
||||
|
@ -74,8 +75,18 @@ class WatchController extends AbstractController
|
|||
try {
|
||||
$video = $this->videoService->get($this->uuidMapper->fromString($videoId));
|
||||
$user = $this->userService->getLoggedInUser();
|
||||
$link = null;
|
||||
|
||||
$allowed = $this->isAllowed($video, $user, $linkId);
|
||||
$allowed = self::NOT_ALLOWED;
|
||||
|
||||
if ($video->getUploader() == $user) {
|
||||
$allowed = self::IS_OWNER;
|
||||
}
|
||||
|
||||
if (!$allowed) {
|
||||
$link = $this->videoLinkService->get($this->uuidMapper->fromString($linkId));
|
||||
$allowed = $this->isAllowed($video, $user, $link);
|
||||
}
|
||||
} catch (ConversionException $e) {
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
@ -86,6 +97,7 @@ class WatchController extends AbstractController
|
|||
|
||||
return [
|
||||
"video" => $video,
|
||||
"link" => $link,
|
||||
"user" => $user,
|
||||
"isOwner" => $allowed == self::IS_OWNER
|
||||
];
|
||||
|
@ -151,6 +163,30 @@ class WatchController extends AbstractController
|
|||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{linkId}/{videoId}/v/{viewId}", methods={"POST"}, name="app_watch_view")
|
||||
*/
|
||||
public function viewCounter($videoId, $linkId, $viewId): Response
|
||||
{
|
||||
$data = $this->checkRequestData($videoId, $linkId);
|
||||
|
||||
if ($data["isOwner"]) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
|
||||
try {
|
||||
$viewId = $this->uuidMapper->fromString($viewId);
|
||||
} catch (ConversionException $e) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
|
||||
if (!$this->loggingService->validateView($data["video"], $data["link"], $viewId)) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
|
||||
return new Response("ok");
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/{linkId}/{videoId}/", name="app_watch_page")
|
||||
*/
|
||||
|
@ -158,7 +194,15 @@ class WatchController extends AbstractController
|
|||
{
|
||||
$data = $this->checkRequestData($videoId, $linkId);
|
||||
|
||||
$viewToken = null;
|
||||
if (!$data["isOwner"]) {
|
||||
$viewToken = $this->uuidMapper->toString($this->loggingService->createView($data["video"], $data["link"]));
|
||||
}
|
||||
|
||||
$data["video"]->setCustomId($videoId);
|
||||
|
||||
return $this->render("watch/watch.html.twig", [
|
||||
"viewToken" => $viewToken,
|
||||
"thumbnail" => $this->generateUrl("app_watch_thumbnail", [
|
||||
"linkId" => $linkId,
|
||||
"videoId" => $videoId
|
||||
|
@ -167,6 +211,7 @@ class WatchController extends AbstractController
|
|||
"linkId" => $linkId,
|
||||
"videoId" => $videoId
|
||||
]),
|
||||
"linkId" => $linkId,
|
||||
"video" => $data["video"],
|
||||
]);
|
||||
}
|
||||
|
|
99
src/Entity/View.php
Normal file
99
src/Entity/View.php
Normal file
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\ViewRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Ramsey\Uuid\Doctrine\UuidGenerator;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=ViewRepository::class)
|
||||
* @ORM\Table(name="`view`")
|
||||
*/
|
||||
class View
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="uuid", unique=true)
|
||||
* @ORM\GeneratedValue(strategy="CUSTOM")
|
||||
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Video::class)
|
||||
* @ORM\JoinColumn(nullable=false)
|
||||
*/
|
||||
private $video;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=VideoLink::class)
|
||||
* @ORM\JoinColumn(nullable=false)
|
||||
*/
|
||||
private $link;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable")
|
||||
*/
|
||||
private $timestamp;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable", nullable=true)
|
||||
*/
|
||||
private $validated;
|
||||
|
||||
public function getId(): ?UuidInterface
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getVideo(): ?Video
|
||||
{
|
||||
return $this->video;
|
||||
}
|
||||
|
||||
public function setVideo(?Video $video): self
|
||||
{
|
||||
$this->video = $video;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLink(): ?VideoLink
|
||||
{
|
||||
return $this->link;
|
||||
}
|
||||
|
||||
public function setLink(?VideoLink $link): self
|
||||
{
|
||||
$this->link = $link;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTimestamp(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
public function setTimestamp(): self
|
||||
{
|
||||
$this->timestamp = new DateTimeImmutable();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValidated(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->validated;
|
||||
}
|
||||
|
||||
public function setValidated(): self
|
||||
{
|
||||
$this->validated = new DateTimeImmutable();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
61
src/Repository/ViewRepository.php
Normal file
61
src/Repository/ViewRepository.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\View;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @method View|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method View|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method View[] findAll()
|
||||
* @method View[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class ViewRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, View::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return View[] Returns an array of View objects
|
||||
// */
|
||||
/*
|
||||
public function findByExampleField($value)
|
||||
{
|
||||
return $this->createQueryBuilder('v')
|
||||
->andWhere('v.exampleField = :val')
|
||||
->setParameter('val', $value)
|
||||
->orderBy('v.id', 'ASC')
|
||||
->setMaxResults(10)
|
||||
->getQuery()
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
public function findOneBySomeField($value): ?View
|
||||
{
|
||||
return $this->createQueryBuilder('v')
|
||||
->andWhere('v.exampleField = :val')
|
||||
->setParameter('val', $value)
|
||||
->getQuery()
|
||||
->getOneOrNullResult()
|
||||
;
|
||||
}
|
||||
*/
|
||||
|
||||
public function save(View $view)
|
||||
{
|
||||
$this->_em->persist($view);
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$this->_em->flush();
|
||||
}
|
||||
}
|
57
src/Service/LoggingService.php
Normal file
57
src/Service/LoggingService.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
|
||||
use App\Entity\Video;
|
||||
use App\Entity\VideoLink;
|
||||
use App\Entity\View;
|
||||
use App\Repository\ViewRepository;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
class LoggingService
|
||||
{
|
||||
|
||||
private $viewRepository;
|
||||
|
||||
public function __construct(ViewRepository $viewRepository)
|
||||
{
|
||||
$this->viewRepository = $viewRepository;
|
||||
}
|
||||
|
||||
public function createView(Video $video, VideoLink $link): UuidInterface
|
||||
{
|
||||
$view = new View();
|
||||
$view->setVideo($video);
|
||||
$view->setLink($link);
|
||||
$view->setTimestamp();
|
||||
|
||||
$this->viewRepository->save($view);
|
||||
|
||||
return $view->getId();
|
||||
}
|
||||
|
||||
public function validateView(Video $video, VideoLink $link, UuidInterface $viewId): bool
|
||||
{
|
||||
$view = $this->viewRepository->findOneById($viewId);
|
||||
if (!$view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($view->getVideo() != $video) {
|
||||
return false;
|
||||
}
|
||||
if ($view->getLink() != $link) {
|
||||
return false;
|
||||
}
|
||||
if ($view->getValidated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$view->setValidated();
|
||||
$this->viewRepository->update();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -65,7 +65,7 @@
|
|||
</div>
|
||||
<div class="info">
|
||||
<div class="link">
|
||||
<a href="{{ path("app_new_link") }}?video={{ video.customId }}">
|
||||
<a href="{{ path("app_new_link") }}?video={{ video.customId | url_encode }}">
|
||||
<i class="fas fa-link"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -18,6 +18,31 @@
|
|||
// enable quality selector
|
||||
videojs('video').hlsQualitySelector();
|
||||
</script>
|
||||
<script>
|
||||
{% if viewToken %}
|
||||
(function () {
|
||||
let player = videojs('video');
|
||||
//let target = {{ video.length > 120 ? 60 : video.length * 0.5 }};
|
||||
let target = 10;
|
||||
let current = 0;
|
||||
setInterval(function () {
|
||||
console.log(!player.paused() + ", " + current + ", " + target);
|
||||
if (!player.paused()) {
|
||||
current++;
|
||||
|
||||
if (current === target) {
|
||||
ajaxPost("{{ path("app_watch_view", {
|
||||
linkId: linkId,
|
||||
videoId: video.customId,
|
||||
viewId: viewToken
|
||||
}) }}", null, function () {
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
})()
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
|
Loading…
Reference in a new issue