feat: finsih qbit adapter

This commit is contained in:
master 2025-04-05 14:24:47 +08:00
parent b0c12acbc6
commit a3609696c7
4 changed files with 29 additions and 89 deletions

View File

@ -23,8 +23,7 @@ where
&self, &self,
selector: Self::Selector, selector: Self::Selector,
) -> Result<Self::IdSelector, DownloaderError> { ) -> Result<Self::IdSelector, DownloaderError> {
let hashes = let hashes = <Self as TorrentDownloaderTrait>::query_torrent_hashes(self, selector).await?;
<Self as TorrentDownloaderTrait>::query_torrent_hashes(&self, selector).await?;
self.pause_torrents(hashes).await self.pause_torrents(hashes).await
} }
@ -32,16 +31,14 @@ where
&self, &self,
selector: Self::Selector, selector: Self::Selector,
) -> Result<Self::IdSelector, DownloaderError> { ) -> Result<Self::IdSelector, DownloaderError> {
let hashes = let hashes = <Self as TorrentDownloaderTrait>::query_torrent_hashes(self, selector).await?;
<Self as TorrentDownloaderTrait>::query_torrent_hashes(&self, selector).await?;
self.resume_torrents(hashes).await self.resume_torrents(hashes).await
} }
async fn remove_downloads( async fn remove_downloads(
&self, &self,
selector: Self::Selector, selector: Self::Selector,
) -> Result<Self::IdSelector, DownloaderError> { ) -> Result<Self::IdSelector, DownloaderError> {
let hashes = let hashes = <Self as TorrentDownloaderTrait>::query_torrent_hashes(self, selector).await?;
<Self as TorrentDownloaderTrait>::query_torrent_hashes(&self, selector).await?;
self.remove_torrents(hashes).await self.remove_torrents(hashes).await
} }

View File

