Torrent search and torrent show implemented. Simple imdex page implemented.
This commit is contained in:
parent
b409bfbfc7
commit
8ab6acf620
2
config/packages/pagefanta.yaml
Normal file
2
config/packages/pagefanta.yaml
Normal file
|
@ -0,0 +1,2 @@
|
|||
white_october_pagerfanta:
|
||||
default_view: twitter_bootstrap4
|
|
@ -1,7 +1,16 @@
|
|||
#index:
|
||||
# path: /
|
||||
# controller: App\Controller\DefaultController::index
|
||||
|
||||
test:
|
||||
index:
|
||||
path: /
|
||||
controller: App\Controller\TestController::test
|
||||
controller: App\Controller\MainController::index
|
||||
|
||||
torrent_show:
|
||||
path: /torrents/{id}
|
||||
controller: App\Controller\TorrentController::showTorrent
|
||||
requirements:
|
||||
method: GET
|
||||
id: '\d+'
|
||||
|
||||
torrent_search:
|
||||
path: /torrents/search
|
||||
controller: App\Controller\TorrentController::searchTorrent
|
||||
requirements:
|
||||
method: GET
|
|
@ -0,0 +1,3 @@
|
|||
#content {
|
||||
margin-top: 80px;
|
||||
}
|
16
src/Controller/MainController.php
Normal file
16
src/Controller/MainController.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Repository\TorrentRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
|
||||
class MainController extends Controller
|
||||
{
|
||||
public function index(TorrentRepository $repo)
|
||||
{
|
||||
return $this->render('index.html.twig', [
|
||||
'torrentsCount' => $repo->getTorrentsTotalCount(),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class TestController extends Controller
|
||||
{
|
||||
public function test(\App\Repository\Torrent $torrentRepo)
|
||||
{
|
||||
$torrents = $torrentRepo->getLastTorrents(10);
|
||||
|
||||
return $this->render('torrent_list.html.twig', [
|
||||
'torrents' => $torrents,
|
||||
]);
|
||||
}
|
||||
}
|
40
src/Controller/TorrentController.php
Normal file
40
src/Controller/TorrentController.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\Torrent;
|
||||
use App\Repository\TorrentRepository;
|
||||
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||
use Pagerfanta\Pagerfanta;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class TorrentController extends Controller
|
||||
{
|
||||
private const PER_PAGE = 20;
|
||||
|
||||
public function showTorrent(Torrent $torrent)
|
||||
{
|
||||
return $this->render('torrent_show.html.twig', [
|
||||
'torrent' => $torrent,
|
||||
]);
|
||||
}
|
||||
|
||||
public function searchTorrent(Request $request, TorrentRepository $repo)
|
||||
{
|
||||
$query = $request->query->get('query', '');
|
||||
$page = (int) $request->query->get('page', '1');
|
||||
|
||||
$pagerAdapter = new DoctrineORMAdapter($repo->createFindLikeQueryBuilder($query));
|
||||
$pager = new Pagerfanta($pagerAdapter);
|
||||
$pager
|
||||
->setCurrentPage($page)
|
||||
->setMaxPerPage(self::PER_PAGE)
|
||||
;
|
||||
|
||||
return $this->render('search_results.html.twig', [
|
||||
'torrents' => $pager,
|
||||
'searchQuery' => $query,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ use Doctrine\ORM\Mapping as ORM;
|
|||
* @ORM\Index(name="discovered_on_index", columns={"discovered_on"}),
|
||||
* @ORM\Index(name="info_hash_index", columns={"info_hash"})
|
||||
* })
|
||||
* @ORM\Entity(readOnly=true)
|
||||
* @ORM\Entity(readOnly=true, repositoryClass="App\Repository\TorrentRepository")
|
||||
*/
|
||||
class Torrent
|
||||
{
|
||||
|
@ -67,7 +67,7 @@ class Torrent
|
|||
return $this->infoHash;
|
||||
}
|
||||
|
||||
public function getInfoHashAsHex()
|
||||
public function getInfoHashAsHex(): string
|
||||
{
|
||||
return bin2hex(stream_get_contents($this->infoHash));
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Symfony\Bridge\Doctrine\RegistryInterface;
|
||||
|
||||
class Torrent extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(RegistryInterface $registry)
|
||||
{
|
||||
parent::__construct($registry, \App\Entity\Torrent::class);
|
||||
}
|
||||
|
||||
public function getLastTorrents(int $number): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('t')
|
||||
->orderBy('t.discoveredOn', 'DESC')
|
||||
->setMaxResults($number)
|
||||
;
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
}
|
45
src/Repository/TorrentRepository.php
Normal file
45
src/Repository/TorrentRepository.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\RegistryInterface;
|
||||
|
||||
class TorrentRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(RegistryInterface $registry)
|
||||
{
|
||||
parent::__construct($registry, \App\Entity\Torrent::class);
|
||||
}
|
||||
|
||||
public function getTorrentsTotalCount(): int
|
||||
{
|
||||
$qb = $this->createQueryBuilder('t')
|
||||
->select('COUNT(t.id)')
|
||||
;
|
||||
|
||||
return (int) $qb->getQuery()->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function createFindLikeQueryBuilder(string $query): QueryBuilder
|
||||
{
|
||||
$qb = $this->createQueryBuilder('t');
|
||||
|
||||
$where = $qb->expr()->andX();
|
||||
|
||||
$query = trim($query);
|
||||
$query = preg_replace('/\s+/', ' ', $query);
|
||||
|
||||
$parts = explode(' ', $query);
|
||||
|
||||
foreach ($parts as $idx => $part) {
|
||||
$where->add($qb->expr()->like('LOWER(t.name)', ':part_'.$idx));
|
||||
$qb->setParameter('part_'.$idx, '%'.strtolower($part).'%');
|
||||
}
|
||||
|
||||
$qb->where($where);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
}
|
|
@ -7,63 +7,41 @@
|
|||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
{% endblock %}
|
||||
<!--<link rel="icon" href="images/favicon.ico">-->
|
||||
<!--<link rel="icon" href="/images/favicon.ico">-->
|
||||
|
||||
<title>{% block title %}Magnetoco Web{% endblock %}</title>
|
||||
|
||||
{% block css %}
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<!--<link href="starter-template.css" rel="stylesheet">-->
|
||||
<link href="/css/style.css" rel="stylesheet">
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
|
||||
<a class="navbar-brand" href="#">Navbar</a>
|
||||
<a class="navbar-brand" href="{{ path('index') }}">Magnetico Web</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Link</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link disabled" href="#">Disabled</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="https://example.com" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdown01">
|
||||
{% block navbar_menu_items %}
|
||||
<a class="dropdown-item" href="#">Action</a>
|
||||
<a class="dropdown-item" href="#">Another action</a>
|
||||
<a class="dropdown-item" href="#">Something else here</a>
|
||||
{% endblock %}
|
||||
</div>
|
||||
<a class="nav-link" href="#">Last</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="form-inline my-2 my-lg-0">
|
||||
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
|
||||
<form class="form-inline my-2 my-lg-0" action="{{ path('torrent_search') }}" method="get">
|
||||
<input name="query" class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search"
|
||||
value="{% if searchQuery is defined %}{{ searchQuery }}{% endif %}">
|
||||
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main role="main" class="container">
|
||||
{% block content %}
|
||||
<div class="well">
|
||||
<h1>Bootstrap starter template</h1>
|
||||
<p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
<main id="content" role="main" class="container">
|
||||
{% block content %}{% endblock %}
|
||||
</main><!-- /.container -->
|
||||
|
||||
{% block javascript %}
|
||||
|
|
7
templates/index.html.twig
Normal file
7
templates/index.html.twig
Normal file
|
@ -0,0 +1,7 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="well">
|
||||
<p>Torrents indexed: {{ torrentsCount }}</p>
|
||||
</div>
|
||||
{% endblock %}
|
5
templates/search_results.html.twig
Normal file
5
templates/search_results.html.twig
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'torrent_list.html.twig' with {'torrents': torrents} %}
|
||||
{% endblock %}
|
|
@ -1,33 +1,32 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="pagination">
|
||||
{{ pagerfanta(torrents) }}
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th scope="col">ID</th>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Size</th>
|
||||
<th scope="col">Discovered</th>
|
||||
<th scope="col">Files</th>
|
||||
<th scope="col">Link</th>
|
||||
</thead>
|
||||
{# @var torrent \App\Entity\\App\Entity\Torrent #}
|
||||
{# @var torrent \App\Entity\Torrent #}
|
||||
{% for torrent in torrents %}
|
||||
<tr>
|
||||
<td>{{ torrent.id }}</td>
|
||||
<td>
|
||||
<a href="magnet:?xt=urn:btih:{{ torrent.infoHashAsHex }}&dn={{ torrent.name|url_encode }}">{{ torrent.name }}</a>
|
||||
<a href="{{ path('torrent_show', {'id': torrent.id}) }}">{{ torrent.name }}</a>
|
||||
</td>
|
||||
<td>{{ torrent.totalSize }}</td>
|
||||
<td>{{ torrent.discoveredOn }}</td>
|
||||
<td>{{ (torrent.totalSize / 1024 / 1024) | round(2, 'ceil')}} MB</td>
|
||||
<td>{{ torrent.discoveredOn | date('Y-m-d H:i:s')}}</td>
|
||||
<td>
|
||||
{#
|
||||
<ul>
|
||||
{% for file in torrent.files %}
|
||||
<li>{{ file.path }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
#}
|
||||
<a href="magnet:?xt=urn:btih:{{ torrent.infoHashAsHex }}&dn={{ torrent.name | url_encode }}">🔗</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<div class="pagination">
|
||||
{{ pagerfanta(torrents) }}
|
||||
</div>
|
||||
{% endblock %}
|
43
templates/torrent_show.html.twig
Normal file
43
templates/torrent_show.html.twig
Normal file
|
@ -0,0 +1,43 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
{# @var torrent \App\Entity\Torrent #}
|
||||
<table class="table">
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>{{ torrent.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hash</td>
|
||||
<td>
|
||||
<a href="magnet:?xt=urn:btih:{{ torrent.infoHashAsHex }}&dn={{ torrent.name | url_encode }}">{{ torrent.infoHashAsHex }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Size</td>
|
||||
<td>{{ (torrent.totalSize / 1024 / 1024) | round(2, 'ceil')}} MB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Discovered</td>
|
||||
<td>{{ torrent.discoveredOn | date('Y-m-d H:i:s')}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">File</th>
|
||||
<th scope="col">Size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{# @var file \App\Entity\File #}
|
||||
{% for file in torrent.files | sort %}
|
||||
<tr>
|
||||
<td>{{ file.path }}</td>
|
||||
<td>{{ (file.size / 1024 / 1024) | round(2, 'ceil')}} MB</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
Loading…
Reference in a new issue