Merged in feature/file_tree (pull request #48)
#12 File tree (bstreeview) implemented instead of file path list. A bit of refactoring. symfony/asset added and used in templates. symfony/flex upgraded due to bugfix.
This commit is contained in:
commit
af9b96f5a5
|
@ -16,11 +16,13 @@
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-hash": "*",
|
"ext-hash": "*",
|
||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
|
"ext-json": "*",
|
||||||
"babdev/pagerfanta-bundle": "^2.4",
|
"babdev/pagerfanta-bundle": "^2.4",
|
||||||
"excelwebzone/recaptcha-bundle": "^1.5",
|
"excelwebzone/recaptcha-bundle": "^1.5",
|
||||||
"sensio/framework-extra-bundle": "^5.1",
|
"sensio/framework-extra-bundle": "^5.1",
|
||||||
"sentry/sentry-symfony": "^3.4",
|
"sentry/sentry-symfony": "^3.4",
|
||||||
"suin/php-rss-writer": "^1.6",
|
"suin/php-rss-writer": "^1.6",
|
||||||
|
"symfony/asset": "^4.1",
|
||||||
"symfony/console": "^4.1",
|
"symfony/console": "^4.1",
|
||||||
"symfony/dotenv": "^4.1",
|
"symfony/dotenv": "^4.1",
|
||||||
"symfony/expression-language": "^4.1",
|
"symfony/expression-language": "^4.1",
|
||||||
|
|
81
composer.lock
generated
81
composer.lock
generated
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "1e62b51a6d3895dcabaf9c263dddca29",
|
"content-hash": "e5b436812dc4f90d4d85a90807e00c18",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "babdev/pagerfanta-bundle",
|
"name": "babdev/pagerfanta-bundle",
|
||||||
|
@ -3625,6 +3625,71 @@
|
||||||
],
|
],
|
||||||
"time": "2017-07-13T10:47:50+00:00"
|
"time": "2017-07-13T10:47:50+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/asset",
|
||||||
|
"version": "v4.4.18",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/asset.git",
|
||||||
|
"reference": "7339980f70621f26db6208a75a8ee2a48a7ede5b"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/asset/zipball/7339980f70621f26db6208a75a8ee2a48a7ede5b",
|
||||||
|
"reference": "7339980f70621f26db6208a75a8ee2a48a7ede5b",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1.3"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/http-foundation": "^3.4|^4.0|^5.0",
|
||||||
|
"symfony/http-kernel": "^3.4|^4.0|^5.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"symfony/http-foundation": ""
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Component\\Asset\\": ""
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony Asset Component",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2020-12-13T22:21:11+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/cache",
|
"name": "symfony/cache",
|
||||||
"version": "v5.2.0",
|
"version": "v5.2.0",
|
||||||
|
@ -4738,16 +4803,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/flex",
|
"name": "symfony/flex",
|
||||||
"version": "v1.10.0",
|
"version": "v1.11.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/flex.git",
|
"url": "https://github.com/symfony/flex.git",
|
||||||
"reference": "e38520236bdc911c2f219634b485bc328746e980"
|
"reference": "ceb2b4e612bd0b4bb36a4d7fb2e800c861652f48"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/flex/zipball/e38520236bdc911c2f219634b485bc328746e980",
|
"url": "https://api.github.com/repos/symfony/flex/zipball/ceb2b4e612bd0b4bb36a4d7fb2e800c861652f48",
|
||||||
"reference": "e38520236bdc911c2f219634b485bc328746e980",
|
"reference": "ceb2b4e612bd0b4bb36a4d7fb2e800c861652f48",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -4757,6 +4822,7 @@
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"composer/composer": "^1.0.2|^2.0",
|
"composer/composer": "^1.0.2|^2.0",
|
||||||
"symfony/dotenv": "^4.4|^5.0",
|
"symfony/dotenv": "^4.4|^5.0",
|
||||||
|
"symfony/filesystem": "^4.4|^5.0",
|
||||||
"symfony/phpunit-bridge": "^4.4|^5.0",
|
"symfony/phpunit-bridge": "^4.4|^5.0",
|
||||||
"symfony/process": "^3.4|^4.4|^5.0"
|
"symfony/process": "^3.4|^4.4|^5.0"
|
||||||
},
|
},
|
||||||
|
@ -4797,7 +4863,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2020-11-05T10:56:45+00:00"
|
"time": "2020-12-03T10:57:35+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/form",
|
"name": "symfony/form",
|
||||||
|
@ -8622,7 +8688,8 @@
|
||||||
"php": ">=7.4.0",
|
"php": ">=7.4.0",
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-hash": "*",
|
"ext-hash": "*",
|
||||||
"ext-iconv": "*"
|
"ext-iconv": "*",
|
||||||
|
"ext-json": "*"
|
||||||
},
|
},
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
"plugin-api-version": "1.1.0"
|
"plugin-api-version": "1.1.0"
|
||||||
|
|
|
@ -20,7 +20,7 @@ services:
|
||||||
|
|
||||||
App\:
|
App\:
|
||||||
resource: '../src/*'
|
resource: '../src/*'
|
||||||
exclude: '../src/{Api/V1/{DTO},Magnetico/{Entity,Migrations},Entity,FormRequest,Migrations,Tests,Kernel.php}'
|
exclude: '../src/{Api/V1/{DTO},Magnetico/{Entity,Migrations},Entity,FormRequest,Migrations,Tests,View,Kernel.php}'
|
||||||
# Use array in exclude config from Symfony 4.2
|
# Use array in exclude config from Symfony 4.2
|
||||||
#- '../src/Api/V1/{DTO}'
|
#- '../src/Api/V1/{DTO}'
|
||||||
#- '../src/Magnetico/{Entity,Migrations}'
|
#- '../src/Magnetico/{Entity,Migrations}'
|
||||||
|
|
10
public/assets/bstreeview/css/bstreeview.min.css
vendored
Normal file
10
public/assets/bstreeview/css/bstreeview.min.css
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
@preserve
|
||||||
|
bstreeview.css
|
||||||
|
Version: 1.2.0
|
||||||
|
Authors: Sami CHNITER <sami.chniter@gmail.com>
|
||||||
|
Copyright 2020
|
||||||
|
License: Apache License 2.0
|
||||||
|
Project: https://github.com/chniter/bstreeview
|
||||||
|
*/
|
||||||
|
.bstreeview{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem;padding:0;overflow:hidden}.bstreeview .list-group{margin-bottom:0}.bstreeview .list-group-item{border-radius:0;border-width:1px 0 0 0;padding-top:.5rem;padding-bottom:.5rem;cursor:pointer}.bstreeview .list-group-item:hover{background-color:#dee2e6}.bstreeview>.list-group-item:first-child{border-top-width:0}.bstreeview .state-icon{margin-right:8px}.bstreeview .item-icon{margin-right:5px}
|
10
public/assets/bstreeview/js/bstreeview.min.js
vendored
Normal file
10
public/assets/bstreeview/js/bstreeview.min.js
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
@preserve
|
||||||
|
bstreeview.js
|
||||||
|
Version: 1.2.0
|
||||||
|
Authors: Sami CHNITER <sami.chniter@gmail.com>
|
||||||
|
Copyright 2020
|
||||||
|
License: Apache License 2.0
|
||||||
|
Project: https://github.com/chniter/bstreeview
|
||||||
|
*/
|
||||||
|
!function (t, e, i, s) { "use strict"; var n = { expandIcon: "fa fa-angle-down fa-fw", collapseIcon: "fa fa-angle-right fa-fw", indent: 1.25, parentsMarginLeft: "1.25rem", openNodeLinkOnNewTab: !0 }, a = '<div role="treeitem" class="list-group-item" data-toggle="collapse"></div>', d = '<div role="group" class="list-group collapse" id="itemid"></div>', o = '<i class="state-icon"></i>', r = '<i class="item-icon"></i>'; function l(e, i) { this.element = e, this.itemIdPrefix = e.id + "-item-", this.settings = t.extend({}, n, i), this.init() } t.extend(l.prototype, { init: function () { this.tree = [], this.nodes = [], this.settings.data && (this.settings.data.isPrototypeOf(String) && (this.settings.data = t.parseJSON(this.settings.data)), this.tree = t.extend(!0, [], this.settings.data), delete this.settings.data), t(this.element).addClass("bstreeview"), this.initData({ nodes: this.tree }); var i = this; this.build(t(this.element), this.tree, 0), t(this.element).on("click", ".list-group-item", function (s) { t(".state-icon", this).toggleClass(i.settings.expandIcon).toggleClass(i.settings.collapseIcon), s.target.hasAttribute("href") && (i.settings.openNodeLinkOnNewTab ? e.open(s.target.getAttribute("href"), "_blank") : e.location = s.target.getAttribute("href")) }) }, initData: function (e) { if (e.nodes) { var i = e, s = this; t.each(e.nodes, function (t, e) { e.nodeId = s.nodes.length, e.parentId = i.nodeId, s.nodes.push(e), e.nodes && s.initData(e) }) } }, build: function (e, i, s) { var n = this, l = n.settings.parentsMarginLeft; s > 0 && (l = (n.settings.indent + s * n.settings.indent).toString() + "rem;"), s += 1, t.each(i, function (i, g) { var h = t(a).attr("data-target", "#" + n.itemIdPrefix + g.nodeId).attr("style", "padding-left:" + l).attr("aria-level", s); if (g.nodes) { var c = t(o).addClass(n.settings.collapseIcon); h.append(c) } if (g.icon) { var f = t(r).addClass(g.icon); h.append(f) } if (h.append(g.text), g.href && h.attr("href", g.href), g.class && h.addClass(g.class), g.id && h.attr("id", g.id), e.append(h), g.nodes) { var p = t(d).attr("id", n.itemIdPrefix + g.nodeId); e.append(p), n.build(p, g.nodes, s) } }) } }), t.fn.bstreeview = function (e) { return this.each(function () { t.data(this, "plugin_bstreeview") || t.data(this, "plugin_bstreeview", new l(this, e)) }) } }(jQuery, window, document);
|
|
@ -5,6 +5,7 @@ namespace App\Controller;
|
||||||
use App\Magnetico\Entity\Torrent;
|
use App\Magnetico\Entity\Torrent;
|
||||||
use App\Search\TorrentSearcher;
|
use App\Search\TorrentSearcher;
|
||||||
use App\Pager\PagelessDoctrineORMAdapter;
|
use App\Pager\PagelessDoctrineORMAdapter;
|
||||||
|
use App\View\Torrent\FileTreeNode;
|
||||||
use Pagerfanta\Pagerfanta;
|
use Pagerfanta\Pagerfanta;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\{Request, Response};
|
use Symfony\Component\HttpFoundation\{Request, Response};
|
||||||
|
@ -38,6 +39,7 @@ class TorrentController extends AbstractController
|
||||||
{
|
{
|
||||||
return $this->render('torrent_show.html.twig', [
|
return $this->render('torrent_show.html.twig', [
|
||||||
'torrent' => $torrent,
|
'torrent' => $torrent,
|
||||||
|
'files' => FileTreeNode::createFromTorrent($torrent),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
67
src/Helper/BstreeviewFileTreeBuilder.php
Normal file
67
src/Helper/BstreeviewFileTreeBuilder.php
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Helper;
|
||||||
|
|
||||||
|
use App\Magnetico\Entity\Torrent;
|
||||||
|
use App\View\Torrent\FileTreeNode;
|
||||||
|
|
||||||
|
class BstreeviewFileTreeBuilder
|
||||||
|
{
|
||||||
|
private const DEFAULT_FILE_ICON = 'fas fa-file';
|
||||||
|
private const DEFAULT_DIR_ICON = 'fas fa-folder';
|
||||||
|
|
||||||
|
private FileSizeHumanizer $humanizer;
|
||||||
|
|
||||||
|
public function __construct(FileSizeHumanizer $humanizer)
|
||||||
|
{
|
||||||
|
$this->humanizer = $humanizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildFileTreeDataArrayFromTorrent(
|
||||||
|
Torrent $torrent,
|
||||||
|
?string $fileIcon = self::DEFAULT_FILE_ICON,
|
||||||
|
?string $dirIcon = self::DEFAULT_DIR_ICON
|
||||||
|
): array {
|
||||||
|
return $this->buildFileTreeDataArray(
|
||||||
|
FileTreeNode::createFromTorrent($torrent),
|
||||||
|
$fileIcon,
|
||||||
|
$dirIcon
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildFileTreeDataArray(
|
||||||
|
FileTreeNode $node,
|
||||||
|
?string $fileIcon = self::DEFAULT_FILE_ICON,
|
||||||
|
?string $dirIcon = self::DEFAULT_DIR_ICON
|
||||||
|
): array {
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($node->getChildren() as $name => $child) {
|
||||||
|
$element = [
|
||||||
|
'text' => '<strong>'.$name.'</strong>',
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($child->isDirectory()) {
|
||||||
|
$element['nodes'] = $this->buildFileTreeDataArray($child, $fileIcon, $dirIcon);
|
||||||
|
|
||||||
|
if ($dirIcon) {
|
||||||
|
$element['icon'] = $dirIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding number of chilren
|
||||||
|
$element['text'] .= ' ['.$child->countChildren().']';
|
||||||
|
} else {
|
||||||
|
if ($fileIcon) {
|
||||||
|
$element['icon'] = $fileIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding file size.
|
||||||
|
$element['text'] .= ' ('.$this->humanizer->humanize($child->getSize()).')';
|
||||||
|
}
|
||||||
|
|
||||||
|
$data[] = $element;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Twig;
|
namespace App\Helper;
|
||||||
|
|
||||||
use Twig\Extension\AbstractExtension;
|
class FileSizeHumanizer
|
||||||
use Twig\TwigFilter;
|
|
||||||
|
|
||||||
class HumanReadableSizeExtension extends AbstractExtension
|
|
||||||
{
|
{
|
||||||
// Can't really exceed 'EB' on 64-bit platform but let it go
|
// Can't really exceed 'EB' on 64-bit platform but let it go
|
||||||
private const SIZE_SUFFIXES = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
private const SIZE_SUFFIXES = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
||||||
|
@ -21,14 +18,7 @@ class HumanReadableSizeExtension extends AbstractExtension
|
||||||
$this->binaryPrefix = $useBinaryPrefix;
|
$this->binaryPrefix = $useBinaryPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFilters(): array
|
public function humanize(int $bytes, int $decimals = 2, bool $forceBinary = false): string
|
||||||
{
|
|
||||||
return [
|
|
||||||
new TwigFilter('readable_size', [$this, 'humanizeSize']),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function humanizeSize(int $bytes, int $decimals = 2, bool $forceBinary = false): string
|
|
||||||
{
|
{
|
||||||
$bytesString = (string) $bytes;
|
$bytesString = (string) $bytes;
|
||||||
|
|
||||||
|
@ -49,4 +39,4 @@ class HumanReadableSizeExtension extends AbstractExtension
|
||||||
|
|
||||||
return sprintf("%.{$decimals}f %s", $bytes / ($sizeDivider ** $suffixIndex), $suffix);
|
return sprintf("%.{$decimals}f %s", $bytes / ($sizeDivider ** $suffixIndex), $suffix);
|
||||||
}
|
}
|
||||||
}
|
}
|
24
src/Twig/FileSizeHumanizerExtension.php
Normal file
24
src/Twig/FileSizeHumanizerExtension.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Twig;
|
||||||
|
|
||||||
|
use App\Helper\FileSizeHumanizer;
|
||||||
|
use Twig\Extension\AbstractExtension;
|
||||||
|
use Twig\TwigFilter;
|
||||||
|
|
||||||
|
class FileSizeHumanizerExtension extends AbstractExtension
|
||||||
|
{
|
||||||
|
private FileSizeHumanizer $humanizer;
|
||||||
|
|
||||||
|
public function __construct(FileSizeHumanizer $humanizer)
|
||||||
|
{
|
||||||
|
$this->humanizer = $humanizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilters(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new TwigFilter('humanize_size', [$this->humanizer, 'humanize']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
25
src/Twig/FileTreeExtension.php
Normal file
25
src/Twig/FileTreeExtension.php
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Twig;
|
||||||
|
|
||||||
|
use App\Helper\BstreeviewFileTreeBuilder;
|
||||||
|
use Twig\Extension\AbstractExtension;
|
||||||
|
use Twig\{TwigFilter};
|
||||||
|
|
||||||
|
class FileTreeExtension extends AbstractExtension
|
||||||
|
{
|
||||||
|
private BstreeviewFileTreeBuilder $builder;
|
||||||
|
|
||||||
|
public function __construct(BstreeviewFileTreeBuilder $builder)
|
||||||
|
{
|
||||||
|
$this->builder = $builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilters()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new TwigFilter('file_tree', [$this->builder, 'buildFileTreeDataArray']),
|
||||||
|
new TwigFilter('torrent_file_tree', [$this->builder, 'buildFileTreeDataArrayFromTorrent']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
146
src/View/Torrent/FileTreeNode.php
Normal file
146
src/View/Torrent/FileTreeNode.php
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Torrent;
|
||||||
|
|
||||||
|
use App\Magnetico\Entity\File;
|
||||||
|
use App\Magnetico\Entity\Torrent;
|
||||||
|
|
||||||
|
class FileTreeNode
|
||||||
|
{
|
||||||
|
private ?string $name;
|
||||||
|
|
||||||
|
private bool $isDir = true;
|
||||||
|
|
||||||
|
private ?int $size;
|
||||||
|
|
||||||
|
private ?FileTreeNode $parent;
|
||||||
|
|
||||||
|
/** @var FileTreeNode[]|File[] */
|
||||||
|
private array $children = [];
|
||||||
|
|
||||||
|
private function __construct(string $name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function createFromTorrent(Torrent $torrent): FileTreeNode
|
||||||
|
{
|
||||||
|
$node = new static($torrent->getName());
|
||||||
|
|
||||||
|
foreach ($torrent->getFiles() as $file) {
|
||||||
|
$node->addFileToPath($file->getPath(), $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function createFromFile(string $name, File $file, ?FileTreeNode $parent): FileTreeNode
|
||||||
|
{
|
||||||
|
$node = new static($name);
|
||||||
|
$node->isDir = false;
|
||||||
|
$node->size = $file->getSize();
|
||||||
|
$node->parent = $parent;
|
||||||
|
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addFileToPath(string $path, File $file): void
|
||||||
|
{
|
||||||
|
$path = ltrim($path, '/');
|
||||||
|
|
||||||
|
$pathParts = explode('/', $path);
|
||||||
|
|
||||||
|
// If we have file only file and not a tree.
|
||||||
|
if (1 === count($pathParts)) {
|
||||||
|
$this->addChild($path, FileTreeNode::createFromFile($path, $file, $this));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a file in a tree.
|
||||||
|
$childNodeName = array_shift($pathParts);
|
||||||
|
$childNodeChildPath = implode('/', $pathParts);
|
||||||
|
|
||||||
|
if (!$this->hasChild($childNodeName)) {
|
||||||
|
$childNode = new static($childNodeName);
|
||||||
|
$childNode->parent = $this;
|
||||||
|
$this->addChild($childNodeName, $childNode);
|
||||||
|
} else {
|
||||||
|
$childNode = $this->getChild($childNodeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
$childNode->addFileToPath($childNodeChildPath, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addChild(string $name, FileTreeNode $element, bool $overwriteDuplicates = false): void
|
||||||
|
{
|
||||||
|
if (!$overwriteDuplicates && array_key_exists($name, $this->children)) {
|
||||||
|
throw new \RuntimeException(sprintf(
|
||||||
|
'Child \'%s\' already exist.',
|
||||||
|
$name
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->children[$name] = $element;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasChild(string $name): bool
|
||||||
|
{
|
||||||
|
return array_key_exists($name, $this->children);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
*
|
||||||
|
* @return FileTreeNode|File
|
||||||
|
*/
|
||||||
|
public function getChild(string $name)
|
||||||
|
{
|
||||||
|
if (!array_key_exists($name, $this->children)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf(
|
||||||
|
'Node has no \'%s\' child.',
|
||||||
|
$name
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->children[$name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return File[]|FileTreeNode[]
|
||||||
|
*/
|
||||||
|
public function getChildren(bool $dirsFirst = true): array
|
||||||
|
{
|
||||||
|
if (!$dirsFirst) {
|
||||||
|
return $this->children;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dirs = [];
|
||||||
|
$files = [];
|
||||||
|
|
||||||
|
foreach ($this->children as $name => $child) {
|
||||||
|
if ($child instanceof FileTreeNode) {
|
||||||
|
$dirs[$name] = $child;
|
||||||
|
} elseif ($child instanceof File) {
|
||||||
|
$files[] = $child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_merge($dirs, $files);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isDirectory(): bool
|
||||||
|
{
|
||||||
|
return $this->isDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countChildren(): int
|
||||||
|
{
|
||||||
|
return count($this->children);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSize(): ?int
|
||||||
|
{
|
||||||
|
return $this->size;
|
||||||
|
}
|
||||||
|
}
|
12
symfony.lock
12
symfony.lock
|
@ -5,6 +5,9 @@
|
||||||
"clue/stream-filter": {
|
"clue/stream-filter": {
|
||||||
"version": "v1.4.1"
|
"version": "v1.4.1"
|
||||||
},
|
},
|
||||||
|
"composer/package-versions-deprecated": {
|
||||||
|
"version": "1.11.99.1"
|
||||||
|
},
|
||||||
"doctrine/annotations": {
|
"doctrine/annotations": {
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
@ -111,6 +114,12 @@
|
||||||
"jean85/pretty-package-versions": {
|
"jean85/pretty-package-versions": {
|
||||||
"version": "1.2"
|
"version": "1.2"
|
||||||
},
|
},
|
||||||
|
"laminas/laminas-code": {
|
||||||
|
"version": "3.5.0"
|
||||||
|
},
|
||||||
|
"laminas/laminas-eventmanager": {
|
||||||
|
"version": "3.3.0"
|
||||||
|
},
|
||||||
"laminas/laminas-zendframework-bridge": {
|
"laminas/laminas-zendframework-bridge": {
|
||||||
"version": "1.0.1"
|
"version": "1.0.1"
|
||||||
},
|
},
|
||||||
|
@ -207,6 +216,9 @@
|
||||||
"suin/php-rss-writer": {
|
"suin/php-rss-writer": {
|
||||||
"version": "1.6.0"
|
"version": "1.6.0"
|
||||||
},
|
},
|
||||||
|
"symfony/asset": {
|
||||||
|
"version": "v4.4.18"
|
||||||
|
},
|
||||||
"symfony/cache": {
|
"symfony/cache": {
|
||||||
"version": "v4.1.0"
|
"version": "v4.1.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,11 +12,11 @@
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
<!-- Bootstrap core CSS -->
|
<!-- Bootstrap core CSS -->
|
||||||
<link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
<link href="{{ asset('assets/bootstrap/css/bootstrap.min.css') }}" rel="stylesheet">
|
||||||
<!-- Font Awesome -->
|
<!-- Font Awesome -->
|
||||||
<link href="/assets/fontawesome/css/all.css" rel="stylesheet">
|
<link href="{{ asset('assets/fontawesome/css/all.css') }}" rel="stylesheet">
|
||||||
<!-- SIte style -->
|
<!-- SIte style -->
|
||||||
<link href="/assets/magnetico-web/css/style.css" rel="stylesheet">
|
<link href="{{ asset('assets/magnetico-web/css/style.css') }}" rel="stylesheet">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -81,9 +81,9 @@
|
||||||
</main><!-- /.container -->
|
</main><!-- /.container -->
|
||||||
|
|
||||||
{% block javascript %}
|
{% block javascript %}
|
||||||
<script src="/assets/jquery/js/jquery-3.5.1.slim.min.js"></script>
|
<script src="{{ asset('assets/jquery/js/jquery-3.5.1.slim.min.js') }}"></script>
|
||||||
<script src="/assets/popper/js/popper.min.js"></script>
|
<script src="{{ asset('assets/popper/js/popper.min.js') }}"></script>
|
||||||
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
<script src="{{ asset('assets/bootstrap/js/bootstrap.min.js') }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
10
templates/torrent_files.html.twig
Normal file
10
templates/torrent_files.html.twig
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="file-tree"></div>
|
||||||
|
|
||||||
|
{# @var \App\Magnetico\Entity\Torrent torrent #}
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.addEventListener('DOMContentLoaded', function(){
|
||||||
|
let tree = {{ torrent | torrent_file_tree | json_encode | raw }};
|
||||||
|
|
||||||
|
$('.file-tree').bstreeview({data: tree});
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -21,7 +21,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{ magnet(torrent.infoHash, torrent.name) }}">🔗</a></td>
|
<td><a href="{{ magnet(torrent.infoHash, torrent.name) }}">🔗</a></td>
|
||||||
<td><a href="{{ path('torrents_show', {'id': torrent.id}) }}">{{ torrent.name }}</a></td>
|
<td><a href="{{ path('torrents_show', {'id': torrent.id}) }}">{{ torrent.name }}</a></td>
|
||||||
<td>{{ torrent.totalSize | readable_size }}</td>
|
<td>{{ torrent.totalSize | humanize_size }}</td>
|
||||||
<td>{{ torrent.discoveredOn | date('Y-m-d H:i:s')}}</td>
|
<td>{{ torrent.discoveredOn | date('Y-m-d H:i:s')}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
{% extends 'base.html.twig' %}
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{{ parent() }}
|
||||||
|
<!-- BSTreeView plugin -->
|
||||||
|
<link href="{{ asset('assets/bstreeview/css/bstreeview.min.css') }}" rel="stylesheet">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block javascript %}
|
||||||
|
{{ parent() }}
|
||||||
|
<!-- BSTreeView plugin -->
|
||||||
|
<script src="{{ asset('assets/bstreeview/js/bstreeview.min.js') }}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{# @var torrent \App\Magnetico\Entity\Torrent #}
|
{# @var torrent \App\Magnetico\Entity\Torrent #}
|
||||||
<table class="table">
|
<table class="table">
|
||||||
|
@ -16,7 +28,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Size</td>
|
<td>Size</td>
|
||||||
<td><abbr title="{{ torrent.totalSize }} bytes">{{ torrent.totalSize | readable_size }}</abbr></td>
|
<td><abbr title="{{ torrent.totalSize }} bytes">{{ torrent.totalSize | humanize_size }}</abbr></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Discovered</td>
|
<td>Discovered</td>
|
||||||
|
@ -24,21 +36,5 @@
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<table class="table">
|
{% include 'torrent_files.html.twig' with {'torrent': torrent} only %}
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">File</th>
|
|
||||||
<th scope="col">Size</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{# @var file \App\Magnetico\Entity\File #}
|
|
||||||
{% for file in torrent.files | sort %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ file.path }}</td>
|
|
||||||
<td>{{ file.size | readable_size }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
Loading…
Reference in a new issue