Initial commit for desktop configuration. Broken now
This commit is contained in:
parent
dd355b0a74
commit
a3475784e9
8 changed files with 304 additions and 57 deletions
|
|
@ -68,18 +68,24 @@ fn dump_dht(dht: &Dht, filename: &Path, tempfile_name: &Path) -> anyhow::Result<
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PersistentDht {
|
impl PersistentDht {
|
||||||
|
pub fn default_persistence_filename() -> anyhow::Result<PathBuf> {
|
||||||
|
let dirs = get_configuration_directory("dht")?;
|
||||||
|
let path = dirs.cache_dir().join("dht.json");
|
||||||
|
Ok(path)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn create(config: Option<PersistentDhtConfig>) -> anyhow::Result<Dht> {
|
pub async fn create(config: Option<PersistentDhtConfig>) -> anyhow::Result<Dht> {
|
||||||
let mut config = config.unwrap_or_default();
|
let mut config = config.unwrap_or_default();
|
||||||
let config_filename = match config.config_filename.take() {
|
let config_filename = match config.config_filename.take() {
|
||||||
Some(config_filename) => config_filename,
|
Some(config_filename) => config_filename,
|
||||||
None => {
|
None => Self::default_persistence_filename()?,
|
||||||
let dirs = get_configuration_directory("dht")?;
|
|
||||||
let path = dirs.cache_dir().join("dht.json");
|
|
||||||
info!("will store DHT routing table to {:?} periodically", &path);
|
|
||||||
path
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"will store DHT routing table to {:?} periodically",
|
||||||
|
&config_filename
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(parent) = config_filename.parent() {
|
if let Some(parent) = config_filename.parent() {
|
||||||
std::fs::create_dir_all(parent)
|
std::fs::create_dir_all(parent)
|
||||||
.with_context(|| format!("error creating dir {:?}", &parent))?;
|
.with_context(|| format!("error creating dir {:?}", &parent))?;
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ pub type Result<T> = std::result::Result<T, ApiError>;
|
||||||
|
|
||||||
/// Library API for use in different web frameworks.
|
/// Library API for use in different web frameworks.
|
||||||
/// Contains all methods you might want to expose with (de)serializable inputs/outputs.
|
/// Contains all methods you might want to expose with (de)serializable inputs/outputs.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Api {
|
pub struct Api {
|
||||||
session: Arc<Session>,
|
session: Arc<Session>,
|
||||||
rust_log_reload_tx: Option<UnboundedSender<String>>,
|
rust_log_reload_tx: Option<UnboundedSender<String>>,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,14 @@ impl ApiError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn new_from_text(status: StatusCode, text: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
status: Some(status),
|
||||||
|
kind: ApiErrorKind::Text(text),
|
||||||
|
plaintext: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn not_implemented(msg: &str) -> Self {
|
pub fn not_implemented(msg: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -69,6 +77,7 @@ impl ApiError {
|
||||||
enum ApiErrorKind {
|
enum ApiErrorKind {
|
||||||
TorrentNotFound(usize),
|
TorrentNotFound(usize),
|
||||||
DhtDisabled,
|
DhtDisabled,
|
||||||
|
Text(&'static str),
|
||||||
Other(anyhow::Error),
|
Other(anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +100,7 @@ impl Serialize for ApiError {
|
||||||
ApiErrorKind::TorrentNotFound(_) => "torrent_not_found",
|
ApiErrorKind::TorrentNotFound(_) => "torrent_not_found",
|
||||||
ApiErrorKind::DhtDisabled => "dht_disabled",
|
ApiErrorKind::DhtDisabled => "dht_disabled",
|
||||||
ApiErrorKind::Other(_) => "internal_error",
|
ApiErrorKind::Other(_) => "internal_error",
|
||||||
|
ApiErrorKind::Text(_) => "internal_error",
|
||||||
},
|
},
|
||||||
human_readable: format!("{self}"),
|
human_readable: format!("{self}"),
|
||||||
status: self.status().as_u16(),
|
status: self.status().as_u16(),
|
||||||
|
|
@ -130,6 +140,7 @@ impl std::fmt::Display for ApiError {
|
||||||
ApiErrorKind::TorrentNotFound(idx) => write!(f, "torrent {idx} not found"),
|
ApiErrorKind::TorrentNotFound(idx) => write!(f, "torrent {idx} not found"),
|
||||||
ApiErrorKind::Other(err) => write!(f, "{err:?}"),
|
ApiErrorKind::Other(err) => write!(f, "{err:?}"),
|
||||||
ApiErrorKind::DhtDisabled => write!(f, "DHT is disabled"),
|
ApiErrorKind::DhtDisabled => write!(f, "DHT is disabled"),
|
||||||
|
ApiErrorKind::Text(t) => write!(f, "{t}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -373,6 +373,11 @@ impl Session {
|
||||||
Self::new_with_opts(output_folder, SessionOptions::default()).await
|
Self::new_with_opts(output_folder, SessionOptions::default()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default_persistence_filename() -> anyhow::Result<PathBuf> {
|
||||||
|
let dir = get_configuration_directory("session")?;
|
||||||
|
Ok(dir.data_dir().join("session.json"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new session with options.
|
/// Create a new session with options.
|
||||||
pub async fn new_with_opts(
|
pub async fn new_with_opts(
|
||||||
output_folder: PathBuf,
|
output_folder: PathBuf,
|
||||||
|
|
@ -405,9 +410,7 @@ impl Session {
|
||||||
let peer_opts = opts.peer_opts.unwrap_or_default();
|
let peer_opts = opts.peer_opts.unwrap_or_default();
|
||||||
let persistence_filename = match opts.persistence_filename {
|
let persistence_filename = match opts.persistence_filename {
|
||||||
Some(filename) => filename,
|
Some(filename) => filename,
|
||||||
None => get_configuration_directory("session")?
|
None => Self::default_persistence_filename()?,
|
||||||
.data_dir()
|
|
||||||
.join("session.json"),
|
|
||||||
};
|
};
|
||||||
let spawner = BlockingSpawner::default();
|
let spawner = BlockingSpawner::default();
|
||||||
|
|
||||||
|
|
@ -608,7 +611,8 @@ impl Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn(
|
/// Spawn a task in the context of the session.
|
||||||
|
pub fn spawn(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
span: tracing::Span,
|
span: tracing::Span,
|
||||||
|
|
@ -626,7 +630,9 @@ impl Session {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(&self) {
|
/// Stop the session and all managed tasks.
|
||||||
|
// TODO: this probably doesn't kill everything properly.
|
||||||
|
pub async fn stop(&self) {
|
||||||
let _ = self.cancel_tx.send(());
|
let _ = self.cancel_tx.send(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
desktop/src-tauri/Cargo.lock
generated
2
desktop/src-tauri/Cargo.lock
generated
|
|
@ -3015,8 +3015,10 @@ dependencies = [
|
||||||
"directories",
|
"directories",
|
||||||
"http 1.0.0",
|
"http 1.0.0",
|
||||||
"librqbit",
|
"librqbit",
|
||||||
|
"parking_lot",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_with",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ http = "1.0.0"
|
||||||
directories = "5.0.1"
|
directories = "5.0.1"
|
||||||
tracing-subscriber = {version = "0.3.18", features = ["env-filter"] }
|
tracing-subscriber = {version = "0.3.18", features = ["env-filter"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
serde_with = "3.4.0"
|
||||||
|
parking_lot = "0.12.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||||
|
|
|
||||||
132
desktop/src-tauri/src/config.rs
Normal file
132
desktop/src-tauri/src/config.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
use std::{
|
||||||
|
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
|
||||||
|
path::PathBuf,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use librqbit::{dht::PersistentDht, Session};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_with::serde_as;
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct RqbitDesktopConfigDht {
|
||||||
|
pub disable: bool,
|
||||||
|
pub disable_persistence: bool,
|
||||||
|
pub persistence_filename: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RqbitDesktopConfigDht {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
disable: false,
|
||||||
|
disable_persistence: false,
|
||||||
|
persistence_filename: PersistentDht::default_persistence_filename().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub struct RqbitDesktopConfigTcpListen {
|
||||||
|
pub disable: bool,
|
||||||
|
pub min_port: u16,
|
||||||
|
pub max_port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RqbitDesktopConfigTcpListen {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
disable: false,
|
||||||
|
// TODO: use consts from librqbit
|
||||||
|
min_port: 4240,
|
||||||
|
max_port: 4260,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct RqbitDesktopConfigPersistence {
|
||||||
|
pub disable: bool,
|
||||||
|
pub filename: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RqbitDesktopConfigPersistence {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
disable: false,
|
||||||
|
filename: Session::default_persistence_filename().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub struct RqbitDesktopConfigPeerOpts {
|
||||||
|
#[serde_as(as = "serde_with::DurationSeconds")]
|
||||||
|
pub connect_timeout: Duration,
|
||||||
|
|
||||||
|
#[serde_as(as = "serde_with::DurationSeconds")]
|
||||||
|
pub read_write_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RqbitDesktopConfigPeerOpts {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
connect_timeout: Duration::from_secs(2),
|
||||||
|
read_write_timeout: Duration::from_secs(10),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct RqbitDesktopConfigHttpApi {
|
||||||
|
pub disable: bool,
|
||||||
|
pub listen_addr: SocketAddr,
|
||||||
|
pub read_only: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RqbitDesktopConfigHttpApi {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
disable: Default::default(),
|
||||||
|
listen_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 3030)),
|
||||||
|
read_only: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, Serialize, Deserialize)]
|
||||||
|
pub struct RqbitDesktopConfigUpnp {
|
||||||
|
pub disable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct RqbitDesktopConfig {
|
||||||
|
pub default_download_location: PathBuf,
|
||||||
|
pub dht: RqbitDesktopConfigDht,
|
||||||
|
pub tcp_listen: RqbitDesktopConfigTcpListen,
|
||||||
|
pub upnp: RqbitDesktopConfigUpnp,
|
||||||
|
pub persistence: RqbitDesktopConfigPersistence,
|
||||||
|
pub peer_opts: RqbitDesktopConfigPeerOpts,
|
||||||
|
pub http_api: RqbitDesktopConfigHttpApi,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RqbitDesktopConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
let download_folder = directories::UserDirs::new()
|
||||||
|
.expect("directories::UserDirs::new()")
|
||||||
|
.download_dir()
|
||||||
|
.expect("download_dir()")
|
||||||
|
.to_path_buf();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
default_download_location: download_folder,
|
||||||
|
dht: Default::default(),
|
||||||
|
tcp_listen: Default::default(),
|
||||||
|
upnp: Default::default(),
|
||||||
|
persistence: Default::default(),
|
||||||
|
peer_opts: Default::default(),
|
||||||
|
http_api: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,134 @@
|
||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use config::RqbitDesktopConfig;
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use librqbit::{
|
use librqbit::{
|
||||||
api::{
|
api::{
|
||||||
ApiAddTorrentResponse, EmptyJsonResponse, TorrentDetailsResponse, TorrentListResponse,
|
ApiAddTorrentResponse, EmptyJsonResponse, TorrentDetailsResponse, TorrentListResponse,
|
||||||
TorrentStats,
|
TorrentStats,
|
||||||
},
|
},
|
||||||
librqbit_spawn, AddTorrent, AddTorrentOptions, Api, ApiError, Session, SessionOptions,
|
dht::PersistentDhtConfig,
|
||||||
|
librqbit_spawn, AddTorrent, AddTorrentOptions, Api, ApiError, PeerConnectionOptions, Session,
|
||||||
|
SessionOptions,
|
||||||
};
|
};
|
||||||
|
use parking_lot::RwLock;
|
||||||
use tracing::error_span;
|
use tracing::error_span;
|
||||||
|
|
||||||
struct State {
|
const ERR_NOT_CONFIGURED: ApiError =
|
||||||
|
ApiError::new_from_text(StatusCode::FAILED_DEPENDENCY, "not configured");
|
||||||
|
|
||||||
|
struct StateShared {
|
||||||
|
config: config::RqbitDesktopConfig,
|
||||||
api: Api,
|
api: Api,
|
||||||
|
session: Arc<Session>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StateShared {}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
shared: Arc<RwLock<Option<StateShared>>>,
|
||||||
|
rust_log_reload_tx: tokio::sync::mpsc::UnboundedSender<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn api(&self) -> Result<Api, ApiError> {
|
||||||
|
let g = self.shared.read();
|
||||||
|
match &*g {
|
||||||
|
Some(s) => Ok(s.api.clone()),
|
||||||
|
None => Err(ERR_NOT_CONFIGURED),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_config(&self) -> Option<RqbitDesktopConfig> {
|
||||||
|
self.shared.read().as_ref().map(|s| s.config.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn configure(&self, config: RqbitDesktopConfig) -> Result<(), ApiError> {
|
||||||
|
let existing = self.shared.write().take();
|
||||||
|
|
||||||
|
if let Some(existing) = existing {
|
||||||
|
existing.session.stop().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let config_clone = config.clone();
|
||||||
|
|
||||||
|
let session = Session::new_with_opts(
|
||||||
|
config.default_download_location,
|
||||||
|
SessionOptions {
|
||||||
|
disable_dht: config.dht.disable,
|
||||||
|
disable_dht_persistence: config.dht.disable_persistence,
|
||||||
|
dht_config: Some(PersistentDhtConfig {
|
||||||
|
config_filename: Some(config.dht.persistence_filename),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
persistence: !config.persistence.disable,
|
||||||
|
persistence_filename: Some(config.persistence.filename),
|
||||||
|
peer_opts: Some(PeerConnectionOptions {
|
||||||
|
connect_timeout: Some(config.peer_opts.connect_timeout),
|
||||||
|
read_write_timeout: Some(config.peer_opts.read_write_timeout),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
listen_port_range: if !config.tcp_listen.disable {
|
||||||
|
Some(config.tcp_listen.min_port..config.tcp_listen.max_port)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
enable_upnp_port_forwarding: !config.upnp.disable,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("couldn't set up librqbit session")?;
|
||||||
|
|
||||||
|
let api = Api::new(session.clone(), None);
|
||||||
|
|
||||||
|
if !config.http_api.disable {
|
||||||
|
let http_api_task = librqbit::http_api::HttpApi::new(
|
||||||
|
session.clone(),
|
||||||
|
Some(self.rust_log_reload_tx.clone()),
|
||||||
|
)
|
||||||
|
.make_http_api_and_run(config.http_api.listen_addr, config.http_api.read_only);
|
||||||
|
|
||||||
|
session.spawn("http api", error_span!("http_api"), http_api_task);
|
||||||
|
}
|
||||||
|
|
||||||
|
*self.shared.write() = Some(StateShared {
|
||||||
|
config: config_clone,
|
||||||
|
api,
|
||||||
|
session,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn torrents_list(state: tauri::State<State>) -> TorrentListResponse {
|
fn config_default() -> config::RqbitDesktopConfig {
|
||||||
state.api.api_torrent_list()
|
config::RqbitDesktopConfig::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn config_current(state: tauri::State<'_, State>) -> Option<config::RqbitDesktopConfig> {
|
||||||
|
state.current_config()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn config_change(
|
||||||
|
state: tauri::State<'_, State>,
|
||||||
|
config: RqbitDesktopConfig,
|
||||||
|
) -> Result<EmptyJsonResponse, ApiError> {
|
||||||
|
state.configure(config).await.map(|_| EmptyJsonResponse {})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn torrents_list(state: tauri::State<State>) -> Result<TorrentListResponse, ApiError> {
|
||||||
|
Ok(state.api()?.api_torrent_list())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
@ -28,7 +138,7 @@ async fn torrent_create_from_url(
|
||||||
opts: Option<AddTorrentOptions>,
|
opts: Option<AddTorrentOptions>,
|
||||||
) -> Result<ApiAddTorrentResponse, ApiError> {
|
) -> Result<ApiAddTorrentResponse, ApiError> {
|
||||||
state
|
state
|
||||||
.api
|
.api()?
|
||||||
.api_add_torrent(AddTorrent::Url(url.into()), opts)
|
.api_add_torrent(AddTorrent::Url(url.into()), opts)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +155,7 @@ async fn torrent_create_from_base64_file(
|
||||||
.context("invalid base64")
|
.context("invalid base64")
|
||||||
.map_err(|e| ApiError::new_from_anyhow(StatusCode::BAD_REQUEST, e))?;
|
.map_err(|e| ApiError::new_from_anyhow(StatusCode::BAD_REQUEST, e))?;
|
||||||
state
|
state
|
||||||
.api
|
.api()?
|
||||||
.api_add_torrent(AddTorrent::TorrentFileBytes(bytes.into()), opts)
|
.api_add_torrent(AddTorrent::TorrentFileBytes(bytes.into()), opts)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +165,7 @@ async fn torrent_details(
|
||||||
state: tauri::State<'_, State>,
|
state: tauri::State<'_, State>,
|
||||||
id: usize,
|
id: usize,
|
||||||
) -> Result<TorrentDetailsResponse, ApiError> {
|
) -> Result<TorrentDetailsResponse, ApiError> {
|
||||||
state.api.api_torrent_details(id)
|
state.api()?.api_torrent_details(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
@ -63,7 +173,7 @@ async fn torrent_stats(
|
||||||
state: tauri::State<'_, State>,
|
state: tauri::State<'_, State>,
|
||||||
id: usize,
|
id: usize,
|
||||||
) -> Result<TorrentStats, ApiError> {
|
) -> Result<TorrentStats, ApiError> {
|
||||||
state.api.api_stats_v1(id)
|
state.api()?.api_stats_v1(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
@ -71,7 +181,7 @@ async fn torrent_action_delete(
|
||||||
state: tauri::State<'_, State>,
|
state: tauri::State<'_, State>,
|
||||||
id: usize,
|
id: usize,
|
||||||
) -> Result<EmptyJsonResponse, ApiError> {
|
) -> Result<EmptyJsonResponse, ApiError> {
|
||||||
state.api.api_torrent_action_delete(id)
|
state.api()?.api_torrent_action_delete(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
@ -79,7 +189,7 @@ async fn torrent_action_pause(
|
||||||
state: tauri::State<'_, State>,
|
state: tauri::State<'_, State>,
|
||||||
id: usize,
|
id: usize,
|
||||||
) -> Result<EmptyJsonResponse, ApiError> {
|
) -> Result<EmptyJsonResponse, ApiError> {
|
||||||
state.api.api_torrent_action_pause(id)
|
state.api()?.api_torrent_action_pause(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
@ -87,7 +197,7 @@ async fn torrent_action_forget(
|
||||||
state: tauri::State<'_, State>,
|
state: tauri::State<'_, State>,
|
||||||
id: usize,
|
id: usize,
|
||||||
) -> Result<EmptyJsonResponse, ApiError> {
|
) -> Result<EmptyJsonResponse, ApiError> {
|
||||||
state.api.api_torrent_action_forget(id)
|
state.api()?.api_torrent_action_forget(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
@ -95,7 +205,7 @@ async fn torrent_action_start(
|
||||||
state: tauri::State<'_, State>,
|
state: tauri::State<'_, State>,
|
||||||
id: usize,
|
id: usize,
|
||||||
) -> Result<EmptyJsonResponse, ApiError> {
|
) -> Result<EmptyJsonResponse, ApiError> {
|
||||||
state.api.api_torrent_action_start(id)
|
state.api()?.api_torrent_action_start(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|
@ -133,41 +243,15 @@ fn init_logging() -> tokio::sync::mpsc::UnboundedSender<String> {
|
||||||
reload_tx
|
reload_tx
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_session() {
|
async fn start() {
|
||||||
|
tauri::async_runtime::set(tokio::runtime::Handle::current());
|
||||||
let rust_log_reload_tx = init_logging();
|
let rust_log_reload_tx = init_logging();
|
||||||
|
|
||||||
tauri::async_runtime::set(tokio::runtime::Handle::current());
|
|
||||||
|
|
||||||
let download_folder = directories::UserDirs::new()
|
|
||||||
.expect("directories::UserDirs::new()")
|
|
||||||
.download_dir()
|
|
||||||
.expect("download_dir()")
|
|
||||||
.to_path_buf();
|
|
||||||
|
|
||||||
let session = Session::new_with_opts(
|
|
||||||
download_folder,
|
|
||||||
SessionOptions {
|
|
||||||
disable_dht: false,
|
|
||||||
disable_dht_persistence: false,
|
|
||||||
persistence: true,
|
|
||||||
listen_port_range: Some(4240..4260),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.expect("couldn't set up librqbit session");
|
|
||||||
|
|
||||||
let api = Api::new(session.clone(), None);
|
|
||||||
|
|
||||||
librqbit_spawn(
|
|
||||||
"http api",
|
|
||||||
error_span!("http_api"),
|
|
||||||
librqbit::http_api::HttpApi::new(session, Some(rust_log_reload_tx))
|
|
||||||
.make_http_api_and_run("127.0.0.1:3030".parse().unwrap(), false),
|
|
||||||
);
|
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.manage(State { api })
|
.manage(State {
|
||||||
|
shared: Arc::new(RwLock::new(None)),
|
||||||
|
rust_log_reload_tx,
|
||||||
|
})
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
torrents_list,
|
torrents_list,
|
||||||
torrent_details,
|
torrent_details,
|
||||||
|
|
@ -178,7 +262,10 @@ async fn start_session() {
|
||||||
torrent_action_forget,
|
torrent_action_forget,
|
||||||
torrent_action_start,
|
torrent_action_start,
|
||||||
torrent_create_from_base64_file,
|
torrent_create_from_base64_file,
|
||||||
get_version
|
get_version,
|
||||||
|
config_default,
|
||||||
|
config_current,
|
||||||
|
config_change,
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|
@ -189,5 +276,5 @@ fn main() {
|
||||||
.enable_all()
|
.enable_all()
|
||||||
.build()
|
.build()
|
||||||
.expect("couldn't set up tokio runtime")
|
.expect("couldn't set up tokio runtime")
|
||||||
.block_on(start_session())
|
.block_on(start())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue