|
3 | 3 | use super::{IndexerError, TorrentIndexer, common}; |
4 | 4 | use crate::torznab::TorrentResult; |
5 | 5 | use async_trait::async_trait; |
| 6 | +use futures::future::join_all; |
6 | 7 | use reqwest::Client; |
7 | 8 | use scraper::{Html, Selector}; |
8 | 9 |
|
@@ -42,6 +43,25 @@ impl X1337Indexer { |
42 | 43 | Ok(response.text().await?) |
43 | 44 | } |
44 | 45 |
|
| 46 | + /// Fetch the magnet URL from a 1337x detail page |
| 47 | + async fn fetch_magnet_url(&self, detail_url: &str) -> Result<String, IndexerError> { |
| 48 | + let html = self.fetch_html(detail_url).await?; |
| 49 | + let document = Html::parse_document(&html); |
| 50 | + |
| 51 | + // The magnet link is in an anchor with href starting with "magnet:" |
| 52 | + let magnet_selector = Selector::parse("a[href^='magnet:']") |
| 53 | + .map_err(|e| IndexerError::ParseError(format!("Invalid selector: {}", e)))?; |
| 54 | + |
| 55 | + document |
| 56 | + .select(&magnet_selector) |
| 57 | + .next() |
| 58 | + .and_then(|elem| elem.value().attr("href")) |
| 59 | + .map(String::from) |
| 60 | + .ok_or_else(|| { |
| 61 | + IndexerError::ParseError("Magnet link not found on detail page".to_string()) |
| 62 | + }) |
| 63 | + } |
| 64 | + |
45 | 65 | fn parse_results(&self, html: &str) -> Result<Vec<TorrentResult>, IndexerError> { |
46 | 66 | let document = Html::parse_document(html); |
47 | 67 |
|
@@ -132,9 +152,33 @@ impl TorrentIndexer for X1337Indexer { |
132 | 152 | tracing::debug!(url, "1337x: searching"); |
133 | 153 |
|
134 | 154 | let html = self.fetch_html(&url).await?; |
135 | | - let results = self.parse_results(&html)?; |
| 155 | + let mut results = self.parse_results(&html)?; |
| 156 | + |
| 157 | + tracing::debug!( |
| 158 | + count = results.len(), |
| 159 | + "1337x: found results, fetching magnet URLs" |
| 160 | + ); |
| 161 | + |
| 162 | + // Fetch magnet URLs from detail pages in parallel |
| 163 | + let detail_urls: Vec<_> = results.iter().filter_map(|r| r.link.clone()).collect(); |
| 164 | + let magnet_futures: Vec<_> = detail_urls |
| 165 | + .iter() |
| 166 | + .map(|detail_url| self.fetch_magnet_url(detail_url)) |
| 167 | + .collect(); |
| 168 | + |
| 169 | + let magnets = join_all(magnet_futures).await; |
| 170 | + |
| 171 | + // Update results with fetched magnet URLs |
| 172 | + for (result, magnet_result) in results.iter_mut().zip(magnets.into_iter()) { |
| 173 | + if let Ok(magnet) = magnet_result { |
| 174 | + result.magnet_url = Some(magnet); |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + // Filter out results without magnet URLs |
| 179 | + results.retain(|r| r.magnet_url.is_some()); |
136 | 180 |
|
137 | | - tracing::debug!(count = results.len(), "1337x: found results"); |
| 181 | + tracing::debug!(count = results.len(), "1337x: results with magnet URLs"); |
138 | 182 |
|
139 | 183 | if results.is_empty() { |
140 | 184 | Err(IndexerError::NotFound) |
|
0 commit comments