use snafu::prelude::*; use imdl::infohash::Infohash; use imdl::input::Input; use imdl::input_target::InputTarget; use imdl::magnet_link::MagnetLink; use imdl::metainfo::Metainfo; use imdl::torrent_summary::TorrentSummary; use std::path::Path; use std::net::TcpListener; use crate::error::{Error, InvalidMagnetSnafu as InvalidMagnet, InvalidTorrentSnafu as InvalidTorrent, FailedToReadFileSnafu as FailedToReadFile, OtherTorrentSnafu as OtherTorrentError}; /// Helper method for imdl functions which expect an imdl::Input type pub fn input_path>(path: T) -> Result { let path = path.as_ref(); let absolute = path.canonicalize().context(FailedToReadFile { path: path.to_path_buf() })?; let data = std::fs::read(absolute).context(FailedToReadFile { path: path.to_path_buf() })?; Ok(Input::new( InputTarget::Path(path.to_path_buf()), data )) } /// Extracts the infohash of a magnet link pub fn magnet_hash>(magnet: T) -> Result { let magnet = magnet.as_ref(); let magnet = MagnetLink::parse(magnet).context(InvalidMagnet { magnet: magnet.to_string() })?; let bytes = magnet.infohash.inner.bytes.clone(); let mut s = String::new(); for byte in bytes { s.push_str(&format!("{:02x}", byte)); } Ok(s) } /// Extracts the name of a magnet link pub fn magnet_name>(magnet: T) -> Result, Error> { let magnet = magnet.as_ref(); let magnet = MagnetLink::parse(magnet).context(InvalidMagnet { magnet })?; Ok(magnet.name) } /// Extracts the infohash of a torrent file pub fn torrent_hash>(torrent: T) -> Result { let torrent = torrent.as_ref(); let input = input_path(torrent)?; let summary = TorrentSummary::from_input(&input).context(InvalidTorrent { torrent })?; Ok(summary.torrent_summary_data().info_hash) } /// Extracts the name of a torrent file pub fn torrent_name>(torrent: T) -> Result { let torrent = torrent.as_ref(); let input = input_path(torrent)?; let summary = TorrentSummary::from_input(&input).context(InvalidTorrent { torrent })?; Ok(summary.torrent_summary_data().name) } /// Turns a torrent file into a magnet link pub fn torrent_to_magnet>(torrent: T) -> Result { let torrent = torrent.as_ref(); let input = input_path(torrent)?; let infohash = Infohash::from_input(&input).context(InvalidTorrent { torrent })?; let metainfo = Metainfo::from_input(&input).context(InvalidTorrent { torrent })?; let mut link = MagnetLink::with_infohash(infohash); link.set_name(&metainfo.info.name); for result in metainfo.trackers() { let result = result.context(OtherTorrentError { torrent })?; link.add_tracker(result); } Ok(link.to_url().to_string()) } // Fallible: only used in tests #[allow(dead_code)] pub fn find_free_port() -> u16 { let bind = TcpListener::bind("127.0.0.1:0").unwrap(); bind.local_addr().unwrap().port() }