@ -131,7 +131,7 @@ impl TorrentFileSource {
.boxed() .boxed()
.and_then(|s| { .and_then(|s| {
s.path_segments() s.path_segments()
.and_then(|p| p.last()) .and_then(|mut p| p.next_back())
.map(String::from) .map(String::from)
.ok_or_else(|| anyhow::anyhow!("invalid url")) .ok_or_else(|| anyhow::anyhow!("invalid url"))
.to_dyn_boxed() .to_dyn_boxed()

View File

@ -2,7 +2,6 @@ use std::{
borrow::Cow, borrow::Cow,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
fmt::Debug, fmt::Debug,
io,
sync::{Arc, Weak}, sync::{Arc, Weak},
time::Duration, time::Duration,
}; };
@ -167,6 +166,7 @@ impl TorrentTaskTrait for QBittorrentTask {
.as_deref() .as_deref()
.unwrap_or("") .unwrap_or("")
.split(',') .split(',')
.map(|s| s.trim())
.filter(|s| !s.is_empty()) .filter(|s| !s.is_empty())
.map(Cow::Borrowed) .map(Cow::Borrowed)
} }
@ -357,7 +357,7 @@ impl QBittorrentDownloader {
save_path: creation.save_path.into(), save_path: creation.save_path.into(),
wait_sync_timeout: creation wait_sync_timeout: creation
.wait_sync_timeout .wait_sync_timeout
.unwrap_or(Duration::from_millis(10000)), .unwrap_or(Duration::from_secs(10)),
downloader_id: creation.downloader_id, downloader_id: creation.downloader_id,
sync_watch: watch::channel(Utc::now()).0, sync_watch: watch::channel(Utc::now()).0,
sync_data: Arc::new(RwLock::new(QBittorrentSyncData::default())), sync_data: Arc::new(RwLock::new(QBittorrentSyncData::default())),
@ -433,8 +433,12 @@ impl QBittorrentDownloader {
category: &str, category: &str,
) -> Result<(), DownloaderError> { ) -> Result<(), DownloaderError> {
{ {
let category_no_exists = {
let sync_data = self.sync_data.read().await; let sync_data = self.sync_data.read().await;
if !sync_data.categories.contains_key(category) { !sync_data.categories.contains_key(category)
};
if category_no_exists {
self.add_category(category).await?; self.add_category(category).await?;
} }
} }
@ -495,49 +499,6 @@ impl QBittorrentDownloader {
Ok(()) Ok(())
} }
#[instrument(level = "debug", skip(self, replacer))]
pub async fn move_torrent_contents<F: FnOnce(String) -> String>(
&self,
hash: &str,
replacer: F,
) -> Result<(), DownloaderError> {
let old_path = {
let sync_data = self.sync_data.read().await;
sync_data
.torrents
.get(hash)
.and_then(|t| t.content_path.as_deref())
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
"no torrent or torrent does not contain content path",
)
})?
.to_string()
};
let new_path = replacer(old_path.clone());
self.client
.rename_file(hash, old_path.clone(), new_path.to_string())
.await?;
self.wait_sync_until(
|sync_data| {
let torrents = &sync_data.torrents;
torrents.get(hash).is_some_and(|t| {
t.content_path.as_deref().is_some_and(|p| {
path_equals_as_file_url(p, &new_path)
.inspect_err(|error| {
tracing::warn!(name = "path_equals_as_file_url", error = ?error);
})
.unwrap_or(false)
})
})
},
None,
)
.await?;
Ok(())
}
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
pub async fn move_torrents( pub async fn move_torrents(
&self, &self,
@ -608,17 +569,23 @@ impl QBittorrentDownloader {
where where
S: Fn(&QBittorrentSyncData) -> bool, S: Fn(&QBittorrentSyncData) -> bool,
{ {
{
let sync_data = &self.sync_data.read().await;
if stop_wait_fn(sync_data) {
return Ok(());
}
}
let timeout = timeout.unwrap_or(self.wait_sync_timeout); let timeout = timeout.unwrap_or(self.wait_sync_timeout);
let start_time = Utc::now(); let start_time = Utc::now();
let mut receiver = self.sync_watch.subscribe(); let mut receiver = self.sync_watch.subscribe();
while let Ok(()) = receiver.changed().await { while let Ok(()) = receiver.changed().await {
let has_timeout = { let has_timeout = {
let sync_time = *receiver.borrow(); let sync_time = *receiver.borrow();
sync_time let diff_time = sync_time - start_time;
.signed_duration_since(start_time) diff_time.num_milliseconds() > timeout.as_millis() as i64
.num_milliseconds()
> timeout.as_millis() as i64
}; };
if has_timeout { if has_timeout {
tracing::warn!(name = "wait_until timeout", timeout = ?timeout); tracing::warn!(name = "wait_until timeout", timeout = ?timeout);
@ -958,7 +925,7 @@ pub mod tests {
.init(); .init();
let torrents_image = create_torrents_testcontainers().await?; let torrents_image = create_torrents_testcontainers().await?;
let torrents_container = torrents_image.start().await?; let _torrents_container = torrents_image.start().await?;
let torrents_req = MockRequest { let torrents_req = MockRequest {
id: "f10ebdda-dd2e-43f8-b80c-bf0884d071c4".into(), id: "f10ebdda-dd2e-43f8-b80c-bf0884d071c4".into(),
@ -1022,8 +989,6 @@ pub mod tests {
) )
.await?; .await?;
torrents_container.stop().await?;
Ok(()) Ok(())
} }
@ -1090,12 +1055,11 @@ pub mod tests {
assert!(!files.is_empty()); assert!(!files.is_empty());
let first_file = files.first().expect("should have first file"); let first_file = files.first().expect("should have first file");
assert_eq!( assert!(
&first_file.name, &first_file.name.ends_with(r#"[Nekomoe kissaten&LoliHouse] Boku no Kokoro no Yabai Yatsu - 20 [WebRip 1080p HEVC-10bit AAC ASSx2].mkv"#)
r#"[Nekomoe kissaten&LoliHouse] Boku no Kokoro no Yabai Yatsu - 20 [WebRip 1080p HEVC-10bit AAC ASSx2].mkv"#
); );
let test_tag = format!("test_tag_{}", Utc::now().timestamp()); let test_tag = "test_tag".to_string();
downloader downloader
.add_torrent_tags(vec![torrent_hash.clone()], vec![test_tag.clone()]) .add_torrent_tags(vec![torrent_hash.clone()], vec![test_tag.clone()])
@ -1138,29 +1102,6 @@ pub mod tests {
)? )?
); );
downloader
.move_torrent_contents(&torrent_hash, |f| {
f.replace(&folder_name, &format!("moved_{}", &folder_name))
})
.await?;
let target_torrent = get_torrent().await?;
let actual_content_path = &target_torrent
.torrent
.content_path
.expect("failed to get actual content path");
assert!(
path_equals_as_file_url(
actual_content_path,
base_save_path.join(actual_content_path)
)
.whatever_context::<_, RError>(
"failed to compare actual content path and found expected content path"
)?
);
downloader downloader
.remove_torrents(vec![torrent_hash.clone()].into()) .remove_torrents(vec![torrent_hash.clone()].into())
.await?; .await?;
@ -1175,6 +1116,8 @@ pub mod tests {
assert!(torrent_infos1.is_empty()); assert!(torrent_infos1.is_empty());
tracing::info!("test finished");
Ok(()) Ok(())
} }
} }

View File

@ -15,7 +15,7 @@ docker buildx build --platform linux/amd64 --tag konobangu-testing-torrents:late
## Run ## Run
```bash ```bash
docker run --network_mode=host --name konobangu-testing-torrents konobangu-testing-torrents:latest docker run -p 6080:6080 -p 6081:6081 -p 6082:6082 --name konobangu-testing-torrents konobangu-testing-torrents:latest
``` ```
## Publish ## Publish