mirror of
https://github.com/YGGverse/btracker.git
synced 2026-03-31 17:15:31 +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 {
|
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 > * {
|
body > * {
|
||||||
|
|
|
||||||
16
src/main.rs
16
src/main.rs
|
|
@ -19,8 +19,9 @@ use scraper::{Scrape, Scraper};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use torrent::Torrent;
|
use torrent::Torrent;
|
||||||
|
|
||||||
#[get("/?<page>")]
|
#[get("/?<search>&<page>")]
|
||||||
fn index(
|
fn index(
|
||||||
|
search: Option<&str>,
|
||||||
page: Option<usize>,
|
page: Option<usize>,
|
||||||
scraper: &State<Scraper>,
|
scraper: &State<Scraper>,
|
||||||
public: &State<Public>,
|
public: &State<Public>,
|
||||||
|
|
@ -39,6 +40,7 @@ fn index(
|
||||||
}
|
}
|
||||||
let (total, torrents) = public
|
let (total, torrents) = public
|
||||||
.torrents(
|
.torrents(
|
||||||
|
search,
|
||||||
Some((Sort::Modified, Order::Desc)),
|
Some((Sort::Modified, Order::Desc)),
|
||||||
page.map(|p| if p > 0 { p - 1 } else { p } * public.default_limit),
|
page.map(|p| if p > 0 { p - 1 } else { p } * public.default_limit),
|
||||||
Some(public.default_limit),
|
Some(public.default_limit),
|
||||||
|
|
@ -52,6 +54,10 @@ fn index(
|
||||||
context! {
|
context! {
|
||||||
title: {
|
title: {
|
||||||
let mut t = String::new();
|
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 {
|
if let Some(p) = page && p > 1 {
|
||||||
t.push_str(&format!("Page {p}"));
|
t.push_str(&format!("Page {p}"));
|
||||||
t.push_str(S)
|
t.push_str(S)
|
||||||
|
|
@ -64,9 +70,9 @@ fn index(
|
||||||
t
|
t
|
||||||
},
|
},
|
||||||
meta: meta.inner(),
|
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 }
|
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
|
rows: torrents
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|t| match Torrent::from_public(&t.bytes, t.time) {
|
.filter_map(|t| match Torrent::from_public(&t.bytes, t.time) {
|
||||||
|
|
@ -90,7 +96,8 @@ fn index(
|
||||||
page.unwrap_or(1),
|
page.unwrap_or(1),
|
||||||
(total as f64 / public.default_limit as f64).ceil(),
|
(total as f64 / public.default_limit as f64).ceil(),
|
||||||
total.plurify(&["torrent", "torrents", "torrents"])
|
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
|
for t in public
|
||||||
.torrents(
|
.torrents(
|
||||||
|
None,
|
||||||
Some((Sort::Modified, Order::Desc)),
|
Some((Sort::Modified, Order::Desc)),
|
||||||
None,
|
None,
|
||||||
Some(public.default_limit),
|
Some(public.default_limit),
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ pub struct Torrent {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Public {
|
pub struct Public {
|
||||||
pub default_limit: usize,
|
|
||||||
default_capacity: usize,
|
default_capacity: usize,
|
||||||
|
pub default_limit: usize,
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,8 +43,8 @@ impl Public {
|
||||||
return Err("Public root is not directory".into());
|
return Err("Public root is not directory".into());
|
||||||
}
|
}
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
default_limit,
|
|
||||||
default_capacity,
|
default_capacity,
|
||||||
|
default_limit,
|
||||||
root: root.canonicalize().map_err(|e| e.to_string())?,
|
root: root.canonicalize().map_err(|e| e.to_string())?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -62,11 +62,12 @@ impl Public {
|
||||||
|
|
||||||
pub fn torrents(
|
pub fn torrents(
|
||||||
&self,
|
&self,
|
||||||
|
keyword: Option<&str>,
|
||||||
sort_order: Option<(Sort, Order)>,
|
sort_order: Option<(Sort, Order)>,
|
||||||
start: Option<usize>,
|
start: Option<usize>,
|
||||||
limit: Option<usize>,
|
limit: Option<usize>,
|
||||||
) -> Result<(usize, Vec<Torrent>), Error> {
|
) -> Result<(usize, Vec<Torrent>), Error> {
|
||||||
let f = self.files(sort_order)?;
|
let f = self.files(keyword, sort_order)?;
|
||||||
let t = f.len();
|
let t = f.len();
|
||||||
let l = limit.unwrap_or(t);
|
let l = limit.unwrap_or(t);
|
||||||
let mut b = Vec::with_capacity(l);
|
let mut b = Vec::with_capacity(l);
|
||||||
|
|
@ -96,13 +97,40 @@ impl Public {
|
||||||
|
|
||||||
// Helpers
|
// 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);
|
let mut b = Vec::with_capacity(self.default_capacity);
|
||||||
for entry in fs::read_dir(&self.root)? {
|
for entry in fs::read_dir(&self.root)? {
|
||||||
let e = entry?;
|
let e = entry?;
|
||||||
if e.file_type()?.is_file() && e.path().extension().is_some_and(|e| e == EXTENSION) {
|
let p = e.path();
|
||||||
b.push((e.metadata()?.modified()?, e))
|
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 {
|
if let Some((sort, order)) = sort_order {
|
||||||
match sort {
|
match sort {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,10 @@
|
||||||
{% if meta.trackers %}
|
{% if meta.trackers %}
|
||||||
<div>{% for tracker in meta.trackers %}<code>{{ tracker }}</code>{% endfor %}</div>
|
<div>{% for tracker in meta.trackers %}<code>{{ tracker }}</code>{% endfor %}</div>
|
||||||
{% endif %}
|
{% 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>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
{% block content %}{% endblock content %}
|
{% block content %}{% endblock content %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue