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:
|
index:
|
||||||
# path: /
|
|
||||||
# controller: App\Controller\DefaultController::index
|
|
||||||
|
|
||||||
test:
|
|
||||||
path: /
|
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="discovered_on_index", columns={"discovered_on"}),
|
||||||
* @ORM\Index(name="info_hash_index", columns={"info_hash"})
|
* @ORM\Index(name="info_hash_index", columns={"info_hash"})
|
||||||
* })
|
* })
|
||||||
* @ORM\Entity(readOnly=true)
|
* @ORM\Entity(readOnly=true, repositoryClass="App\Repository\TorrentRepository")
|
||||||
*/
|
*/
|
||||||
class Torrent
|
class Torrent
|
||||||
{
|
{
|
||||||
|
@ -67,7 +67,7 @@ class Torrent
|
||||||
return $this->infoHash;
|
return $this->infoHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getInfoHashAsHex()
|
public function getInfoHashAsHex(): string
|
||||||
{
|
{
|
||||||
return bin2hex(stream_get_contents($this->infoHash));
|
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="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<!--<link rel="icon" href="images/favicon.ico">-->
|
<!--<link rel="icon" href="/images/favicon.ico">-->
|
||||||
|
|
||||||
<title>{% block title %}Magnetoco Web{% endblock %}</title>
|
<title>{% block title %}Magnetoco Web{% endblock %}</title>
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
<!-- Bootstrap core CSS -->
|
<!-- Bootstrap core CSS -->
|
||||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="/css/style.css" rel="stylesheet">
|
||||||
<!-- Custom styles for this template -->
|
|
||||||
<!--<link href="starter-template.css" rel="stylesheet">-->
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
|
<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">
|
<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>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
|
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
|
||||||
<ul class="navbar-nav mr-auto">
|
<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">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#">Link</a>
|
<a class="nav-link" href="#">Last</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>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<form class="form-inline my-2 my-lg-0">
|
<form class="form-inline my-2 my-lg-0" action="{{ path('torrent_search') }}" method="get">
|
||||||
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
|
<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>
|
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main role="main" class="container">
|
<main id="content" role="main" class="container">
|
||||||
{% block content %}
|
{% block content %}{% endblock %}
|
||||||
<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><!-- /.container -->
|
</main><!-- /.container -->
|
||||||
|
|
||||||
{% block javascript %}
|
{% 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 %}
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="pagination">
|
||||||
|
{{ pagerfanta(torrents) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<th scope="col">ID</th>
|
|
||||||
<th scope="col">Name</th>
|
<th scope="col">Name</th>
|
||||||
<th scope="col">Size</th>
|
<th scope="col">Size</th>
|
||||||
<th scope="col">Discovered</th>
|
<th scope="col">Discovered</th>
|
||||||
<th scope="col">Files</th>
|
<th scope="col">Link</th>
|
||||||
</thead>
|
</thead>
|
||||||
{# @var torrent \App\Entity\\App\Entity\Torrent #}
|
{# @var torrent \App\Entity\Torrent #}
|
||||||
{% for torrent in torrents %}
|
{% for torrent in torrents %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ torrent.id }}</td>
|
|
||||||
<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>
|
||||||
<td>{{ torrent.totalSize }}</td>
|
<td>{{ (torrent.totalSize / 1024 / 1024) | round(2, 'ceil')}} MB</td>
|
||||||
<td>{{ torrent.discoveredOn }}</td>
|
<td>{{ torrent.discoveredOn | date('Y-m-d H:i:s')}}</td>
|
||||||
<td>
|
<td>
|
||||||
{#
|
<a href="magnet:?xt=urn:btih:{{ torrent.infoHashAsHex }}&dn={{ torrent.name | url_encode }}">🔗</a>
|
||||||
<ul>
|
|
||||||
{% for file in torrent.files %}
|
|
||||||
<li>{{ file.path }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
#}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<div class="pagination">
|
||||||
|
{{ pagerfanta(torrents) }}
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% 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