Compare commits

...

35 commits
2.5.1 ... main

Author SHA1 Message Date
yggverse
d72bda1e71 remove deprecated info 2025-08-05 21:46:49 +03:00
yggverse
085e6174d9 update readme 2025-08-05 21:45:08 +03:00
yggverse
7bcc52fcae update readme 2025-08-05 21:44:31 +03:00
yggverse
5a1ada42e7 update readme 2025-08-05 21:42:17 +03:00
yggverse
ee9b7be6ac fix github markdown 2025-08-05 21:30:58 +03:00
yggverse
09811cd801 add reference to btracker project 2025-08-05 21:30:28 +03:00
yggverse
1281acea22 remove deprecated info 2025-08-05 21:27:26 +03:00
ghost
95addf0c48 add crawler 2024-02-01 16:59:14 +02:00
ghost
e780c5b4b5 fix description variables 2023-12-23 08:18:25 +02:00
ghost
6c775f822c fix torrent description filters 2023-12-23 07:56:54 +02:00
ghost
bff1962071 undefined variable 2023-12-10 22:15:59 +02:00
ghost
10181a04f1 hide header description #32 2023-12-10 01:55:49 +02:00
ghost
7eb02d06cc update version 2023-12-08 20:18:53 +02:00
ghost
2f4dbff90d add trim filters 2023-12-08 20:18:28 +02:00
ghost
3c6b1d6ab7 allow markdown from whitelist only 2023-12-08 20:06:47 +02:00
ghost
9d596de610 update version 2023-12-08 04:10:51 +02:00
ghost
6603790aba disable markdown as unsafe for remote content without additional filters implementation 2023-12-08 04:09:38 +02:00
ghost
fe608cff8f update version 2023-12-08 02:19:01 +02:00
ghost
9814a56135 composer update 2023-12-07 02:30:15 +02:00
ghost
27e598fded add rel/nofollow for wanted links 2023-12-03 02:22:53 +02:00
ghost
f5d4c19eb9 add new crawler 2023-12-03 02:11:08 +02:00
ghost
9081acebb3 update readme 2023-12-01 15:57:00 +02:00
ghost
3b832c94bc add rel="nofollow" to the action links 2023-11-19 23:05:47 +02:00
ghost
110976c619 add clickable links support for software info 2023-11-19 19:25:27 +02:00
ghost
2afaf2f618 fix links regex condition 2023-11-19 19:24:23 +02:00
ghost
8aff756e30 apply search filter for RSS #35 2023-11-18 09:27:33 +02:00
ghost
7177cdb4fe add torrent private info 2023-11-15 09:52:49 +02:00
ghost
3ce3dfe77b fix contributors list by integrate new features 2023-11-13 19:38:05 +02:00
ghost
5523034307 add search filters counter #35 2023-11-13 11:23:37 +02:00
ghost
e3503bc4bd add rel/nofollow to the filter link #35 2023-11-13 10:50:41 +02:00
ghost
002a41da87 add filter link #35 2023-11-13 10:37:16 +02:00
ghost
deb35d5013 add filter attributes support for pagination and tag links #35 2023-11-13 10:11:48 +02:00
ghost
1ae5d324c2 remove h2 tag from filter headers #35 2023-11-13 09:39:10 +02:00
ghost
5a0342a998 init extended search feature #35 2023-11-13 09:35:04 +02:00
ghost
7a1fa12271 fix locales sort order 2023-11-13 08:24:12 +02:00
28 changed files with 911 additions and 455 deletions

4
.env
View file

