pub mod json; #[cfg(feature = "postgres")] pub mod postgres; use std::{collections::HashSet, path::PathBuf}; use anyhow::Context; use async_trait::async_trait; use bytes::Bytes; use futures::stream::BoxStream; use librqbit_core::magnet::Magnet; use librqbit_core::Id20; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::{ session::TorrentId, torrent_state::ManagedTorrentHandle, AddTorrent, AddTorrentOptions, }; #[derive(Serialize, Deserialize, Clone)] pub struct SerializedTorrent { #[serde( serialize_with = "serialize_info_hash", deserialize_with = "deserialize_info_hash" )] info_hash: Id20, #[serde(skip)] torrent_bytes: Bytes, trackers: HashSet, output_folder: PathBuf, only_files: Option>, is_paused: bool, } impl SerializedTorrent { pub fn info_hash(&self) -> &Id20 { &self.info_hash } pub fn into_add_torrent(self) -> anyhow::Result<(AddTorrent<'static>, AddTorrentOptions)> { let add_torrent = if !self.torrent_bytes.is_empty() { AddTorrent::TorrentFileBytes(self.torrent_bytes) } else { let magnet = Magnet::from_id20(self.info_hash, self.trackers.into_iter().collect()).to_string(); AddTorrent::from_url(magnet) }; let opts = AddTorrentOptions { paused: self.is_paused, output_folder: Some( self.output_folder .to_str() .context("broken path")? .to_owned(), ), only_files: self.only_files, overwrite: true, ..Default::default() }; Ok((add_torrent, opts)) } } // TODO: make this info_hash first, ID-second. #[async_trait] pub trait SessionPersistenceStore: core::fmt::Debug + Send + Sync { async fn next_id(&self) -> anyhow::Result; async fn store(&self, id: TorrentId, torrent: &ManagedTorrentHandle) -> anyhow::Result<()>; async fn delete(&self, id: TorrentId) -> anyhow::Result<()>; async fn get(&self, id: TorrentId) -> anyhow::Result; async fn update_metadata( &self, id: TorrentId, torrent: &ManagedTorrentHandle, ) -> anyhow::Result<()>; async fn stream_all( &self, ) -> anyhow::Result>>; } pub type BoxSessionPersistenceStore = Box; fn serialize_info_hash(id: &Id20, serializer: S) -> Result where S: Serializer, { id.as_string().serialize(serializer) } fn deserialize_info_hash<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { Id20::deserialize(deserializer) }