mirror of
https://github.com/sigmasternchen/MyTube
synced 2025-03-15 04:48:55 +00:00
video upload works
This commit is contained in:
parent
f3d856c2c1
commit
d50b80f041
15 changed files with 1687 additions and 116 deletions
|
@ -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
1360
composer.lock
generated
File diff suppressed because it is too large
Load diff
0
landingzone/.gitignore
vendored
Normal file
0
landingzone/.gitignore
vendored
Normal file
63
migrations/Version20210105224915.php
Normal file
63
migrations/Version20210105224915.php
Normal 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
49
scripts/transcode.sh
Executable 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
|
|
@ -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()
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -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
54
src/Form/VideoType.php
Normal 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,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
34
src/Service/UserService.php
Normal file
34
src/Service/UserService.php
Normal 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;
|
||||
}
|
||||
}
|
49
src/Service/VideoService.php
Normal file
49
src/Service/VideoService.php
Normal 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();
|
||||
}
|
||||
}
|
57
symfony.lock
57
symfony.lock
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
{% block body %}
|
||||
|
||||
Hi
|
||||
{% for video in videos %}
|
||||
<div>
|
||||
{{ video.name }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
7
templates/home/upload.html.twig
Normal file
7
templates/home/upload.html.twig
Normal file
|
@ -0,0 +1,7 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Home{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{{ form(form) }}
|
||||
{% endblock %}
|
Loading…
Reference in a new issue