75 lines
2.4 KiB
Rust
75 lines
2.4 KiB
Rust
//! Magnet-link resolver backed by librqbit.
|
|
//!
|
|
//! Turns a `magnet:` URI into the same bencoded torrent bytes a `.torrent`
|
|
//! download would yield, by resolving metadata over the BitTorrent DHT and
|
|
//! the BEP-9 `ut_metadata` peer extension. The bytes are then handed to
|
|
//! [`crate::torrent::TorrentParser`] so magnet and torrent sources are
|
|
//! parsed identically.
|
|
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
|
|
use anyhow::{Context, bail};
|
|
use librqbit::{
|
|
AddTorrent, AddTorrentOptions, AddTorrentResponse, ListOnlyResponse, Session, SessionOptions,
|
|
};
|
|
use tempfile::TempDir;
|
|
|
|
const RESOLVE_TIMEOUT: Duration = Duration::from_secs(90);
|
|
|
|
pub struct MagnetResolver {
|
|
session: Arc<Session>,
|
|
_scratch_dir: TempDir,
|
|
}
|
|
|
|
impl std::fmt::Debug for MagnetResolver {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("MagnetResolver").finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
impl MagnetResolver {
|
|
pub async fn new() -> anyhow::Result<Self> {
|
|
let scratch_dir = tempfile::TempDir::new()?;
|
|
let opts = SessionOptions {
|
|
disable_dht_persistence: true,
|
|
persistence: None,
|
|
enable_upnp_port_forwarding: false,
|
|
..Default::default()
|
|
};
|
|
let session = Session::new_with_opts(scratch_dir.path().to_path_buf(), opts).await?;
|
|
Ok(Self {
|
|
session,
|
|
_scratch_dir: scratch_dir,
|
|
})
|
|
}
|
|
|
|
pub async fn resolve(&self, magnet: &str) -> anyhow::Result<Vec<u8>> {
|
|
let added = tokio::time::timeout(
|
|
RESOLVE_TIMEOUT,
|
|
self.session.add_torrent(
|
|
AddTorrent::from_url(magnet),
|
|
Some(AddTorrentOptions {
|
|
list_only: true,
|
|
..Default::default()
|
|
}),
|
|
),
|
|
)
|
|
.await
|
|
.context("magnet resolution timed out (>90s, likely no peers)")?
|
|
.context("error adding magnet to session")?;
|
|
|
|
match added {
|
|
AddTorrentResponse::ListOnly(ListOnlyResponse { torrent_bytes, .. }) => {
|
|
Ok(torrent_bytes.to_vec())
|
|
}
|
|
AddTorrentResponse::AlreadyManaged(_, handle) => {
|
|
handle.with_metadata(|m| m.torrent_bytes.to_vec())
|
|
}
|
|
AddTorrentResponse::Added(_, _) => {
|
|
bail!("torrent unexpectedly added under list_only")
|
|
}
|
|
}
|
|
}
|
|
}
|