mirror of
https://github.com/YGGverse/btracker.git
synced 2026-03-31 09:05:30 +00:00
implement basic search
This commit is contained in:
parent
a5133b006b
commit
97ac3d9dce
4 changed files with 82 additions and 11 deletions
|
|
@ -67,7 +67,38 @@ table > thead > tr > th {
|
|||
}
|
||||
|
||||
table > tbody > tr:hover > td {
|
||||
background: var(--background)
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
form {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
form input {
|
||||
background: var(--item);
|
||||
border-color: var(--item);
|
||||
border-radius: 3px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
color: var(--default);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
form input:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
form input[type="text"] {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
form input[type="text"]:focus {
|
||||
border-color: var(--separator);
|
||||
}
|
||||
|
||||
form input[type="submit"] {
|
||||
cursor: pointer;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
body > * {
|
||||
|
|
|
|||
16
src/main.rs
16
src/main.rs
|
|
@ -19,8 +19,9 @@ use scraper::{Scrape, Scraper};
|
|||
use std::str::FromStr;
|
||||
use torrent::Torrent;
|
||||
|
||||
#[get("/?<page>")]
|
||||
#[get("/?<search>&<page>")]
|
||||
fn index(
|
||||
search: Option<&str>,
|
||||
page: Option<usize>,
|
||||
scraper: &State<Scraper>,
|
||||
public: &State<Public>,
|
||||
|
|
@ -39,6 +40,7 @@ fn index(
|
|||
}
|
||||
let (total, torrents) = public
|
||||
.torrents(
|
||||
search,
|
||||
Some((Sort::Modified, Order::Desc)),
|
||||
page.map(|p| if p > 0 { p - 1 } else { p } * public.default_limit),
|
||||
Some(public.default_limit),
|
||||
|
|
@ -52,6 +54,10 @@ fn index(
|
|||
context! {
|
||||
title: {
|
||||
let mut t = String::new();
|
||||
if let Some(q) = search && !q.is_empty() {
|
||||
t.push_str(q);
|
||||
t.push_str(S)
|
||||
}
|
||||
if let Some(p) = page && p > 1 {
|
||||
t.push_str(&format!("Page {p}"));
|
||||
t.push_str(S)
|
||||
|
|
@ -64,9 +70,9 @@ fn index(
|
|||
t
|
||||
},
|
||||
meta: meta.inner(),
|
||||
back: page.map(|p| uri!(index(if p > 2 { Some(p - 1) } else { None }))),
|
||||
back: page.map(|p| uri!(index(search, if p > 2 { Some(p - 1) } else { None }))),
|
||||
next: if page.unwrap_or(1) * public.default_limit >= total { None }
|
||||
else { Some(uri!(index(Some(page.map_or(2, |p| p + 1))))) },
|
||||
else { Some(uri!(index(search, Some(page.map_or(2, |p| p + 1))))) },
|
||||
rows: torrents
|
||||
.into_iter()
|
||||
.filter_map(|t| match Torrent::from_public(&t.bytes, t.time) {
|
||||
|
|
@ -90,7 +96,8 @@ fn index(
|
|||
page.unwrap_or(1),
|
||||
(total as f64 / public.default_limit as f64).ceil(),
|
||||
total.plurify(&["torrent", "torrents", "torrents"])
|
||||
)
|
||||
),
|
||||
search
|
||||
},
|
||||
))
|
||||
}
|
||||
|
|
@ -164,6 +171,7 @@ fn rss(meta: &State<Meta>, public: &State<Public>) -> Result<RawXml<String>, Sta
|
|||
);
|
||||
for t in public
|
||||
.torrents(
|
||||
None,
|
||||
Some((Sort::Modified, Order::Desc)),
|
||||
None,
|
||||
Some(public.default_limit),
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ pub struct Torrent {
|
|||
}
|
||||
|
||||
pub struct Public {
|
||||
pub default_limit: usize,
|
||||
default_capacity: usize,
|
||||
pub default_limit: usize,
|
||||
root: PathBuf,
|
||||
}
|
||||
|
||||
|
|
@ -43,8 +43,8 @@ impl Public {
|
|||
return Err("Public root is not directory".into());
|
||||
}
|
||||
Ok(Self {
|
||||
default_limit,
|
||||
default_capacity,
|
||||
default_limit,
|
||||
root: root.canonicalize().map_err(|e| e.to_string())?,
|
||||
})
|
||||
}
|
||||
|
|
@ -62,11 +62,12 @@ impl Public {
|
|||
|
||||
pub fn torrents(
|
||||
&self,
|
||||
keyword: Option<&str>,
|
||||
sort_order: Option<(Sort, Order)>,
|
||||
start: Option<usize>,
|
||||
limit: Option<usize>,
|
||||
) -> Result<(usize, Vec<Torrent>), Error> {
|
||||
let f = self.files(sort_order)?;
|
||||
let f = self.files(keyword, sort_order)?;
|
||||
let t = f.len();
|
||||
let l = limit.unwrap_or(t);
|
||||
let mut b = Vec::with_capacity(l);
|
||||
|
|
@ -96,13 +97,40 @@ impl Public {
|
|||
|
||||
// Helpers
|
||||
|
||||
fn files(&self, sort_order: Option<(Sort, Order)>) -> Result<Vec<DirEntry>, Error> {
|
||||
fn files(
|
||||
&self,
|
||||
keyword: Option<&str>,
|
||||
sort_order: Option<(Sort, Order)>,
|
||||
) -> Result<Vec<DirEntry>, Error> {
|
||||
let mut b = Vec::with_capacity(self.default_capacity);
|
||||
for entry in fs::read_dir(&self.root)? {
|
||||
let e = entry?;
|
||||
if e.file_type()?.is_file() && e.path().extension().is_some_and(|e| e == EXTENSION) {
|
||||
b.push((e.metadata()?.modified()?, e))
|
||||
let p = e.path();
|
||||
if !p.is_file() || p.extension().is_none_or(|e| e != EXTENSION) {
|
||||
continue;
|
||||
}
|
||||
if keyword.is_some_and(|k| {
|
||||
!k.is_empty()
|
||||
&& !librqbit_core::torrent_metainfo::torrent_from_bytes(
|
||||
&fs::read(e.path()).unwrap(),
|
||||
)
|
||||
.is_ok_and(
|
||||
|m: librqbit_core::torrent_metainfo::TorrentMetaV1Owned| {
|
||||
m.info_hash.as_string().contains(k)
|
||||
|| m.info.name.is_some_and(|n| n.to_string().contains(k))
|
||||
|| m.info.files.is_some_and(|f| {
|
||||
f.iter().any(|f| {
|
||||
let mut p = PathBuf::new();
|
||||
f.full_path(&mut p)
|
||||
.is_ok_and(|_| p.to_string_lossy().contains(k))
|
||||
})
|
||||
})
|
||||
},
|
||||
) // @TODO implement fast in-memory search index
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
b.push((e.metadata()?.modified()?, e))
|
||||
}
|
||||
if let Some((sort, order)) = sort_order {
|
||||
match sort {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@
|
|||
{% if meta.trackers %}
|
||||
<div>{% for tracker in meta.trackers %}<code>{{ tracker }}</code>{% endfor %}</div>
|
||||
{% endif %}
|
||||
<form action="/" method="GET">
|
||||
<input type="text" name="search" value="{% if search %}{{ search }}{% endif %}" placeholder="Keyword, file, hash..." />
|
||||
<input type="submit" value="Search" />
|
||||
</form>
|
||||
</header>
|
||||
<main>
|
||||
{% block content %}{% endblock content %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue