implement pagination

This commit is contained in:
yggverse 2025-08-05 17:59:45 +03:00
parent d014358028
commit 4e4f260190
5 changed files with 67 additions and 36 deletions

View file

@ -14,7 +14,7 @@ pub struct Config {
/// Default listing limit
#[arg(long, default_value_t = 50)]
pub limit: usize,
pub list_limit: usize,
/// Default capacity (estimated torrents in `storage`)
#[arg(long, default_value_t = 1000)]

View file

@ -30,8 +30,12 @@ pub struct Meta {
pub trackers: Option<HashSet<Url>>,
}
#[get("/")]
fn index(storage: &State<Storage>, meta: &State<Meta>) -> Result<Template, Custom<String>> {
#[get("/?<page>")]
fn index(
page: Option<usize>,
storage: &State<Storage>,
meta: &State<Meta>,
) -> Result<Template, Custom<String>> {
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
struct Row {
@ -41,25 +45,32 @@ fn index(storage: &State<Storage>, meta: &State<Meta>) -> Result<Template, Custo
size: String,
torrent: Torrent,
}
let rows = storage
.torrents(
Some((Sort::Modified, Order::Asc)),
page.map(|p| if p > 0 { p - 1 } else { p } * storage.default_limit),
Some(storage.default_limit),
)
.map_err(|e| Custom(Status::InternalServerError, e.to_string()))?
.into_iter()
.map(|torrent| Row {
created: torrent
.creation_date
.map(|t| t.format(&meta.format_time).to_string()),
indexed: torrent.time.format(&meta.format_time).to_string(),
magnet: format::magnet(&torrent.info_hash, meta.trackers.as_ref()),
size: format::bytes(torrent.size),
torrent,
})
.collect::<Vec<Row>>();
Ok(Template::render(
"index",
context! {
meta: meta.inner(),
rows: storage
.torrents(
Some((Sort::Modified, Order::Asc)),
Some(storage.default_limit),
)
.map_err(|e| Custom(Status::InternalServerError, e.to_string()))?
.into_iter()
.map(|torrent| Row {
created: torrent.creation_date.map(|t|t.format(&meta.format_time).to_string()),
indexed: torrent.time.format(&meta.format_time).to_string(),
magnet: format::magnet(&torrent.info_hash, meta.trackers.as_ref()),
size: format::bytes(torrent.size),
torrent,
})
.collect::<Vec<Row>>()
back: page.map(|p| uri!(index(if p > 2 { Some(p - 1) } else { None }))),
next: if rows.len() < storage.default_limit { None }
else { Some(uri!(index(Some(page.map_or(2, |p| p + 1))))) },
rows
},
))
}
@ -70,6 +81,7 @@ fn rss(feed: &State<Feed>, storage: &State<Storage>) -> Result<RawXml<String>, C
for torrent in storage
.torrents(
Some((Sort::Modified, Order::Asc)),
None,
Some(storage.default_limit),
)
.map_err(|e| Custom(Status::InternalServerError, e.to_string()))?
@ -89,7 +101,7 @@ fn rocket() -> _ {
config.link.clone(),
config.tracker.clone().map(|u| u.into_iter().collect()),
);
let storage = Storage::init(config.storage, config.limit, config.capacity).unwrap(); // @TODO handle
let storage = Storage::init(config.storage, config.list_limit, config.capacity).unwrap(); // @TODO handle
rocket::build()
.attach(Template::fairing())
.configure(rocket::Config {

View file

@ -74,12 +74,13 @@ impl Storage {
pub fn torrents(
&self,
sort_order: Option<(Sort, Order)>,
start: Option<usize>,
limit: Option<usize>,
) -> Result<Vec<Torrent>, String> {
let f = self.files(sort_order)?;
let l = limit.unwrap_or(f.len());
let mut b = Vec::with_capacity(l);
for file in f.into_iter().take(l) {
for file in f.into_iter().skip(start.unwrap_or_default()).take(l) {
if file
.path()
.extension()

View file

@ -1,22 +1,30 @@
{% extends "layout/default" %}
{% block content %}
{% for row in rows %}
<div>
<a name="{{ row.torrent.info_hash }}"></a>
<h2>{{ row.torrent.name }}</h2>
{% if row.torrent.comment %}<p>{{ row.torrent.comment }}</p>{% endif %}
{% if rows %}
{% for row in rows %}
<div>
<ul>
<li><span title="Indexed">{{ row.indexed }}</span>{%
if row.created %} <span title="Created">({{ row.created }})</span>{% endif %}</li>{%
if row.size %}<li><span title="Size">{{ row.size }}</span></li>{% endif %}
</ul>
<a rel="nofollow" href="{{ row.magnet }}" title="Magnet">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
<path d="M15 12h-4v3h4v-3ZM5 12H1v3h4v-3ZM0 8a8 8 0 1 1 16 0v8h-6V8a2 2 0 1 0-4 0v8H0V8Z"/>
</svg>
</a>
<a name="{{ row.torrent.info_hash }}"></a>
<h2>{{ row.torrent.name }}</h2>
{% if row.torrent.comment %}<p>{{ row.torrent.comment }}</p>{% endif %}
<div>
<ul>
<li><span title="Indexed">{{ row.indexed }}</span>{%
if row.created %} <span title="Created">({{ row.created }})</span>{% endif %}</li>{%
if row.size %}<li><span title="Size">{{ row.size }}</span></li>{% endif %}
</ul>
<a rel="nofollow" href="{{ row.magnet }}" title="Magnet">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 16 16">
<path d="M15 12h-4v3h4v-3ZM5 12H1v3h4v-3ZM0 8a8 8 0 1 1 16 0v8h-6V8a2 2 0 1 0-4 0v8H0V8Z"/>
</svg>
</a>
</div>
</div>
{% endfor %}
{% else %}
<div>
Nothing.
</div>
{% endfor %}
{% endif %}
{% if next %}<a href="{{ next }}">Next</a>{% endif %}
{% if back %}<a href="{{ back }}">Back</a>{% endif %}
{% endblock content %}

View file

@ -86,6 +86,16 @@
margin: 0 auto;
}
/* pagination */
main > a {
background: #34384f;
border-radius: 3px;
float: right;
margin-left: 8px;
opacity: .96;
padding: 8px;
}
/* item row */
main > div {
background-color: #34384f;