2020-02-04 16:55:50 +01:00
|
|
|
use crate::common::*;
|
|
|
|
|
|
|
|
pub(crate) struct TorrentSummary {
|
|
|
|
metainfo: Metainfo,
|
|
|
|
infohash: sha1::Digest,
|
|
|
|
size: Bytes,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TorrentSummary {
|
2020-02-04 17:36:00 +01:00
|
|
|
fn new(bytes: &[u8], metainfo: Metainfo) -> Result<Self, Error> {
|
2020-02-11 12:08:57 +01:00
|
|
|
let value = Value::from_bencode(&bytes).unwrap();
|
2020-02-04 16:55:50 +01:00
|
|
|
|
2020-02-11 12:08:57 +01:00
|
|
|
let infohash = if let Value::Dict(items) = value {
|
2020-02-04 16:55:50 +01:00
|
|
|
let info = items
|
|
|
|
.iter()
|
2020-02-14 09:12:49 +01:00
|
|
|
.find(|pair: &(&Cow<[u8]>, &Value)| pair.0.as_ref() == b"info")
|
2020-02-04 16:55:50 +01:00
|
|
|
.unwrap()
|
|
|
|
.1
|
2020-02-11 12:08:57 +01:00
|
|
|
.to_bencode()
|
|
|
|
.unwrap();
|
2020-02-04 16:55:50 +01:00
|
|
|
Sha1::from(info).digest()
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Self {
|
2020-02-04 17:36:00 +01:00
|
|
|
size: Bytes::from(bytes.len().into_u64()),
|
2020-02-04 16:55:50 +01:00
|
|
|
infohash,
|
|
|
|
metainfo,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-02-04 17:36:00 +01:00
|
|
|
pub(crate) fn from_metainfo(metainfo: Metainfo) -> Result<Self, Error> {
|
|
|
|
let bytes = metainfo.serialize()?;
|
|
|
|
Self::new(&bytes, metainfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn load(path: &Path) -> Result<Self, Error> {
|
|
|
|
let bytes = fs::read(path).context(error::Filesystem { path })?;
|
|
|
|
|
|
|
|
let metainfo = Metainfo::deserialize(path, &bytes)?;
|
|
|
|
|
|
|
|
Self::new(&bytes, metainfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn write(&self, env: &mut Env) -> Result<(), Error> {
|
|
|
|
let table = self.table();
|
|
|
|
|
2020-02-04 19:54:41 +01:00
|
|
|
if env.out_is_term() {
|
|
|
|
let out_style = env.out_style();
|
|
|
|
table
|
|
|
|
.write_human_readable(&mut env.out, out_style)
|
|
|
|
.context(error::Stdout)?;
|
|
|
|
} else {
|
|
|
|
table
|
|
|
|
.write_tab_delimited(&mut env.out)
|
|
|
|
.context(error::Stdout)?;
|
|
|
|
}
|
2020-02-04 17:36:00 +01:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn table(&self) -> Table {
|
2020-02-04 16:55:50 +01:00
|
|
|
let mut table = Table::new();
|
|
|
|
|
|
|
|
table.row("Name", &self.metainfo.info.name);
|
|
|
|
|
|
|
|
if let Some(comment) = &self.metainfo.comment {
|
|
|
|
table.row("Comment", comment);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(creation_date) = self.metainfo.creation_date {
|
|
|
|
#[allow(clippy::as_conversions)]
|
|
|
|
table.row(
|
2020-03-12 06:17:55 +01:00
|
|
|
"Creation Date",
|
2020-02-04 16:55:50 +01:00
|
|
|
Utc.timestamp(
|
|
|
|
creation_date
|
|
|
|
.min(i64::max_value() as u64)
|
|
|
|
.try_into()
|
|
|
|
.unwrap(),
|
|
|
|
0,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-02-06 08:57:35 +01:00
|
|
|
if let Some(created_by) = &self.metainfo.created_by {
|
|
|
|
table.row("Created By", created_by);
|
|
|
|
}
|
|
|
|
|
2020-02-04 17:51:56 +01:00
|
|
|
if let Some(source) = &self.metainfo.info.source {
|
|
|
|
table.row("Source", source);
|
|
|
|
}
|
|
|
|
|
2020-02-04 16:55:50 +01:00
|
|
|
table.row("Info Hash", self.infohash);
|
|
|
|
|
2020-02-04 19:54:41 +01:00
|
|
|
table.size("Torrent Size", self.size);
|
2020-02-04 16:55:50 +01:00
|
|
|
|
2020-02-04 19:54:41 +01:00
|
|
|
table.size("Content Size", self.metainfo.info.mode.total_size());
|
2020-02-04 16:55:50 +01:00
|
|
|
|
|
|
|
table.row(
|
|
|
|
"Private",
|
2020-02-14 09:12:49 +01:00
|
|
|
if self.metainfo.info.private.unwrap_or(false) {
|
2020-02-04 16:55:50 +01:00
|
|
|
"yes"
|
|
|
|
} else {
|
|
|
|
"no"
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
match &self.metainfo.announce_list {
|
|
|
|
Some(tiers) => {
|
2020-02-07 00:00:57 +01:00
|
|
|
if tiers.iter().all(|tier| tier.len() == 1) {
|
|
|
|
let mut list = Vec::new();
|
|
|
|
if !tiers
|
|
|
|
.iter()
|
|
|
|
.any(|tier| tier.contains(&self.metainfo.announce))
|
|
|
|
{
|
|
|
|
list.push(self.metainfo.announce.clone());
|
|
|
|
}
|
2020-02-04 19:54:41 +01:00
|
|
|
|
2020-02-07 00:00:57 +01:00
|
|
|
for tier in tiers {
|
|
|
|
list.push(tier[0].clone());
|
|
|
|
}
|
2020-02-04 16:55:50 +01:00
|
|
|
|
2020-02-07 00:00:57 +01:00
|
|
|
table.list("Trackers", list);
|
|
|
|
} else {
|
|
|
|
let mut value = Vec::new();
|
2020-02-04 16:55:50 +01:00
|
|
|
|
2020-02-07 00:00:57 +01:00
|
|
|
if !tiers
|
|
|
|
.iter()
|
|
|
|
.any(|tier| tier.contains(&self.metainfo.announce))
|
|
|
|
{
|
|
|
|
value.push(("Main".to_owned(), vec![self.metainfo.announce.clone()]));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i, tier) in tiers.iter().enumerate() {
|
|
|
|
value.push((format!("Tier {}", i + 1), tier.clone()));
|
|
|
|
}
|
|
|
|
|
|
|
|
table.tiers("Trackers", value);
|
|
|
|
}
|
2020-02-04 16:55:50 +01:00
|
|
|
}
|
|
|
|
None => table.row("Tracker", &self.metainfo.announce),
|
|
|
|
}
|
|
|
|
|
2020-02-14 11:16:19 +01:00
|
|
|
if let Some(nodes) = &self.metainfo.nodes {
|
|
|
|
table.list(
|
|
|
|
"DHT Nodes",
|
|
|
|
nodes
|
|
|
|
.iter()
|
|
|
|
.map(ToString::to_string)
|
|
|
|
.collect::<Vec<String>>(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-02-14 09:12:49 +01:00
|
|
|
table.size("Piece Size", self.metainfo.info.piece_length);
|
2020-02-04 16:55:50 +01:00
|
|
|
|
2020-02-16 03:08:36 +01:00
|
|
|
table.row("Piece Count", self.metainfo.info.pieces.count());
|
2020-02-04 16:55:50 +01:00
|
|
|
|
2020-02-06 08:57:35 +01:00
|
|
|
match &self.metainfo.info.mode {
|
2020-02-06 09:12:07 +01:00
|
|
|
Mode::Single { .. } => {
|
|
|
|
table.row("File Count", 1);
|
|
|
|
table.row("Files", &self.metainfo.info.name);
|
|
|
|
}
|
2020-02-06 08:57:35 +01:00
|
|
|
Mode::Multiple { files } => {
|
|
|
|
table.row("File Count", files.len());
|
|
|
|
table.directory(
|
|
|
|
"Files",
|
|
|
|
&self.metainfo.info.name,
|
|
|
|
files
|
|
|
|
.iter()
|
|
|
|
.map(|file_info| file_info.path.clone())
|
|
|
|
.collect(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
2020-02-04 16:55:50 +01:00
|
|
|
|
|
|
|
table
|
|
|
|
}
|
|
|
|
}
|