mirror of
https://github.com/YGGverse/btracker.git
synced 2026-03-31 09:05:30 +00:00
init info page features
This commit is contained in:
parent
c5a0684466
commit
2fc9535710
7 changed files with 121 additions and 47 deletions
30
src/format.rs
Normal file
30
src/format.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
use crate::{Meta, Scrape, Scraper, Torrent};
|
||||||
|
use rocket::{State, serde::Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Format {
|
||||||
|
pub created: Option<String>,
|
||||||
|
pub files: String,
|
||||||
|
pub indexed: String,
|
||||||
|
pub magnet: String,
|
||||||
|
pub scrape: Option<Scrape>,
|
||||||
|
pub size: String,
|
||||||
|
pub torrent: Torrent,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Format {
|
||||||
|
pub fn from_torrent(torrent: Torrent, scraper: &State<Scraper>, meta: &State<Meta>) -> Self {
|
||||||
|
Self {
|
||||||
|
created: torrent
|
||||||
|
.creation_date
|
||||||
|
.map(|t| t.format(&meta.format_time).to_string()),
|
||||||
|
indexed: torrent.time.format(&meta.format_time).to_string(),
|
||||||
|
magnet: torrent.magnet(meta.trackers.as_ref()),
|
||||||
|
scrape: scraper.scrape(&torrent.info_hash),
|
||||||
|
size: torrent.size(),
|
||||||
|
files: torrent.files(),
|
||||||
|
torrent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/main.rs
75
src/main.rs
|
|
@ -3,36 +3,27 @@ extern crate rocket;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod feed;
|
mod feed;
|
||||||
|
mod format;
|
||||||
|
mod meta;
|
||||||
mod scraper;
|
mod scraper;
|
||||||
mod storage;
|
mod storage;
|
||||||
mod torrent;
|
mod torrent;
|
||||||
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use feed::Feed;
|
use feed::Feed;
|
||||||
|
use format::Format;
|
||||||
|
use meta::Meta;
|
||||||
use plurify::Plurify;
|
use plurify::Plurify;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
State,
|
State,
|
||||||
http::Status,
|
http::Status,
|
||||||
response::{content::RawXml, status::Custom},
|
response::{content::RawXml, status::Custom},
|
||||||
serde::Serialize,
|
|
||||||
};
|
};
|
||||||
use rocket_dyn_templates::{Template, context};
|
use rocket_dyn_templates::{Template, context};
|
||||||
use scraper::{Scrape, Scraper};
|
use scraper::{Scrape, Scraper};
|
||||||
|
use std::str::FromStr;
|
||||||
use storage::{Order, Sort, Storage};
|
use storage::{Order, Sort, Storage};
|
||||||
use torrent::Torrent;
|
use torrent::Torrent;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
pub struct Meta {
|
|
||||||
pub canonical: Option<Url>,
|
|
||||||
pub description: Option<String>,
|
|
||||||
pub format_time: String,
|
|
||||||
pub title: String,
|
|
||||||
/// * use vector to keep the order from the arguments list
|
|
||||||
pub trackers: Option<Vec<Url>>,
|
|
||||||
pub version: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/?<page>")]
|
#[get("/?<page>")]
|
||||||
fn index(
|
fn index(
|
||||||
|
|
@ -41,18 +32,6 @@ fn index(
|
||||||
storage: &State<Storage>,
|
storage: &State<Storage>,
|
||||||
meta: &State<Meta>,
|
meta: &State<Meta>,
|
||||||
) -> Result<Template, Custom<String>> {
|
) -> Result<Template, Custom<String>> {
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
struct Row {
|
|
||||||
created: Option<String>,
|
|
||||||
files: String,
|
|
||||||
indexed: String,
|
|
||||||
magnet: String,
|
|
||||||
scrape: Option<Scrape>,
|
|
||||||
size: String,
|
|
||||||
torrent: Torrent,
|
|
||||||
}
|
|
||||||
|
|
||||||
let (total, torrents) = storage
|
let (total, torrents) = storage
|
||||||
.torrents(
|
.torrents(
|
||||||
Some((Sort::Modified, Order::Desc)),
|
Some((Sort::Modified, Order::Desc)),
|
||||||
|
|
@ -74,23 +53,13 @@ fn index(
|
||||||
rows: torrents
|
rows: torrents
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|t| match Torrent::from_storage(&t.bytes, t.time) {
|
.filter_map(|t| match Torrent::from_storage(&t.bytes, t.time) {
|
||||||
Ok(torrent) => Some(Row {
|
Ok(torrent) => Some(Format::from_torrent(torrent, scraper, meta)),
|
||||||
created: torrent
|
|
||||||
.creation_date
|
|
||||||
.map(|t| t.format(&meta.format_time).to_string()),
|
|
||||||
indexed: torrent.time.format(&meta.format_time).to_string(),
|
|
||||||
magnet: torrent.magnet(meta.trackers.as_ref()),
|
|
||||||
scrape: scraper.scrape(&torrent.info_hash),
|
|
||||||
size: torrent.size(),
|
|
||||||
files: torrent.files(),
|
|
||||||
torrent,
|
|
||||||
}),
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Torrent storage read error: `{e}`");
|
error!("Torrent storage read error: `{e}`");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<Row>>(),
|
.collect::<Vec<Format>>(),
|
||||||
pagination_totals: format!(
|
pagination_totals: format!(
|
||||||
"Page {} / {} ({total} {} total)",
|
"Page {} / {} ({total} {} total)",
|
||||||
page.unwrap_or(1),
|
page.unwrap_or(1),
|
||||||
|
|
@ -101,6 +70,34 @@ fn index(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/<info_hash>")]
|
||||||
|
fn info(
|
||||||
|
info_hash: &str,
|
||||||
|
storage: &State<Storage>,
|
||||||
|
scraper: &State<Scraper>,
|
||||||
|
meta: &State<Meta>,
|
||||||
|
) -> Result<Template, Custom<String>> {
|
||||||
|
match storage.torrent(librqbit_core::Id20::from_str(info_hash).map_err(|e| {
|
||||||
|
warn!("Torrent info-hash parse error: `{e}`");
|
||||||
|
Custom(Status::BadRequest, Status::BadRequest.to_string())
|
||||||
|
})?) {
|
||||||
|
Some(t) => Ok(Template::render(
|
||||||
|
"info",
|
||||||
|
context! {
|
||||||
|
meta: meta.inner(),
|
||||||
|
torrent: Format::from_torrent(
|
||||||
|
Torrent::from_storage(&t.bytes, t.time).map_err(|e| {
|
||||||
|
error!("Torrent parse error: `{e}`");
|
||||||
|
Custom(Status::InternalServerError, E.to_string())
|
||||||
|
})?, scraper, meta
|
||||||
|
),
|
||||||
|
info_hash
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
None => Err(Custom(Status::NotFound, E.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/rss")]
|
#[get("/rss")]
|
||||||
fn rss(feed: &State<Feed>, storage: &State<Storage>) -> Result<RawXml<String>, Custom<String>> {
|
fn rss(feed: &State<Feed>, storage: &State<Storage>) -> Result<RawXml<String>, Custom<String>> {
|
||||||
let mut b = feed.transaction(1024); // @TODO
|
let mut b = feed.transaction(1024); // @TODO
|
||||||
|
|
@ -189,7 +186,7 @@ fn rocket() -> _ {
|
||||||
version: env!("CARGO_PKG_VERSION").into(),
|
version: env!("CARGO_PKG_VERSION").into(),
|
||||||
})
|
})
|
||||||
.mount("/", rocket::fs::FileServer::from(config.statics))
|
.mount("/", rocket::fs::FileServer::from(config.statics))
|
||||||
.mount("/", routes![index, rss])
|
.mount("/", routes![index, info, rss])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Public placeholder text for the `Status::InternalServerError`
|
/// Public placeholder text for the `Status::InternalServerError`
|
||||||
|
|
|
||||||
14
src/meta.rs
Normal file
14
src/meta.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
use rocket::serde::Serialize;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Meta {
|
||||||
|
pub canonical: Option<Url>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub format_time: String,
|
||||||
|
pub title: String,
|
||||||
|
/// * use vector to keep the order from the arguments list
|
||||||
|
pub trackers: Option<Vec<Url>>,
|
||||||
|
pub version: String,
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,8 @@ use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const EXTENSION: &str = "torrent";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub enum Sort {
|
pub enum Sort {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
@ -49,6 +51,15 @@ impl Storage {
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
|
pub fn torrent(&self, info_hash: librqbit_core::Id20) -> Option<Torrent> {
|
||||||
|
let mut p = PathBuf::from(&self.root);
|
||||||
|
p.push(format!("{}.{EXTENSION}", info_hash.as_string()));
|
||||||
|
Some(Torrent {
|
||||||
|
bytes: fs::read(&p).ok()?,
|
||||||
|
time: p.metadata().ok()?.modified().ok()?.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn torrents(
|
pub fn torrents(
|
||||||
&self,
|
&self,
|
||||||
sort_order: Option<(Sort, Order)>,
|
sort_order: Option<(Sort, Order)>,
|
||||||
|
|
@ -66,7 +77,7 @@ impl Storage {
|
||||||
.filter(|f| {
|
.filter(|f| {
|
||||||
f.path()
|
f.path()
|
||||||
.extension()
|
.extension()
|
||||||
.is_some_and(|e| !e.is_empty() && e.to_string_lossy() == "torrent")
|
.is_some_and(|e| !e.is_empty() && e.to_string_lossy() == EXTENSION)
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
b.push(Torrent {
|
b.push(Torrent {
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,7 @@ h1, h2, h3, h4, h5 {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1, h2 {
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: var(--default);
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
{% for row in rows %}
|
{% for row in rows %}
|
||||||
<div>
|
<div>
|
||||||
<a name="{{ row.torrent.info_hash }}"></a>
|
<a name="{{ row.torrent.info_hash }}"></a>
|
||||||
<h2>{{ row.torrent.name }}</h2>
|
<h2><a href="/{{ row.torrent.info_hash }}">{{ row.torrent.name }}</a></h2>
|
||||||
{% if row.torrent.comment %}<p>{{ row.torrent.comment }}</p>{% endif %}
|
{% if row.torrent.comment %}<p>{{ row.torrent.comment }}</p>{% endif %}
|
||||||
<div>
|
<div>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
||||||
27
templates/info.html.tera
Normal file
27
templates/info.html.tera
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{% extends "layout/default" %}
|
||||||
|
{% block content %}
|
||||||
|
{% if torrent %}
|
||||||
|
<div>
|
||||||
|
<h1>{% if torrent.name %}{{ torrent.name }}{% else %}{{ info_hash }}{% endif %}</h1>
|
||||||
|
{% if torrent.comment %}<p>{{ torrent.comment }}</p>{% endif %}
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<li><span title="Indexed">{{ torrent.indexed }}</span></li>
|
||||||
|
{% if torrent.created %}<li><span title="Created">({{ torrent.created }})</span></li>{% endif %}
|
||||||
|
{% if torrent.size %}<li><span title="Size">{{ torrent.size }}</span></li>{% endif %}
|
||||||
|
<li><span title="Files">{{ torrent.files }}</span></li>
|
||||||
|
{% if torrent.scrape %}
|
||||||
|
<li><span title="Seeders" class="seeders">{{ torrent.scrape.seeders }}</span></li>
|
||||||
|
<li><span title="Peers" class="peers">{{ torrent.scrape.peers }}</span></li>
|
||||||
|
<li><span title="Leechers" class="leechers">{{ torrent.scrape.leechers }}</span></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
<div>
|
||||||
|
<a rel="nofollow" href="{{ torrent.magnet }}" title="Get magnet" class="action magnet"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div>Nothing.</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock content %}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue