video upload works

This commit is contained in:
overflowerror 2021-01-06 01:01:03 +01:00
parent f3d856c2c1
commit d50b80f041
15 changed files with 1687 additions and 116 deletions

View file

@ -4,19 +4,20 @@
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=7.2.5",
"ext-ctype": "*",
"ext-iconv": "*",
"composer/package-versions-deprecated": "1.11.99.1",
"doctrine/annotations": "^1.0",
"doctrine/doctrine-bundle": "^2.2",
"doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/orm": "^2.8",
"phpdocumentor/reflection-docblock": "^5.2",
"ramsey/uuid-doctrine": "^1.6",
"sensio/framework-extra-bundle": "^5.6",
"symfony/asset": "5.2.*",
"symfony/console": "5.2.*",
"php": ">=7.2.5",
"ext-ctype": "*",
"ext-iconv": "*",
"amphp/http-client": "^4.5",
"composer/package-versions-deprecated": "1.11.99.1",
"doctrine/annotations": "^1.0",
"doctrine/doctrine-bundle": "^2.2",
"doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/orm": "^2.8",
"phpdocumentor/reflection-docblock": "^5.2",
"ramsey/uuid-doctrine": "^1.6",
"sensio/framework-extra-bundle": "^5.6",
"symfony/asset": "5.2.*",
"symfony/console": "5.2.*",
"symfony/dotenv": "5.2.*",
"symfony/expression-language": "5.2.*",
"symfony/flex": "^1.3.1",

1360
composer.lock generated

File diff suppressed because it is too large Load diff

0
landingzone/.gitignore vendored Normal file
View file

View file

@ -0,0 +1,63 @@
<?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 Version20210105224915 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('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 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, 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) SELECT id, uploader_id, uploaded, name, description, tags FROM __temp__video');
$this->addSql('DROP TABLE __temp__video');
$this->addSql('CREATE INDEX IDX_7CC7DA2C16678C77 ON video (uploader_id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$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 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)
, uploader_id BLOB NOT NULL, PRIMARY KEY(id))');
$this->addSql('INSERT INTO video (id, uploader_id, uploaded, name, description, tags) SELECT id, uploader_id, uploaded, name, description, tags FROM __temp__video');
$this->addSql('DROP TABLE __temp__video');
$this->addSql('CREATE INDEX IDX_7CC7DA2C16678C77 ON video (uploader_id)');
}
}

49
scripts/transcode.sh Executable file
View file

@ -0,0 +1,49 @@
#!/bin/sh
dir="content/$1/"
mkdir "$dir"
mkdir "$dir/360p/"
mkdir "$dir/480p/"
mkdir "$dir/720p/"
mkdir "$dir/1080p/"
#ffmpeg -hide_banner -y -i "landingzone/$1.vid" \
# -vf scale=w=640:h=360:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 \
# -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 800k -maxrate 856k -bufsize 1200k \
# -b:a 96k -hls_segment_filename "$dir/360p/%03d.ts" "$dir/360p.m3u8" \
# -vf scale=w=842:h=480:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 \
# -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 1400k -maxrate 1498k -bufsize 2100k \
# -b:a 128k -hls_segment_filename "$dir/480p/%03d.ts" "$dir/480p.m3u8" \
# -vf scale=w=1280:h=720:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 \
# -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 2800k -maxrate 2996k -bufsize 4200k \
# -b:a 128k -hls_segment_filename "$dir/720p/%03d.ts" "$dir/720p.m3u8" \
# -vf scale=w=1920:h=1080:force_original_aspect_ratio=decrease -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 \
# -sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 5000k -maxrate 5350k -bufsize 7500k \
# -b:a 192k -hls_segment_filename "$dir/1080p/%03d.ts" "$dir/1080p.m3u8"
ffmpeg -hide_banner -y -i "landingzone/$1.vid" \
-vf scale=w='(oh/a/2)*2':h=360 -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 \
-sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 800k -maxrate 856k -bufsize 1200k \
-b:a 96k -hls_segment_filename "$dir/360p/360p-%03d.ts" "$dir/360p/playlist.m3u8" \
-vf scale=w='(oh/a/2)*2':h=480 -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 \
-sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 1400k -maxrate 1498k -bufsize 2100k \
-b:a 128k -hls_segment_filename "$dir/480p/480p-%03d.ts" "$dir/480p/playlist.m3u8" \
-vf scale=w='(oh/a/2)*2':h=720 -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 \
-sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 2800k -maxrate 2996k -bufsize 4200k \
-b:a 128k -hls_segment_filename "$dir/720p/720p-%03d.ts" "$dir/720p/playlist.m3u8" \
-vf scale=w='(oh/a/2)*2':h=1080 -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 20 \
-sc_threshold 0 -g 48 -keyint_min 48 -hls_time 4 -hls_playlist_type vod -b:v 5000k -maxrate 5350k -bufsize 7500k \
-b:a 192k -hls_segment_filename "$dir/1080p/1080p-%03d.ts" "$dir/1080p/playlist.m3u8"
cat > "$dir/playlist.m3u8" <<EOF
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
360p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480
480p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080
1080p.m3u8
EOF

View file

@ -4,22 +4,25 @@
namespace App\Controller;
use App\Repository\UserRepository;
use App\Repository\VideoRepository;
use App\Entity\Video;
use App\Form\VideoType;
use App\Service\UserService;
use App\Service\VideoService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
private $userService;
private $videoService;
private $userRepository;
private $videoRepository;
public function __construct(UserRepository $userRepository, VideoRepository $videoRepository)
public function __construct(UserService $userService, VideoService $videoService)
{
$this->userRepository = $userRepository;
$this->videoRepository = $videoRepository;
$this->userService = $userService;
$this->videoService = $videoService;
}
/**
@ -32,11 +35,36 @@ class HomeController extends AbstractController
return $this->redirectToRoute("app_login");
}
$user = $this->userRepository->findOneByName($this->getUser()->getUsername());
$videos = $this->videoRepository->findByUploader($user);
$user = $this->userService->getLoggedInUser();
$videos = $this->videoService->getVideos($user);
dump($videos);
return $this->render("home/dashboard.html.twig", [
"videos" => $videos
]);
}
return $this->render("home/dashboard.html.twig");
/**
* @Route("/upload", name="app_upload")
*/
public function upload(Request $request): Response
{
$video = new Video();
$form = $this->createForm(VideoType::class, $video);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$video = $form->getData();
$file = $form->get("file")->getData();
if (!$file) {
$form->addError(new FormError(""));
} else {
$this->videoService->addVideo($video, $file);
}
}
return $this->render("home/upload.html.twig", [
"form" => $form->createView()
]);
}
}

View file

@ -13,6 +13,11 @@ use Ramsey\Uuid\UuidInterface;
*/
class Video
{
public const WAITING = 1;
public const PROCESSING_THUMBNAIL = 2;
public const PROCESSING_TRANSCODE = 3;
public const DONE = 4;
/**
* @ORM\Id
* @ORM\Column(type="uuid", unique=true)
@ -47,10 +52,10 @@ class Video
*/
private $tags = [];
public function __construct()
{
$this->uploaded = new DateTimeImmutable();
}
/**
* @ORM\Column(type="integer")
*/
private $state = self::WAITING;
public function getId(): ?UuidInterface
{
@ -74,6 +79,12 @@ class Video
return $this->uploaded;
}
public function setUploaded(): self
{
$this->uploaded = new DateTimeImmutable();
return $this;
}
public function getName(): ?string
{
return $this->name;
@ -109,4 +120,15 @@ class Video
return $this;
}
public function setState($state): self
{
$this->state = $state;
return $this;
}
public function getState(): int
{
return $this->state;
}
}

54
src/Form/VideoType.php Normal file
View file

@ -0,0 +1,54 @@
<?php
namespace App\Form;
use App\Entity\Video;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\File;
class VideoType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, $options): void
{
$builder
->add("name", TextType::class)
->add("description", TextareaType::class)
->add("file", FileType::class, [
"label" => "Video File",
"mapped" => false,
"required" => true,
"constraints" => [
new File([
"maxSize" => "1024Mi",
"maxSizeMessage" => "Yo, the file is too thigh. ({{ limit }} {{ suffix }})",
"mimeTypes" => [
"video/mp4",
"video/H264",
"video/H265",
"video/3gpp",
"video/quicktime",
"video/mpv"
],
"mimeTypesMessage" => "Video type not supported."
])
]
])
->add("submit", SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Video::class,
]);
}
}

View file

@ -54,12 +54,12 @@ class UserRepository extends ServiceEntityRepository implements PasswordUpgrader
}
*/
public function findOneByName($value): ?User
/*public function findOneByName($value): ?User
{
return $this->createQueryBuilder('u')
->andWhere('u.name = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult();
}
}*/
}

View file

@ -45,4 +45,15 @@ class VideoRepository extends ServiceEntityRepository
->getArrayResult()
;
}*/
public function save(Video $video)
{
$this->_em->persist($video);
$this->_em->flush();
}
public function update()
{
$this->_em->flush();
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace App\Service;
use App\Entity\User;
use App\Repository\UserRepository;
use Symfony\Component\Security\Core\Security;
class UserService
{
private $security;
private $userRepository;
public function __construct(Security $security, UserRepository $userRepository)
{
$this->security = $security;
$this->userRepository = $userRepository;
}
public function getLoggedInUser(): ?User
{
$user = $this->security->getUser();
if (!$user) {
return null;
}
$user = $this->userRepository->findOneByName($user->getUsername());
return $user;
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace App\Service;
use App\Entity\User;
use App\Entity\Video;
use App\Repository\VideoRepository;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class VideoService
{
private const LANDINGZONE_DIRECTORY = "../landingzone/";
private $videoRepository;
private $userService;
public function __construct(VideoRepository $videoRepository, UserService $userService)
{
$this->videoRepository = $videoRepository;
$this->userService = $userService;
}
public function getVideos(User $user): array
{
return $this->videoRepository->findByUploader($user);
}
public function addVideo(Video $video, UploadedFile $file)
{
$video->setUploaded();
$video->setUploader($this->userService->getLoggedInUser());
$this->videoRepository->save($video);
$file->move(self::LANDINGZONE_DIRECTORY, $video->getId()->toString() . ".vid");
}
public function getVideosForTranscode(): array
{
return $this->videoRepository->findByState(Video::WAITING);
}
public function setVideoState(Video $video, $state)
{
$video->setState($state);
$this->videoRepository->update();
}
}

View file

@ -1,10 +1,52 @@
{
"amphp/amp": {
"version": "v2.5.1"
},
"amphp/byte-stream": {
"version": "v1.8.0"
},
"amphp/cache": {
"version": "v1.4.0"
},
"amphp/dns": {
"version": "v1.2.3"
},
"amphp/hpack": {
"version": "v3.1.0"
},
"amphp/http": {
"version": "v1.6.3"
},
"amphp/http-client": {
"version": "v4.5.5"
},
"amphp/parser": {
"version": "v1.0.0"
},
"amphp/process": {
"version": "v1.1.0"
},
"amphp/serialization": {
"version": "v1.0.0"
},
"amphp/socket": {
"version": "v1.1.3"
},
"amphp/sync": {
"version": "v1.4.0"
},
"amphp/windows-registry": {
"version": "v0.3.3"
},
"brick/math": {
"version": "0.9.1"
},
"composer/package-versions-deprecated": {
"version": "1.11.99.1"
},
"daverandom/libdns": {
"version": "v2.0.2"
},
"doctrine/annotations": {
"version": "1.0",
"recipe": {
@ -102,6 +144,9 @@
"friendsofphp/proxy-manager-lts": {
"version": "v1.0.2"
},
"kelunik/certificate": {
"version": "v1.1.2"
},
"laminas/laminas-code": {
"version": "4.0.0"
},
@ -111,6 +156,15 @@
"laminas/laminas-zendframework-bridge": {
"version": "1.1.1"
},
"league/uri": {
"version": "6.4.0"
},
"league/uri-interfaces": {
"version": "2.2.0"
},
"league/uri-parser": {
"version": "1.4.1"
},
"monolog/monolog": {
"version": "2.2.0"
},
@ -138,6 +192,9 @@
"psr/event-dispatcher": {
"version": "1.0.0"
},
"psr/http-message": {
"version": "1.0.1"
},
"psr/link": {
"version": "1.0.0"
},

View file

@ -4,6 +4,10 @@
{% block body %}
Hi
{% for video in videos %}
<div>
{{ video.name }}
</div>
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,7 @@
{% extends 'base.html.twig' %}
{% block title %}Home{% endblock %}
{% block body %}
{{ form(form) }}
{% endblock %}