1use std::fmt::{Debug, Display};
2
3use reqwest::Url;
4use serde::Serialize;
5use serde_with::{skip_serializing_none, SerializeDisplay};
6
7use crate::model::Sep;
8
9#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub enum TorrentFilter {
12 All,
13 Downloading,
14 Completed,
15 Paused,
16 Active,
17 Inactive,
18 Resumed,
19 Stalled,
20 StalledUploading,
21 StalledDownloading,
22 Errored,
23}
24
25#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
26pub struct Torrent {
27 pub added_on: Option<i64>,
29 pub amount_left: Option<i64>,
31 pub auto_tmm: Option<bool>,
33 pub availability: Option<f64>,
35 pub category: Option<String>,
37 pub comment: Option<String>,
39 pub completed: Option<i64>,
41 pub completion_on: Option<i64>,
43 pub content_path: Option<String>,
46 pub dl_limit: Option<i64>,
48 pub dlspeed: Option<i64>,
50 pub download_path: Option<String>,
52 pub downloaded: Option<i64>,
54 pub downloaded_session: Option<i64>,
56 pub eta: Option<i64>,
58 pub f_l_piece_prio: Option<bool>,
60 pub force_start: Option<bool>,
62 pub has_metadata: Option<bool>,
64 pub hash: Option<String>,
66 pub inactive_seeding_time_limit: Option<i64>,
68 pub infohash_v1: Option<String>,
70 pub infohash_v2: Option<String>,
72 pub last_activity: Option<i64>,
74 pub magnet_uri: Option<String>,
76 pub max_inactive_seeding_time: Option<i64>,
78 pub max_ratio: Option<f64>,
80 pub max_seeding_time: Option<i64>,
82 pub name: Option<String>,
84 pub num_complete: Option<i64>,
86 pub num_incomplete: Option<i64>,
88 pub num_leechs: Option<i64>,
90 pub num_seeds: Option<i64>,
92 pub popularity: Option<f64>,
94 pub priority: Option<i64>,
97 pub private: Option<bool>,
99 pub progress: Option<f64>,
101 pub ratio: Option<f64>,
103 pub ratio_limit: Option<f64>,
104 pub reannounce: Option<i64>,
106 pub root_path: Option<String>,
108 pub save_path: Option<String>,
110 pub seeding_time: Option<i64>,
112 pub seeding_time_limit: Option<i64>,
118 pub seen_complete: Option<i64>,
120 pub seq_dl: Option<bool>,
122 pub size: Option<i64>,
124 pub state: Option<State>,
126 pub super_seeding: Option<bool>,
128 pub tags: Option<String>,
130 pub time_active: Option<i64>,
132 pub total_size: Option<i64>,
135 pub tracker: Option<String>,
138 pub up_limit: Option<i64>,
140 pub uploaded: Option<i64>,
142 pub uploaded_session: Option<i64>,
144 pub upspeed: Option<i64>,
146}
147
148#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
149pub enum State {
150 #[serde(rename = "error")]
152 Error,
153 #[serde(rename = "missingFiles")]
155 MissingFiles,
156 #[serde(rename = "uploading")]
158 Uploading,
159 #[serde(rename = "pausedUP", alias = "stoppedUP")]
162 PausedUP,
163 #[serde(rename = "queuedUP")]
165 QueuedUP,
166 #[serde(rename = "stalledUP")]
168 StalledUP,
169 #[serde(rename = "checkingUP")]
171 CheckingUP,
172 #[serde(rename = "forcedUP")]
174 ForcedUP,
175 #[serde(rename = "allocating")]
177 Allocating,
178 #[serde(rename = "downloading")]
180 Downloading,
181 #[serde(rename = "metaDL")]
183 MetaDL,
184 #[serde(rename = "pausedDL", alias = "stoppedDL")]
187 PausedDL,
188 #[serde(rename = "queuedDL")]
190 QueuedDL,
191 #[serde(rename = "stalledDL")]
193 StalledDL,
194 #[serde(rename = "checkingDL")]
196 CheckingDL,
197 #[serde(rename = "forcedDL")]
199 ForcedDL,
200 #[serde(rename = "checkingResumeData")]
202 CheckingResumeData,
203 #[serde(rename = "moving")]
205 Moving,
206 #[serde(rename = "unknown")]
208 Unknown,
209}
210
211#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
212pub struct TorrentProperty {
213 pub save_path: Option<String>,
215 pub creation_date: Option<i64>,
217 pub piece_size: Option<i64>,
219 pub comment: Option<String>,
221 pub total_wasted: Option<i64>,
223 pub total_uploaded: Option<i64>,
225 pub total_uploaded_session: Option<i64>,
227 pub total_downloaded: Option<i64>,
229 pub total_downloaded_session: Option<i64>,
231 pub up_limit: Option<i64>,
233 pub dl_limit: Option<i64>,
235 pub time_elapsed: Option<i64>,
237 pub seeding_time: Option<i64>,
239 pub nb_connections: Option<i64>,
241 pub nb_connections_limit: Option<i64>,
243 pub share_ratio: Option<f64>,
245 pub addition_date: Option<i64>,
247 pub completion_date: Option<i64>,
249 pub created_by: Option<String>,
251 pub dl_speed_avg: Option<i64>,
253 pub dl_speed: Option<i64>,
255 pub eta: Option<i64>,
257 pub last_seen: Option<i64>,
259 pub peers: Option<i64>,
261 pub peers_total: Option<i64>,
263 pub pieces_have: Option<i64>,
265 pub pieces_num: Option<i64>,
267 pub reannounce: Option<i64>,
269 pub seeds: Option<i64>,
271 pub seeds_total: Option<i64>,
273 pub total_size: Option<i64>,
275 pub up_speed_avg: Option<i64>,
277 pub up_speed: Option<i64>,
279}
280
281#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
282pub struct WebSeed {
283 pub url: Url,
285}
286
287#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
288pub struct TorrentContent {
289 pub index: u64,
291 pub name: String,
293 pub size: u64,
295 pub progress: f64,
297 pub priority: Priority,
299 pub is_seed: Option<bool>,
301 #[serde(default)]
304 pub piece_range: Vec<i64>,
305 #[serde(default)]
307 pub availability: f64,
308}
309
310#[derive(
311 Debug,
312 Clone,
313 Copy,
314 PartialEq,
315 Eq,
316 PartialOrd,
317 Ord,
318 serde_repr::Serialize_repr,
319 serde_repr::Deserialize_repr,
320)]
321#[repr(u8)]
322pub enum Priority {
323 DoNotDownload = 0,
325 Normal = 1,
327 Mixed = 4,
329 High = 6,
331 Maximal = 7,
333}
334
335#[derive(
336 Debug,
337 Clone,
338 Copy,
339 PartialEq,
340 Eq,
341 PartialOrd,
342 Ord,
343 serde_repr::Serialize_repr,
344 serde_repr::Deserialize_repr,
345)]
346#[repr(u8)]
347pub enum PieceState {
348 NotDownloaded = 0,
350 Downloading = 1,
352 Downloaded = 2,
354}
355
356#[derive(Debug, Clone, PartialEq, Eq, SerializeDisplay)]
358pub enum Hashes {
359 Hashes(Sep<String, '|'>),
361 All,
363}
364
365impl<V: Into<Vec<String>>> From<V> for Hashes {
366 fn from(hashes: V) -> Self {
367 Hashes::Hashes(Sep::from(hashes))
368 }
369}
370
371impl Display for Hashes {
372 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
373 match self {
374 Hashes::Hashes(hashes) => write!(f, "{}", hashes),
375 Hashes::All => write!(f, "all"),
376 }
377 }
378}
379
380#[cfg_attr(feature = "builder", derive(typed_builder::TypedBuilder))]
381#[cfg_attr(
382 feature = "builder",
383 builder(field_defaults(default, setter(strip_option)))
384)]
385#[derive(Debug, Clone, PartialEq, Default, serde::Serialize)]
386#[skip_serializing_none]
387pub struct GetTorrentListArg {
388 pub filter: Option<TorrentFilter>,
393 pub category: Option<String>,
395 pub tag: Option<String>,
399 pub sort: Option<String>,
402 pub reverse: Option<bool>,
404 pub limit: Option<u64>,
406 pub offset: Option<i64>,
408 pub hashes: Option<String>,
410}
411
412#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
413#[serde(untagged)]
414pub enum TorrentSource {
415 Urls { urls: Sep<Url, '\n'> },
417 TorrentFiles { torrents: Vec<TorrentFile> },
419}
420#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
421pub struct TorrentFile {
423 pub filename: String,
424 pub data: Vec<u8>
425}
426impl Default for TorrentSource {
427 fn default() -> Self {
428 TorrentSource::Urls {
429 urls: Sep::from(vec![]),
430 }
431 }
432}
433fn is_torrent_files(source: &TorrentSource) -> bool {
434 matches!(source, TorrentSource::TorrentFiles { .. })
435}
436#[cfg_attr(feature = "builder", derive(typed_builder::TypedBuilder))]
437#[cfg_attr(
438 feature = "builder",
439 builder(field_defaults(default, setter(strip_option)))
440)]
441#[derive(Debug, Clone, PartialEq, serde::Serialize, Default)]
442#[skip_serializing_none]
443pub struct AddTorrentArg {
444 #[serde(flatten)]
445 #[cfg_attr(feature = "builder", builder(!default, setter(!strip_option)))]
446 #[serde(skip_serializing_if = "is_torrent_files")]
447 pub source: TorrentSource,
448 #[serde(skip_serializing_if = "Option::is_none")]
449 pub savepath: Option<String>,
451 #[serde(skip_serializing_if = "Option::is_none")]
453 pub cookie: Option<String>,
454 #[serde(skip_serializing_if = "Option::is_none")]
456 pub category: Option<String>,
457
458 #[serde(skip_serializing_if = "Option::is_none")]
460 pub tags: Option<String>,
461
462 #[serde(skip_serializing_if = "Option::is_none")]
464 pub skip_checking: Option<String>,
465
466 #[serde(skip_serializing_if = "Option::is_none")]
469 pub paused: Option<String>,
470
471 #[serde(skip_serializing_if = "Option::is_none")]
474 pub root_folder: Option<String>,
475
476 #[serde(skip_serializing_if = "Option::is_none")]
478 pub rename: Option<String>,
479
480 #[serde(rename = "upLimit")]
482 #[serde(skip_serializing_if = "Option::is_none")]
483 pub up_limit: Option<i64>,
484
485 #[serde(rename = "dlLimit")]
487 #[serde(skip_serializing_if = "Option::is_none")]
488 pub download_limit: Option<i64>,
489
490 #[serde(rename = "ratioLimit")]
492 #[serde(skip_serializing_if = "Option::is_none")]
493 pub ratio_limit: Option<f64>,
494
495 #[serde(rename = "seedingTimeLimit")]
497 #[serde(skip_serializing_if = "Option::is_none")]
498 pub seeding_time_limit: Option<i64>,
499
500 #[serde(rename = "autoTMM")]
502 #[serde(skip_serializing_if = "Option::is_none")]
503 pub auto_torrent_management: Option<bool>,
504
505 #[serde(rename = "sequentialDownload")]
508 #[serde(skip_serializing_if = "Option::is_none")]
509 pub sequential_download: Option<String>,
510
511 #[serde(rename = "firstLastPiecePrio")]
514 #[serde(skip_serializing_if = "Option::is_none")]
515 pub first_last_piece_priority: Option<String>,
516}
517
518#[cfg_attr(feature = "builder", derive(typed_builder::TypedBuilder))]
519#[derive(Debug, Clone, PartialEq, serde::Serialize)]
520#[serde(rename_all = "camelCase")]
521pub struct SetTorrentSharedLimitArg {
522 #[cfg_attr(feature = "builder", builder(setter(into)))]
523 pub hashes: Hashes,
524 #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
525 pub ratio_limit: Option<RatioLimit>,
526 #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
527 pub seeding_time_limit: Option<SeedingTimeLimit>,
528 #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
529 pub inactive_seeding_time_limit: Option<SeedingTimeLimit>,
530}
531
532#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
533pub enum RatioLimit {
534 Global,
535 NoLimit,
536 Limited(f64),
537}
538
539impl Serialize for RatioLimit {
540 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
541 where
542 S: serde::Serializer,
543 {
544 match self {
545 Self::Global => serializer.serialize_i64(-2),
546 Self::NoLimit => serializer.serialize_i64(-1),
547 Self::Limited(limit) => serializer.serialize_f64(*limit),
548 }
549 }
550}
551
552#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
553pub enum SeedingTimeLimit {
554 Global,
555 NoLimit,
556 Limited(u64),
558}
559
560impl Serialize for SeedingTimeLimit {
561 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
562 where
563 S: serde::Serializer,
564 {
565 match self {
566 Self::Global => serializer.serialize_i64(-2),
567 Self::NoLimit => serializer.serialize_i64(-1),
568 Self::Limited(limit) => serializer.serialize_u64(*limit),
569 }
570 }
571}
572
573#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
574pub(crate) struct HashArg<'a> {
575 hash: &'a str,
576}
577
578impl<'a> HashArg<'a> {
579 pub(crate) fn new(hash: &'a str) -> Self {
580 Self { hash }
581 }
582}
583
584#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
585pub(crate) struct HashesArg {
586 hashes: Hashes,
587}
588
589impl HashesArg {
590 pub(crate) fn new(hashes: impl Into<Hashes> + Send + Sync) -> Self {
591 Self {
592 hashes: hashes.into(),
593 }
594 }
595}