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 completed: Option<i64>,
39 pub completion_on: Option<i64>,
41 pub content_path: Option<String>,
44 pub dl_limit: Option<i64>,
46 pub dlspeed: Option<i64>,
48 pub downloaded: Option<i64>,
50 pub downloaded_session: Option<i64>,
52 pub eta: Option<i64>,
54 pub f_l_piece_prio: Option<bool>,
56 pub force_start: Option<bool>,
58 pub hash: Option<String>,
60 pub last_activity: Option<i64>,
62 pub magnet_uri: Option<String>,
64 pub max_ratio: Option<f64>,
66 pub max_seeding_time: Option<i64>,
68 pub name: Option<String>,
70 pub num_complete: Option<i64>,
72 pub num_incomplete: Option<i64>,
74 pub num_leechs: Option<i64>,
76 pub num_seeds: Option<i64>,
78 pub priority: Option<i64>,
81 pub progress: Option<f64>,
83 pub ratio: Option<f64>,
85 pub ratio_limit: Option<f64>,
86 pub save_path: Option<String>,
88 pub seeding_time: Option<i64>,
90 pub seeding_time_limit: Option<i64>,
96 pub seen_complete: Option<i64>,
98 pub seq_dl: Option<bool>,
100 pub size: Option<i64>,
102 pub state: Option<State>,
104 pub super_seeding: Option<bool>,
106 pub tags: Option<String>,
108 pub time_active: Option<i64>,
110 pub total_size: Option<i64>,
113 pub tracker: Option<String>,
116 pub up_limit: Option<i64>,
118 pub uploaded: Option<i64>,
120 pub uploaded_session: Option<i64>,
122 pub upspeed: Option<i64>,
124}
125
126#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
127pub enum State {
128 #[serde(rename = "error")]
130 Error,
131 #[serde(rename = "missingFiles")]
133 MissingFiles,
134 #[serde(rename = "uploading")]
136 Uploading,
137 #[serde(rename = "pausedUP", alias = "stoppedUP")]
140 PausedUP,
141 #[serde(rename = "queuedUP")]
143 QueuedUP,
144 #[serde(rename = "stalledUP")]
146 StalledUP,
147 #[serde(rename = "checkingUP")]
149 CheckingUP,
150 #[serde(rename = "forcedUP")]
152 ForcedUP,
153 #[serde(rename = "allocating")]
155 Allocating,
156 #[serde(rename = "downloading")]
158 Downloading,
159 #[serde(rename = "metaDL")]
161 MetaDL,
162 #[serde(rename = "pausedDL", alias = "stoppedDL")]
165 PausedDL,
166 #[serde(rename = "queuedDL")]
168 QueuedDL,
169 #[serde(rename = "stalledDL")]
171 StalledDL,
172 #[serde(rename = "checkingDL")]
174 CheckingDL,
175 #[serde(rename = "forcedDL")]
177 ForcedDL,
178 #[serde(rename = "checkingResumeData")]
180 CheckingResumeData,
181 #[serde(rename = "moving")]
183 Moving,
184 #[serde(rename = "unknown")]
186 Unknown,
187}
188
189#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
190pub struct TorrentProperty {
191 pub save_path: Option<String>,
193 pub creation_date: Option<i64>,
195 pub piece_size: Option<i64>,
197 pub comment: Option<String>,
199 pub total_wasted: Option<i64>,
201 pub total_uploaded: Option<i64>,
203 pub total_uploaded_session: Option<i64>,
205 pub total_downloaded: Option<i64>,
207 pub total_downloaded_session: Option<i64>,
209 pub up_limit: Option<i64>,
211 pub dl_limit: Option<i64>,
213 pub time_elapsed: Option<i64>,
215 pub seeding_time: Option<i64>,
217 pub nb_connections: Option<i64>,
219 pub nb_connections_limit: Option<i64>,
221 pub share_ratio: Option<f64>,
223 pub addition_date: Option<i64>,
225 pub completion_date: Option<i64>,
227 pub created_by: Option<String>,
229 pub dl_speed_avg: Option<i64>,
231 pub dl_speed: Option<i64>,
233 pub eta: Option<i64>,
235 pub last_seen: Option<i64>,
237 pub peers: Option<i64>,
239 pub peers_total: Option<i64>,
241 pub pieces_have: Option<i64>,
243 pub pieces_num: Option<i64>,
245 pub reannounce: Option<i64>,
247 pub seeds: Option<i64>,
249 pub seeds_total: Option<i64>,
251 pub total_size: Option<i64>,
253 pub up_speed_avg: Option<i64>,
255 pub up_speed: Option<i64>,
257}
258
259#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
260pub struct WebSeed {
261 pub url: Url,
263}
264
265#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
266pub struct TorrentContent {
267 pub index: u64,
269 pub name: String,
271 pub size: u64,
273 pub progress: f64,
275 pub priority: Priority,
277 pub is_seed: Option<bool>,
279 #[serde(default)]
282 pub piece_range: Vec<i64>,
283 #[serde(default)]
285 pub availability: f64,
286}
287
288#[derive(
289 Debug,
290 Clone,
291 Copy,
292 PartialEq,
293 Eq,
294 PartialOrd,
295 Ord,
296 serde_repr::Serialize_repr,
297 serde_repr::Deserialize_repr,
298)]
299#[repr(u8)]
300pub enum Priority {
301 DoNotDownload = 0,
303 Normal = 1,
305 Mixed = 4,
307 High = 6,
309 Maximal = 7,
311}
312
313#[derive(
314 Debug,
315 Clone,
316 Copy,
317 PartialEq,
318 Eq,
319 PartialOrd,
320 Ord,
321 serde_repr::Serialize_repr,
322 serde_repr::Deserialize_repr,
323)]
324#[repr(u8)]
325pub enum PieceState {
326 NotDownloaded = 0,
328 Downloading = 1,
330 Downloaded = 2,
332}
333
334#[derive(Debug, Clone, PartialEq, Eq, SerializeDisplay)]
336pub enum Hashes {
337 Hashes(Sep<String, '|'>),
339 All,
341}
342
343impl<V: Into<Vec<String>>> From<V> for Hashes {
344 fn from(hashes: V) -> Self {
345 Hashes::Hashes(Sep::from(hashes))
346 }
347}
348
349impl Display for Hashes {
350 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351 match self {
352 Hashes::Hashes(hashes) => write!(f, "{}", hashes),
353 Hashes::All => write!(f, "all"),
354 }
355 }
356}
357
358#[cfg_attr(feature = "builder", derive(typed_builder::TypedBuilder))]
359#[cfg_attr(
360 feature = "builder",
361 builder(field_defaults(default, setter(strip_option)))
362)]
363#[derive(Debug, Clone, PartialEq, Default, serde::Serialize)]
364#[skip_serializing_none]
365pub struct GetTorrentListArg {
366 pub filter: Option<TorrentFilter>,
371 pub category: Option<String>,
373 pub tag: Option<String>,
377 pub sort: Option<String>,
380 pub reverse: Option<bool>,
382 pub limit: Option<u64>,
384 pub offset: Option<i64>,
386 pub hashes: Option<String>,
388}
389
390#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
391#[serde(untagged)]
392pub enum TorrentSource {
393 Urls { urls: Sep<Url, '\n'> },
395 TorrentFiles { torrents: Vec<TorrentFile> },
397}
398#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
399pub struct TorrentFile {
401 pub filename: String,
402 pub data: Vec<u8>
403}
404impl Default for TorrentSource {
405 fn default() -> Self {
406 TorrentSource::Urls {
407 urls: Sep::from(vec![]),
408 }
409 }
410}
411fn is_torrent_files(source: &TorrentSource) -> bool {
412 matches!(source, TorrentSource::TorrentFiles { .. })
413}
414#[cfg_attr(feature = "builder", derive(typed_builder::TypedBuilder))]
415#[cfg_attr(
416 feature = "builder",
417 builder(field_defaults(default, setter(strip_option)))
418)]
419#[derive(Debug, Clone, PartialEq, serde::Serialize, Default)]
420#[skip_serializing_none]
421pub struct AddTorrentArg {
422 #[serde(flatten)]
423 #[cfg_attr(feature = "builder", builder(!default, setter(!strip_option)))]
424 #[serde(skip_serializing_if = "is_torrent_files")]
425 pub source: TorrentSource,
426 #[serde(skip_serializing_if = "Option::is_none")]
427 pub savepath: Option<String>,
429 #[serde(skip_serializing_if = "Option::is_none")]
431 pub cookie: Option<String>,
432 #[serde(skip_serializing_if = "Option::is_none")]
434 pub category: Option<String>,
435
436 #[serde(skip_serializing_if = "Option::is_none")]
438 pub tags: Option<String>,
439
440 #[serde(skip_serializing_if = "Option::is_none")]
442 pub skip_checking: Option<String>,
443
444 #[serde(skip_serializing_if = "Option::is_none")]
447 pub paused: Option<String>,
448
449 #[serde(skip_serializing_if = "Option::is_none")]
452 pub root_folder: Option<String>,
453
454 #[serde(skip_serializing_if = "Option::is_none")]
456 pub rename: Option<String>,
457
458 #[serde(rename = "upLimit")]
460 #[serde(skip_serializing_if = "Option::is_none")]
461 pub up_limit: Option<i64>,
462
463 #[serde(rename = "dlLimit")]
465 #[serde(skip_serializing_if = "Option::is_none")]
466 pub download_limit: Option<i64>,
467
468 #[serde(rename = "ratioLimit")]
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub ratio_limit: Option<f64>,
472
473 #[serde(rename = "seedingTimeLimit")]
475 #[serde(skip_serializing_if = "Option::is_none")]
476 pub seeding_time_limit: Option<i64>,
477
478 #[serde(rename = "autoTMM")]
480 #[serde(skip_serializing_if = "Option::is_none")]
481 pub auto_torrent_management: Option<bool>,
482
483 #[serde(rename = "sequentialDownload")]
486 #[serde(skip_serializing_if = "Option::is_none")]
487 pub sequential_download: Option<String>,
488
489 #[serde(rename = "firstLastPiecePrio")]
492 #[serde(skip_serializing_if = "Option::is_none")]
493 pub first_last_piece_priority: Option<String>,
494}
495
496#[cfg_attr(feature = "builder", derive(typed_builder::TypedBuilder))]
497#[derive(Debug, Clone, PartialEq, serde::Serialize)]
498#[serde(rename_all = "camelCase")]
499pub struct SetTorrentSharedLimitArg {
500 #[cfg_attr(feature = "builder", builder(setter(into)))]
501 pub hashes: Hashes,
502 #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
503 pub ratio_limit: Option<RatioLimit>,
504 #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
505 pub seeding_time_limit: Option<SeedingTimeLimit>,
506 #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
507 pub inactive_seeding_time_limit: Option<SeedingTimeLimit>,
508}
509
510#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
511pub enum RatioLimit {
512 Global,
513 NoLimit,
514 Limited(f64),
515}
516
517impl Serialize for RatioLimit {
518 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
519 where
520 S: serde::Serializer,
521 {
522 match self {
523 Self::Global => serializer.serialize_i64(-2),
524 Self::NoLimit => serializer.serialize_i64(-1),
525 Self::Limited(limit) => serializer.serialize_f64(*limit),
526 }
527 }
528}
529
530#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
531pub enum SeedingTimeLimit {
532 Global,
533 NoLimit,
534 Limited(u64),
536}
537
538impl Serialize for SeedingTimeLimit {
539 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
540 where
541 S: serde::Serializer,
542 {
543 match self {
544 Self::Global => serializer.serialize_i64(-2),
545 Self::NoLimit => serializer.serialize_i64(-1),
546 Self::Limited(limit) => serializer.serialize_u64(*limit),
547 }
548 }
549}
550
551#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
552pub(crate) struct HashArg<'a> {
553 hash: &'a str,
554}
555
556impl<'a> HashArg<'a> {
557 pub(crate) fn new(hash: &'a str) -> Self {
558 Self { hash }
559 }
560}
561
562#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
563pub(crate) struct HashesArg {
564 hashes: Hashes,
565}
566
567impl HashesArg {
568 pub(crate) fn new(hashes: impl Into<Hashes> + Send + Sync) -> Self {
569 Self {
570 hashes: hashes.into(),
571 }
572 }
573}