init torrent info page

This commit is contained in:
yggverse 2025-09-08 16:14:55 +03:00
parent 90d8e48b55
commit 43052a0d38
2 changed files with 150 additions and 66 deletions

View file

@ -1,4 +1,14 @@
pub fn size(value: u64) -> String {
use librqbit_core::torrent_metainfo::TorrentMetaV1Owned;
use plurify::Plurify;
use url::Url;
pub fn files(meta: &TorrentMetaV1Owned) -> String {
let total = meta.info.files.as_ref().map(|f| f.len()).unwrap_or(1);
format!("{total} {}", total.plurify(&["file", "files", "files"]))
}
pub fn size(meta: &TorrentMetaV1Owned) -> String {
fn s(value: u64) -> String {
const KB: f32 = 1024.0;
const MB: f32 = KB * KB;
const GB: f32 = MB * KB;
@ -14,4 +24,27 @@ pub fn size(value: u64) -> String {
} else {
format!("{:.2} GB", f / GB)
}
}
s(meta
.info
.files
.as_ref()
.map(|files| files.iter().map(|file| file.length).sum::<u64>())
.unwrap_or_default()
+ meta.info.length.unwrap_or_default())
}
pub fn magnet(meta: &TorrentMetaV1Owned, trackers: Option<&Vec<Url>>) -> String {
let mut b = format!("magnet:?xt=urn:btih:{}", meta.info_hash.as_string());
if let Some(ref n) = meta.info.name {
b.push_str("&dn=");
b.push_str(&urlencoding::encode(&n.to_string()))
}
if let Some(t) = trackers {
for tracker in t {
b.push_str("&tr=");
b.push_str(&urlencoding::encode(tracker.as_str()))
}
}
b
}

View file

@ -2,23 +2,25 @@ mod config;
mod format;
use anyhow::Result;
use btracker_fs::public::{Order, Public, Sort};
use btracker_fs::public::{Order, Public, Sort, Torrent};
use chrono::Local;
use clap::Parser;
use config::Config;
use librqbit_core::torrent_metainfo::{TorrentMetaV1Owned, torrent_from_bytes};
use librqbit_core::{
Id20,
torrent_metainfo::{TorrentMetaV1Owned, torrent_from_bytes},
};
use log::*;
use native_tls::{HandshakeError, Identity, TlsAcceptor, TlsStream};
use plurify::Plurify;
use std::{
fs::File,
io::{Read, Write},
net::{SocketAddr, TcpListener, TcpStream},
str::FromStr,
sync::Arc,
thread,
};
use titanite::*;
use url::Url;
fn main() -> Result<()> {
if std::env::var("RUST_LOG").is_ok() {
@ -153,10 +155,10 @@ fn response(
stream: &mut TlsStream<TcpStream>,
) {
debug!("Incoming request from `{peer}` to `{}`", request.url.path());
let p = request.url.path().trim_matches('/');
// try index page
if request.url.path().trim_end_matches("/").is_empty() {
return send(
if p.is_empty() {
send(
&match index(
config,
public,
@ -184,25 +186,74 @@ fn response(
}
},
stream,
|result| match result {
Ok(()) => debug!("Home page request from peer `{peer}`"),
Err(e) => error!("Internal server error on handle peer `{peer}` request: `{e}`"),
|result| {
if let Err(e) = result {
error!("Internal server error on handle peer `{peer}` request: `{e}`")
}
},
);
)
}
// try info page
todo!()
else if let Ok(id) = Id20::from_str(p)
&& let Some(torrent) = public.torrent(id)
{
send(
&match info(config, torrent) {
Ok(data) => response::success::Default {
data: data.as_bytes(),
meta: response::success::default::Meta {
mime: "text/gemini".to_string(),
},
}
.into_bytes(),
Err(e) => {
error!("Internal server error on handle peer `{peer}` request: `{e}`");
response::failure::temporary::General {
message: Some("Internal server error".to_string()),
}
.into_bytes()
}
},
stream,
|result| {
if let Err(e) = result {
error!("Internal server error on handle peer `{peer}` request: `{e}`")
}
},
)
}
// not found
else {
warn!(
"Requested resource `{}` not found by peer `{peer}`",
request.url.as_str()
);
send(
&response::Failure::Permanent(response::failure::Permanent::NotFound(
response::failure::permanent::NotFound { message: None },
))
.into_bytes(),
stream,
|result| {
if let Err(e) = result {
error!(
"Internal server error on handle peer `{peer}` request `{}`: `{e}`",
request.url.as_str()
)
}
},
)
}
}
fn close(stream: &mut TlsStream<TcpStream>) -> Result<()> {
fn send(data: &[u8], stream: &mut TlsStream<TcpStream>, callback: impl FnOnce(Result<()>)) {
fn close(stream: &mut TlsStream<TcpStream>) -> Result<()> {
stream.flush()?;
// close connection gracefully
// https://geminiprotocol.net/docs/protocol-specification.gmi#closing-connections
stream.shutdown()?;
Ok(())
}
fn send(data: &[u8], stream: &mut TlsStream<TcpStream>, callback: impl FnOnce(Result<()>)) {
}
callback((|| {
stream.write_all(data)?;
close(stream)?;
@ -210,7 +261,11 @@ fn send(data: &[u8], stream: &mut TlsStream<TcpStream>, callback: impl FnOnce(Re
})());
}
// rotes
fn index(config: &Config, public: &Public, page: Option<usize>) -> Result<String> {
use plurify::Plurify;
let (total, torrents) = public.torrents(
None, // @TODO
Some((Sort::Modified, Order::Desc)),
@ -246,13 +301,9 @@ fn index(config: &Config, public: &Public, page: Option<usize>) -> Result<String
b.push(format!(
"{} • {} • {}\n",
torrent.time.format(&config.date),
size(&i),
files(&i),
format::size(&i),
format::files(&i),
))
/*b.push(format!(
"=> {} Magnet\n",
magnet(&i, config.tracker.as_ref())
))*/
}
b.push("## Navigation\n".into());
@ -283,33 +334,33 @@ fn index(config: &Config, public: &Public, page: Option<usize>) -> Result<String
Ok(b.join("\n"))
}
fn files(meta: &TorrentMetaV1Owned) -> String {
let total = meta.info.files.as_ref().map(|f| f.len()).unwrap_or(1);
format!("{total} {}", total.plurify(&["file", "files", "files"]))
}
fn info(config: &Config, torrent: Torrent) -> Result<String> {
let i: TorrentMetaV1Owned = torrent_from_bytes(&torrent.bytes)?;
fn size(meta: &TorrentMetaV1Owned) -> String {
format::size(
meta.info
.files
let mut b = Vec::new();
b.push(format!("# {}\n", config.name));
b.push(format!(
"## {}\n",
i.info
.name
.as_ref()
.map(|files| files.iter().map(|file| file.length).sum::<u64>())
.map(|n| n.to_string())
.unwrap_or_default()
+ meta.info.length.unwrap_or_default(),
)
}
));
fn magnet(meta: &TorrentMetaV1Owned, trackers: Option<&Vec<Url>>) -> String {
let mut b = format!("magnet:?xt=urn:btih:{}", meta.info_hash.as_string());
if let Some(ref n) = meta.info.name {
b.push_str("&dn=");
b.push_str(&urlencoding::encode(&n.to_string()))
}
if let Some(t) = trackers {
for tracker in t {
b.push_str("&tr=");
b.push_str(&urlencoding::encode(tracker.as_str()))
}
}
b
b.push(format!(
"{} • {} • {}\n",
torrent.time.format(&config.date),
format::size(&i),
format::files(&i),
));
b.push(format!(
"=> {} Magnet\n",
format::magnet(&i, config.tracker.as_ref())
));
Ok(b.join("\n"))
}