@ -48,7 +48,7 @@ MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
# YGGtracker
# Application version, used for API and media cache
APP_VERSION='2.5.0'
APP_VERSION=2.6.2
# Application name
APP_NAME=YGGtracker
@ -88,7 +88,7 @@ APP_POSTERS=1
APP_TRACKERS=http://[201:23b4:991a:634d:8359:4521:5576:15b7]:2023/announce|http://[200:1e2f:e608:eb3a:2bf:1e62:87ba:e2f7]/announce|http://[316:c51a:62a3:8b9::5]/announce
# List of crawlers where ignored in actions and activity features
APP_CRAWLERS=201:23b4:991a:634d:8359:4521:5576:15b7|30a:5fad::e
APP_CRAWLERS=201:23b4:991a:634d:8359:4521:5576:15b7|30a:5fad::e|202:f2bc:f800:7cc4:c109:7857:5cae:6630|200:1554:e730:4030:605b:47be:6fb6:7b11
# Max torrent filesize for uploads (check upload_max_filesize in the php.ini)
APP_TORRENT_FILE_SIZE_MAX=1024000

View file

@ -1,22 +1,16 @@
# YGGtracker
[![Crowdin](https://badges.crowdin.net/yggtracker/localized.svg)](https://crowdin.com/project/yggtracker)
> [!NOTE]
> Take a look at [βtracker](https://github.com/yggverse/btracker) - the modern aggregation alternative written in Rust!
BitTorrent Network for Yggdrasil
A social-oriented BitTorrent catalog for the [Yggdrasil](https://github.com/yggdrasil-network) network, written in the Symfony framework.
YGGtracker is catalog, open tracker and social network with many features that allow to convert, filter and download any torrent in [Yggdrasil network](https://github.com/yggdrasil-network) by community.
Engine uses IPv6 `0200::/7` addresses to identify users without registration.
YGGtracker is a manually operated catalog and social network that allows users to share their torrents in the local network. Engine uses IPv6 `0200::/7` addresses to identify users without registration.
#### [Showcase](https://github.com/YGGverse/YGGtracker/wiki/Showcase)
![Pasted image 1](https://github.com/YGGverse/YGGtracker/assets/108541346/962f7850-01e1-4add-9dbe-c11b80108a75)
#### Instances
* `http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggtracker/`
+ `ftp://[201:23b4:991a:634d:8359:4521:5576:15b7]:21/yggtracker/`
#### Installation
```
@ -70,7 +64,7 @@ Custom settings could be provided in the `/.env.local` file by overwriting defau
#### Localization
Join community translations by [Crowdin](https://crowdin.com/project/yggtracker)
[![Crowdin](https://badges.crowdin.net/yggtracker/localized.svg)](https://crowdin.com/project/yggtracker)
#### API
@ -85,15 +79,6 @@ git checkout main
git checkout -b my-pr-branch-name
```
#### Donate to contributors
* @d47081:
+ ![wakatime](https://wakatime.com/badge/user/0b7fe6c1-b091-4c98-b930-75cfee17c7a5/project/059ec567-2c65-4c65-a48e-51dcc366f1a0.svg)
+ [BTC](https://www.blockchain.com/explorer/addresses/btc/bc1qngdf2kwty6djjqpk0ynkpq9wmlrmtm7e0c534y) | [LTC](https://live.blockcypher.com/ltc/address/LUSiqzKsfB1vBLvpu515DZktG9ioKqLyj7) | [XMR](835gSR1Uvka19gnWPkU2pyRozZugRZSPHDuFL6YajaAqjEtMwSPr4jafM8idRuBWo7AWD3pwFQSYRMRW9XezqrK4BEXBgXE) | [ZEPH](ZEPHsADHXqnhfWhXrRcXnyBQMucE3NM7Ng5ZVB99XwA38PTnbjLKpCwcQVgoie8EJuWozKgBiTmDFW4iY7fNEgSEWyAy4dotqtX)
+ Support our server by order [Linux VPS](https://www.yourserver.se/portal/aff.php?aff=610)
+ Inspiration by [SomaFM Deep Space One](https://somafm.com/deepspaceone/)
#### License
* Engine sources [MIT License](https://github.com/YGGverse/YGGtracker/blob/main/LICENSE)

785
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -9,20 +9,156 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use App\Service\UserService;
use App\Service\TorrentService;
use App\Service\ActivityService;
class SearchController extends AbstractController
{
public function module(
?string $query,
?string $type
Request $request,
UserService $userService,
TorrentService $torrentService,
ActivityService $activityService
): Response
{
// Defaults
$locales = [];
$categories = [];
$sensitive = [];
// Request
$query = $request->get('query') ? urldecode($request->get('query')) : '';
$filter = $request->get('filter') ? true : false;
// Extended search
if ($filter)
{
// Init user
$user = $this->initUser(
$request,
$userService,
$activityService
);
// Keywords
$keywords = explode(' ', $query);
// Locales
foreach (explode('|', $this->getParameter('app.locales')) as $locale)
{
if ($request->get('locales'))
{
$checked = in_array($locale, (array) $request->get('locales'));
}
else
{
$checked = in_array($locale, $user->getLocales());
}
$locales[] =
[
'value' => $locale,
'checked' => $checked,
'total' => $torrentService->findTorrentsTotal(
0,
$keywords,
[$locale],
$request->get('categories') ? $request->get('categories') : $user->getCategories(),
$request->get('sensitive') ? null : false,
!$user->isModerator() ? true : null,
!$user->isModerator() ? true : null,
)
];
}
// Categories
foreach (explode('|', $this->getParameter('app.categories')) as $category)
{
if ($request->get('categories'))
{
$checked = in_array($category, (array) $request->get('categories'));
}
else
{
$checked = in_array($category, $user->getCategories());
}
$categories[] =
[
'value' => $category,
'checked' => $checked,
'total' => $torrentService->findTorrentsTotal(
0,
$keywords,
$request->get('locales') ? $request->get('locales') : $user->getLocales(),
[$category],
$request->get('sensitive') ? null : false,
!$user->isModerator() ? true : null,
!$user->isModerator() ? true : null,
)
];
}
// Sensitive
$sensitive =
[
'checked' => $request->get('sensitive'),
'total' => $torrentService->findTorrentsTotal(
0,
$keywords,
$request->get('locales') ? $request->get('locales') : $user->getLocales(),
$request->get('categories') ? $request->get('categories') : $user->getCategories(),
true,
!$user->isModerator() ? true : null,
!$user->isModerator() ? true : null,
)
];
}
return $this->render(
'default/search/module.html.twig',
[
'query' => $query ? urldecode($query) : '',
'query' => $query,
'filter' => $filter,
'sensitive' => $sensitive,
'locales' => $locales,
'categories' => $categories,
]
);
}
private function initUser(
Request $request,
UserService $userService,
ActivityService $activityService
): ?\App\Entity\User
{
// Init user
if (!$user = $userService->findUserByAddress($request->getClientIp()))
{
$user = $userService->addUser(
$request->getClientIp(),
time(),
$this->getParameter('app.locale'),
explode('|', $this->getParameter('app.locales')),
$activityService->getEventCodes(),
$this->getParameter('app.theme'),
$this->getParameter('app.sensitive'),
$this->getParameter('app.yggdrasil'),
$this->getParameter('app.posters'),
$this->getParameter('app.approved')
);
// Add user join event
$activityService->addEventUserAdd(
$user->getId(),
time()
);
}
return $user;
}
}

View file

@ -265,22 +265,22 @@ class TorrentController extends AbstractController
// Get total torrents
$total = $torrentService->findTorrentsTotal(
$user->getId(),
$request->get('filter') ? 0 : $user->getId(),
$query,
$user->getLocales(),
$user->getCategories(),
$user->isSensitive() ? false : null,
$request->get('filter') ? (array) $request->get('locales') : $user->getLocales(),
$request->get('filter') ? (array) $request->get('categories') : $user->getCategories(),
$request->get('filter') ? ($request->get('sensitive') ? null : false) : ($user->isSensitive() ? false : null),
!$user->isModerator() ? true : null,
!$user->isModerator() ? true : null,
);
$torrents = [];
foreach ($torrentService->findTorrents(
$user->getId(),
$request->get('filter') ? 0 : $user->getId(),
$query,
$user->getLocales(),
$user->getCategories(),
$user->isSensitive() ? false : null,
$request->get('filter') ? (array) $request->get('locales') : $user->getLocales(),
$request->get('filter') ? (array) $request->get('categories') : $user->getCategories(),
$request->get('filter') ? ($request->get('sensitive') ? null : false) : ($user->isSensitive() ? false : null),
!$user->isModerator() ? true : null,
!$user->isModerator() ? true : null,
$this->getParameter('app.pagination'),
@ -632,11 +632,11 @@ class TorrentController extends AbstractController
// Get total torrents
$total = $torrentService->findTorrentsTotal(
$user->getId(),
$request->get('filter') ? 0 : $user->getId(),
$query,
$user->getLocales(),
$user->getCategories(),
$user->isSensitive() ? false : null,
$request->get('filter') ? (array) $request->get('locales') : $user->getLocales(),
$request->get('filter') ? (array) $request->get('categories') : $user->getCategories(),
$request->get('filter') ? ($request->get('sensitive') ? null : false) : ($user->isSensitive() ? false : null),
!$user->isModerator() ? true : null,
!$user->isModerator() ? true : null,
);
@ -644,11 +644,11 @@ class TorrentController extends AbstractController
// Create torrents list
$torrents = [];
foreach ($torrentService->findTorrents(
$user->getId(),
$request->get('filter') ? 0 : $user->getId(),
$query,
$user->getLocales(),
$user->getCategories(),
$user->isSensitive() ? false : null,
$request->get('filter') ? (array) $request->get('locales') : $user->getLocales(),
$request->get('filter') ? (array) $request->get('categories') : $user->getCategories(),
$request->get('filter') ? ($request->get('sensitive') ? null : false) : ($user->isSensitive() ? false : null),
!$user->isModerator() ? true : null,
!$user->isModerator() ? true : null,
$this->getParameter('app.pagination'),

View file

@ -323,6 +323,11 @@ class TorrentService
{
$contributors = [];
foreach ($this->findTorrentCategoriesByTorrentId($torrent->getId()) as $torrentCategory)
{
$contributors[] = $torrentCategory->getUserId();
}
foreach ($this->findTorrentLocalesByTorrentId($torrent->getId()) as $torrentLocale)
{
$contributors[] = $torrentLocale->getUserId();
@ -333,6 +338,11 @@ class TorrentService
$contributors[] = $torrentSensitive->getUserId();
}
foreach ($this->findTorrentPosterByTorrentId($torrent->getId()) as $torrentPoster)
{
$contributors[] = $torrentPoster->getUserId();
}
$contributors[] = $torrent->getUserId();
return array_unique($contributors);

View file

@ -154,7 +154,7 @@ class AppExtension extends AbstractExtension
): string
{
return preg_replace(
'~(https?://(?:www\.)?[^\s]+)~i',
'~(https?://(?:www\.)?[^\(\s\)]+)~i',
'[$1]($1)',
$text
);

View file

@ -16,18 +16,19 @@
<header>
<div class="container">
<div class="row margin-y-16-px text-center">
<a class="logo display-inline-block margin-b-8-px" href="{{ path('torrent_recent') }}">
<a class="logo display-inline-block{#32 margin-b-8-px #}" href="{{ path('torrent_recent') }}">
<span>YGG</span>tracker
</a>
{#32
<div class="font-size-10-px text-center text-color-night">
{{ '<a href="%s">Upload</a> any torrent - download with Yggdrasil' | trans | format(path('torrent_submit')) | raw }}
</div>
#}
{% block header_search %}
{{ render(controller(
'App\\Controller\\SearchController::module',
{
query : app.request.get('query'),
type : app.request.get('type')
request: app.request
}
)) }}
{% endblock %}

View file

@ -1,4 +1,69 @@
<form class="margin-t-16-px" name="search" method="get" action="{{ path('torrent_search') }}">
<input class="min-width-200-px" type="text" name="query" value="{{ query }}" placeholder="{{ 'Keyword, file, hash...' | trans }}" />
<input {% if query %}class="button-green"{% endif %} type="submit" value="{{ 'Search' | trans }}" />
{% if filter %}
<input type="hidden" name="filter" value="true" />
<div class="text-left">
<div class="margin-t-16-px margin-b-4-px padding-x-4-px text-right">
{{ 'Locales' | trans }}
</div>
<div class="padding-t-16-px padding-b-8-px padding-x-24-px border-top-default">
{% for locale in locales | sort %}
{#{% if locale.total %}#}
<div class="margin-t-4-px margin-b-8-px margin-r-8-px display-inline-block min-width-120-px">
{% if locale.checked %}
<input name="locales[]" id="{{ locale.value }}" type="checkbox" value="{{ locale.value }}" checked="checked" />
{% else %}
<input name="locales[]" id="{{ locale.value }}" type="checkbox" value="{{ locale.value }}" />
{% endif %}
<label class="margin-x-4-px" for="{{ locale.value }}">
{{ locale.value | locale_name(locale.value) | u.title }}
<span class="text-color-night">
({{ locale.total }})
</span>
</label>
</div>
{#{% endif %}#}
{% endfor %}
</div>
<div class="margin-b-4-px padding-x-4-px text-right">
{{ 'Categories' | trans }}
</div>
<div class="padding-t-16-px padding-b-8-px padding-x-24-px border-top-default">
{% for category in categories | sort %}
{#{% if category.total %}#}
<div class="margin-t-4-px margin-b-8-px margin-r-8-px display-inline-block min-width-120-px">
{% if category.checked %}
<input name="categories[]" id="{{ category.value }}" type="checkbox" value="{{ category.value }}" checked="checked" />
{% else %}
<input name="categories[]" id="{{ category.value }}" type="checkbox" value="{{ category.value }}" />
{% endif %}
<label class="margin-x-4-px" for="{{ category.value }}">
{{ category.value | trans_category | u.title }}
<span class="text-color-night">
({{ category.total }})
</span>
</label>
</div>
{#{% endif %}#}
{% endfor %}
</div>
<div class="margin-b-4-px padding-x-4-px text-right">
{{ 'other' | trans | u.title }}
</div>
<div class="padding-t-16-px padding-b-8-px padding-x-24-px border-top-default">
{% if sensitive.checked %}
<input name="sensitive" id="sensitive" type="checkbox" value="true" checked="checked" />
{% else %}
<input name="sensitive" id="sensitive" type="checkbox" value="true" />
{% endif %}
<label class="margin-x-4-px" for="sensitive">
{{ 'Sensitive' | trans }}
<span class="text-color-night">
({{ sensitive.total }})
</span>
</label>
</div>
</div>
{% endif %}
</form>

View file

@ -96,14 +96,14 @@
<td class="padding-t-16-px">
{% if torrent.status %}
{{ 'Yes' | trans }}
<a class="float-right" href="{{ path('torrent_status_toggle', { torrentId : torrent.id }) }}" title="{{ 'Toggle' | trans }}">
<a rel="nofollow" class="float-right" href="{{ path('torrent_status_toggle', { torrentId : torrent.id }) }}" title="{{ 'Toggle' | trans }}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M5 3a5 5 0 0 0 0 10h6a5 5 0 0 0 0-10H5zm6 9a4 4 0 1 1 0-8 4 4 0 0 1 0 8z"/>
</svg>
</a>
{% else %}
{{ 'No' | trans }}
<a class="float-right text-color-red" href="{{ path('torrent_status_toggle', { torrentId : torrent.id }) }}" title="{{ 'Toggle' | trans }}">
<a rel="nofollow" class="float-right text-color-red" href="{{ path('torrent_status_toggle', { torrentId : torrent.id }) }}" title="{{ 'Toggle' | trans }}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M11 4a4 4 0 0 1 0 8H8a4.992 4.992 0 0 0 2-4 4.992 4.992 0 0 0-2-4h3zm-6 8a4 4 0 1 1 0-8 4 4 0 0 1 0 8zM0 8a5 5 0 0 0 5 5h6a5 5 0 0 0 0-10H5a5 5 0 0 0-5 5z"/>
</svg>
@ -119,14 +119,14 @@
<td>
{% if torrent.approved %}
{{ 'Yes' | trans }}
<a class="float-right" href="{{ path('torrent_approve_toggle', { torrentId : torrent.id }) }}" title="{{ 'Toggle' | trans }}">
<a rel="nofollow" class="float-right" href="{{ path('torrent_approve_toggle', { torrentId : torrent.id }) }}" title="{{ 'Toggle' | trans }}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M5 3a5 5 0 0 0 0 10h6a5 5 0 0 0 0-10H5zm6 9a4 4 0 1 1 0-8 4 4 0 0 1 0 8z"/>
</svg>
</a>
{% else %}
{{ 'No' | trans }}
<a class="float-right text-color-red" href="{{ path('torrent_approve_toggle', { torrentId : torrent.id }) }}" title="{{ 'Toggle' | trans }}">
<a rel="nofollow" class="float-right text-color-red" href="{{ path('torrent_approve_toggle', { torrentId : torrent.id }) }}" title="{{ 'Toggle' | trans }}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M11 4a4 4 0 0 1 0 8H8a4.992 4.992 0 0 0 2-4 4.992 4.992 0 0 0-2-4h3zm-6 8a4 4 0 1 1 0-8 4 4 0 0 1 0 8zM0 8a5 5 0 0 0 5 5h6a5 5 0 0 0 0-10H5a5 5 0 0 0-5 5z"/>
</svg>
@ -220,13 +220,26 @@
</td>
</tr>
{% endif %}
<tr>
<td>
{{ 'Private' | trans }}
</td>
<td>
{% if file.private %}
{{ 'Yes' | trans }}
{% else %}
{{ 'No' | trans }}
{% endif %}
</td>
</tr>
{% if file.source %}
<tr>
<td>
{{ 'Source' | trans }}
</td>
<td>
{{ file.source | url_to_markdown | markdown_to_html }}
{# strip all tags then apply whitelist markdown filters to prevent ping from remote #}
{{ file.source | trim | striptags | url_to_markdown | markdown_to_html | nl2br }}
</td>
</tr>
{% endif %}
@ -236,7 +249,8 @@
{{ 'Software' | trans }}
</td>
<td>
{{ file.software }}
{# strip all tags then apply whitelist markdown filters to prevent ping from remote #}
{{ file.software | trim | striptags | url_to_markdown | markdown_to_html | nl2br }}
</td>
</tr>
{% endif %}
@ -246,7 +260,8 @@
{{ 'Comment' | trans }}
</td>
<td>
{{ file.comment | url_to_markdown | markdown_to_html }}
{# strip all tags then apply whitelist markdown filters to prevent ping from remote #}
{{ file.comment | trim | striptags | url_to_markdown | markdown_to_html | nl2br }}
</td>
</tr>
{% endif %}
@ -334,7 +349,7 @@
{% endfor %}
</div>
<div class="text-right">
<a class="margin-r-4-px{#opacity-0 parent-hover-opacity-09#}" href="{{ path('torrent_locales_edit', { torrentId : torrent.id }) }}" title="{{'Edit'|trans }}">
<a rel="nofollow" class="margin-r-4-px{#opacity-0 parent-hover-opacity-09#}" href="{{ path('torrent_locales_edit', { torrentId : torrent.id }) }}" title="{{ 'Edit' | trans }}">
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" viewBox="0 0 16 16">
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
</svg>
@ -351,7 +366,7 @@
</div>
</div>
<div class="text-right">
<a class="margin-r-4-px{#opacity-0 parent-hover-opacity-09#}" href="{{ path('torrent_categories_edit', { torrentId : torrent.id }) }}" title="{{'Edit'|trans }}">
<a rel="nofollow" class="margin-r-4-px{#opacity-0 parent-hover-opacity-09#}" href="{{ path('torrent_categories_edit', { torrentId : torrent.id }) }}" title="{{ 'Edit' | trans }}">
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" viewBox="0 0 16 16">
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
</svg>
@ -368,7 +383,7 @@
</div>
</div>
<div class="text-right">
<a class="margin-r-4-px{#opacity-0 parent-hover-opacity-09#}" href="{{ path('torrent_sensitive_edit', { torrentId : torrent.id }) }}" title="{{'Edit'|trans }}">
<a rel="nofollow" class="margin-r-4-px{#opacity-0 parent-hover-opacity-09#}" href="{{ path('torrent_sensitive_edit', { torrentId : torrent.id }) }}" title="{{ 'Edit' | trans }}">
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" viewBox="0 0 16 16">
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
</svg>
@ -384,7 +399,7 @@
</div>
</div>
<div class="text-right">
<a class="margin-r-4-px{#opacity-0 parent-hover-opacity-09#}" href="{{ path('torrent_poster_edit', { torrentId : torrent.id }) }}" title="{{'Edit'|trans }}">
<a rel="nofollow" class="margin-r-4-px{#opacity-0 parent-hover-opacity-09#}" href="{{ path('torrent_poster_edit', { torrentId : torrent.id }) }}" title="{{ 'Edit' | trans }}">
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" viewBox="0 0 16 16">
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
</svg>

View file

@ -5,11 +5,33 @@
{% if query %}
<h2>{{ 'Search results' | trans }}</h2>
<sub>
<a class="text-color-night margin-x-4-px" href="{{ path('rss_torrents_recent', { query : query | url_encode(true) }) }}" title="RSS">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0 8 8 0 0 0-8-8 1 1 0 0 1 0-2zm0 4a6 6 0 0 1 6 6 1 1 0 1 1-2 0 4 4 0 0 0-4-4 1 1 0 0 1 0-2zm.5 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z"/>
</svg>
</a>
{% if app.request.get('filter') %}
<a class="margin-l-4-px" rel="nofollow" href="{{ path('torrent_search', { query : query | url_encode(true) }) }}" title="{{ 'Hide filter' | trans }}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-filter-square-fill" viewBox="0 0 16 16">
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm.5 5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1 0-1zM4 8.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm2 3a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5z"/>
</svg>
</a>
<a class="text-color-night margin-x-4-px" href="{{ path('rss_torrents_recent', { query : query | url_encode(true),
locales : app.request.get('locales'),
categories : app.request.get('categories'),
sensitive : app.request.get('sensitive'),
filter : app.request.get('filter') }) }}" title="RSS">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0 8 8 0 0 0-8-8 1 1 0 0 1 0-2zm0 4a6 6 0 0 1 6 6 1 1 0 1 1-2 0 4 4 0 0 0-4-4 1 1 0 0 1 0-2zm.5 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z"/>
</svg>
</a>
{% else %}
<a class="text-color-night margin-l-4-px" rel="nofollow" href="{{ path('torrent_search', { query : query | url_encode(true), filter : 'true' }) }}" title="{{ 'Show filter' | trans }}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-filter-square-fill" viewBox="0 0 16 16">
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm.5 5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1 0-1zM4 8.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm2 3a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5z"/>
</svg>
</a>
<a class="text-color-night margin-x-4-px" href="{{ path('rss_torrents_recent', { query : query | url_encode(true) }) }}" title="RSS">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0 8 8 0 0 0-8-8 1 1 0 0 1 0-2zm0 4a6 6 0 0 1 6 6 1 1 0 1 1-2 0 4 4 0 0 0-4-4 1 1 0 0 1 0-2zm.5 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z"/>
</svg>
</a>
{% endif %}
</sub>
{% else %}
<h2>{{ 'Recent uploads' | trans }}</h2>
@ -36,7 +58,8 @@
{% if torrent.scrape.leechers > 0 and torrent.scrape.seeders == 0 %}
<a href="{{ path('torrent_file_wanted', { torrentId : torrent.id }) }}"
class="label label-green font-size-10-px position-relative display-inline-block"
title="{{ 'Active leechers waiting for seeders' | trans }}">
title="{{ 'Active leechers waiting for seeders' | trans }}"
rel="nofollow">
{{ 'wanted' | trans }}
</a>
{% endif %}
@ -80,7 +103,11 @@
{% if torrent.keywords %}
<div class="margin-b-16-px">
{% for keyword, quantity in torrent.keywords %}
<a href="{{ path('torrent_search', { query : keyword | url_encode(true) }) }}">
<a href="{{ path('torrent_search', { query : keyword | url_encode(true),
locales : app.request.get('locales'),
categories : app.request.get('categories'),
sensitive : app.request.get('sensitive'),
filter : app.request.get('filter') }) }}">
#{{ keyword }}
</a>
{% endfor %}
@ -169,17 +196,31 @@
{% if query %}
{% if pagination.page > 1 %}
{% if pagination.page == 2 %}
<a rel="nofollow" class="button margin-l-8-px" href="{{ path('torrent_search', { query : query | url_encode(true) | raw }) }}">
<a rel="nofollow" class="button margin-l-8-px" href="{{ path('torrent_search', { query : query | url_encode(true),
locales : app.request.get('locales'),
categories : app.request.get('categories'),
sensitive : app.request.get('sensitive'),
filter : app.request.get('filter') }) }}">
{{ 'Back' | trans | lower }}
</a>
{% else %}
<a rel="nofollow" class="button margin-l-8-px" href="{{ path('torrent_search', { query : query | url_encode(true), page : pagination.page - 1 }) }}">
<a rel="nofollow" class="button margin-l-8-px" href="{{ path('torrent_search', { query : query | url_encode(true),
page : pagination.page - 1,
locales : app.request.get('locales'),
categories : app.request.get('categories'),
sensitive : app.request.get('sensitive'),
filter : app.request.get('filter') }) }}">
{{ 'Back' | trans | lower }}
</a>
{% endif %}
{% endif %}
{% if pagination.page < pagination.pages %}
<a rel="nofollow" class="button margin-l-8-px" href="{{ path('torrent_search', { query : query | url_encode(true), page : pagination.page + 1 }) }}">
<a rel="nofollow" class="button margin-l-8-px" href="{{ path('torrent_search', { query : query | url_encode(true),
page : pagination.page + 1,
locales : app.request.get('locales'),
categories : app.request.get('categories'),
sensitive : app.request.get('sensitive'),
filter : app.request.get('filter') }) }}">
{{ 'Next' | trans | lower }}
</a>
{% endif %}

View file

@ -87,9 +87,9 @@
</div>
</td>
<td class="padding-t-16-px padding-b-8-px">
{% for locale in locales %}
{% for locale in locales | sort %}
<div class="margin-t-4-px margin-b-8-px margin-r-8-px display-inline-block min-width-120-px">
{% if locale in user.locales | sort %}
{% if locale in user.locales %}
<input name="locales[]" id="{{ locale }}" type="checkbox" value="{{ locale }}" checked="checked" />
{% else %}
<input name="locales[]" id="{{ locale }}" type="checkbox" value="{{ locale }}" />

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>other</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Hide filter</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Show filter</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Private</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>другое</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Скрыть фильтр</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Показать фильтр</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Приватный</target>
</trans-unit>
</body>
</file>
</xliff>

View file

@ -901,6 +901,18 @@
<source>other</source>
<target>інше</target>
</trans-unit>
<trans-unit id="v7fLnQX" resname="Hide filter">
<source>Hide filter</source>
<target>Приховати фільтр</target>
</trans-unit>
<trans-unit id="45aF7iu" resname="Show filter">
<source>Show filter</source>
<target>Показати фільтр</target>
</trans-unit>
<trans-unit id="xj62cgx" resname="Private">
<source>Private</source>
<target>Приватний</target>
</trans-unit>
</body>
</file>
</xliff>