1use std::fmt::{Debug, Display};
2
3use serde::Serialize;
4use serde_with::{SerializeDisplay, skip_serializing_none};
5
6use crate::{client::Url, model::Sep};
7
8#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub enum TorrentFilter {
11 All,
12 Downloading,
13 Completed,
14 Paused,
15 Active,
16 Inactive,
17 Resumed,
18 Stalled,
19 StalledUploading,
20 StalledDownloading,
21 Errored,
22}
23
24#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
25pub struct Torrent {
26 pub added_on: Option<i64>,
28 pub amount_left: Option<i64>,
30 pub auto_tmm: Option<bool>,
32 pub availability: Option<f64>,
34 pub category: Option<String>,
36 pub comment: Option<String>,
38 pub completed: Option<i64>,
40 pub completion_on: Option<i64>,
42 pub content_path: Option<String>,
45 pub dl_limit: Option<i64>,
47 pub dlspeed: Option<i64>,
49 pub download_path: Option<String>,
51 pub downloaded: Option<i64>,
53 pub downloaded_session: Option<i64>,
55 pub eta: Option<i64>,
57 pub f_l_piece_prio: Option<bool>,
59 pub force_start: Option<bool>,
61 pub has_metadata: Option<bool>,
63 pub hash: Option<String>,
65 pub inactive_seeding_time_limit: Option<i64>,
67 pub infohash_v1: Option<String>,
69 pub infohash_v2: Option<String>,
71 pub last_activity: Option<i64>,
73 pub magnet_uri: Option<String>,
75 pub max_inactive_seeding_time: Option<i64>,
77 pub max_ratio: Option<f64>,
79 pub max_seeding_time: Option<i64>,
81 pub name: Option<String>,
83 pub num_complete: Option<i64>,
85 pub num_incomplete: Option<i64>,
87 pub num_leechs: Option<i64>,
89 pub num_seeds: Option<i64>,
91 pub popularity: Option<f64>,
93 pub priority: Option<i64>,
96 pub private: Option<bool>,
98 pub progress: Option<f64>,
100 pub ratio: Option<f64>,
102 pub ratio_limit: Option<f64>,
103 pub reannounce: Option<i64>,
105 pub root_path: Option<String>,
107 pub save_path: Option<String>,
109 pub seeding_time: Option<i64>,
111 pub seeding_time_limit: Option<i64>,
117 pub seen_complete: Option<i64>,
119 pub seq_dl: Option<bool>,
121 pub size: Option<i64>,
123 pub state: Option<State>,
125 pub super_seeding: Option<bool>,
127 pub tags: Option<String>,
129 pub time_active: Option<i64>,
131 pub total_size: Option<i64>,
134 pub tracker: Option<String>,
137 pub up_limit: Option<i64>,
139 pub uploaded: Option<i64>,
141 pub uploaded_session: Option<i64>,
143 pub upspeed: Option<i64>,
145}
146
147#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
148pub enum State {
149 #[serde(rename = "error")]
151 Error,
152 #[serde(rename = "missingFiles")]
154 MissingFiles,
155 #[serde(rename = "uploading")]
157 Uploading,
158 #[serde(rename = "pausedUP", alias = "stoppedUP")]
161 PausedUP,
162 #[serde(rename = "queuedUP")]
164 QueuedUP,
165 #[serde(rename = "stalledUP")]
167 StalledUP,
168 #[serde(rename = "checkingUP")]
170 CheckingUP,
171 #[serde(rename = "forcedUP")]
173 ForcedUP,
174 #[serde(rename = "allocating")]
176 Allocating,
177 #[serde(rename = "downloading")]
179 Downloading,
180 #[serde(rename = "metaDL")]
182 MetaDL,
183 #[serde(rename = "pausedDL", alias = "stoppedDL")]
186 PausedDL,
187 #[serde(rename = "queuedDL")]
189 QueuedDL,
190 #[serde(rename = "stalledDL")]
192 StalledDL,
193 #[serde(rename = "checkingDL")]
195 CheckingDL,
196 #[serde(rename = "forcedDL")]
198 ForcedDL,
199 #[serde(rename = "checkingResumeData")]
201 CheckingResumeData,
202 #[serde(rename = "moving")]
204 Moving,
205 #[serde(rename = "unknown")]
207 Unknown,
208}
209
210#[derive(Debug, Clone, PartialEq, serde::Deserialize)]
211pub struct TorrentProperty {
212 pub save_path: Option<String>,
214 pub creation_date: Option<i64>,
216 pub piece_size: Option<i64>,
218 pub comment: Option<String>,
220 pub total_wasted: Option<i64>,
222 pub total_uploaded: Option<i64>,
224 pub total_uploaded_session: Option<i64>,
226 pub total_downloaded: Option<i64>,
228 pub total_downloaded_session: Option<i64>,
230 pub up_limit: Option<i64>,
232 pub dl_limit: Option<i64>,
234 pub time_elapsed: Option<i64>,
236 pub seeding_time: Option<i64>,
238 pub nb_connections: Option<i64>,
240 pub nb_connections_limit: Option<i64>,
242 pub share_ratio: Option<f64>,
244 pub addition_date: Option<i64>,
246 pub completion_date: Option<i64>,
248 pub created_by: Option<String>,
250 pub dl_speed_avg: Option<i64>,
252 pub dl_speed: Option<i64>,
254 pub eta: Option<i64>,
256 pub last_seen: Option<i64>,
258 pub peers: Option<i64>,
260 pub peers_total: Option<i64>,
262 pub pieces_have: Option<i64>,
264 pub pieces_num: Option<i64>,
266 pub reannounce: Option<i64>,
268 pub seeds: Option<i64>,
270 pub seeds_total: Option<i64>,
272 pub total_size: Option<i64>,
274 pub up_speed_avg: Option<i64>,
276 pub up_speed: Option<i64>,
278}
279
280#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
281pub struct WebSeed {
282 pub url: Url,
284}
285
286#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
287pub struct TorrentContent {
288 pub index: u64,
290 pub name: String,
292 pub size: u64,
294 pub progress: f64,
296 pub priority: Priority,
298 pub is_seed: Option<bool>,
300 #[serde(default)]
303 pub piece_range: Vec<i64>,
304 #[serde(default)]
306 pub availability: f64,
307}
308
309#[derive(
310 Debug,
311 Clone,
312 Copy,
313 PartialEq,
314 Eq,
315 PartialOrd,
316 Ord,
317 serde_repr::Serialize_repr,
318 serde_repr::Deserialize_repr,
319)]
320#[repr(u8)]
321pub enum Priority {
322 DoNotDownload = 0,
324 Normal = 1,
326 Mixed = 4,
328 High = 6,
330 Maximal = 7,
332}
333
334#[derive(
335 Debug,
336 Clone,
337 Copy,
338 PartialEq,
339 Eq,
340 PartialOrd,
341 Ord,
342 serde_repr::Serialize_repr,
343 serde_repr::Deserialize_repr,
344)]
345#[repr(u8)]
346pub enum PieceState {
347 NotDownloaded = 0,
349 Downloading = 1,
351 Downloaded = 2,
353}
354
355#[derive(Debug, Clone, PartialEq, Eq, SerializeDisplay)]
357pub enum Hashes {
358 Hashes(Sep<String, '|'>),
360 All,
362}
363
364impl<V: Into<Vec<String>>> From<V> for Hashes {
365 fn from(hashes: V) -> Self {
366 Hashes::Hashes(Sep::from(hashes))
367 }
368}
369
370impl Display for Hashes {
371 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
372 match self {
373 Hashes::Hashes(hashes) => write!(f, "{}", hashes),
374 Hashes::All => write!(f, "all"),
375 }
376 }
377}
378
379#[cfg_attr(feature = "builder", derive(typed_builder::TypedBuilder))]
380#[cfg_attr(
381 feature = "builder",
382 builder(field_defaults(default, setter(strip_option)))
383)]
384#[derive(Debug, Clone, PartialEq, Default, serde::Serialize)]
385#[skip_serializing_none]
386pub struct GetTorrentListArg {
387 pub filter: Option<TorrentFilter>,
392 pub category: Option<String>,
394 pub tag: Option<String>,
398 pub sort: Option<String>,
401 pub reverse: Option<bool>,
403 pub limit: Option<u64>,
405 pub offset: Option<i64>,
407 pub hashes: Option<String>,
409}
410
411#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
412#[serde(untagged)]
413pub enum TorrentSource {
414 Urls { urls: Sep<Url, '\n'> },
416 TorrentFiles { torrents: Vec<TorrentFile> },
418}
419#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
420pub struct TorrentFile {
422 pub filename: String,
423 pub data: Vec<u8>,
424}
425impl Default for TorrentSource {
426 fn default() -> Self {
427 TorrentSource::Urls {
428 urls: Sep::from(vec![]),
429 }
430 }
431}
432fn is_torrent_files(source: &TorrentSource) -> bool {
433 matches!(source, TorrentSource::TorrentFiles { .. })
434}
435#[cfg_attr(feature = "builder", derive(typed_builder::TypedBuilder))]
436#[cfg_attr(
437 feature = "builder",
438 builder(field_defaults(default, setter(strip_option)))
439)]
440#[derive(Debug, Clone, PartialEq, serde::Serialize, Default)]
441#[skip_serializing_none]
442pub struct AddTorrentArg {
443 #[serde(flatten)]
444 #[cfg_attr(feature = "builder", builder(!default, setter(!strip_option)))]
445 #[serde(skip_serializing_if = "is_torrent_files")]
446 pub source: TorrentSource,
447 #[serde(skip_serializing_if = "Option::is_none")]
448 pub savepath: Option<String>,
450 #[serde(skip_serializing_if = "Option::is_none")]
452 pub cookie: Option<String>,
453 #[serde(skip_serializing_if = "Option::is_none")]
455 pub category: Option<String>,
456
457 #[serde(skip_serializing_if = "Option::is_none")]
459 pub tags: Option<String>,
460
461 #[serde(skip_serializing_if = "Option::is_none")]
463 pub skip_checking: Option<String>,
464
465 #[serde(skip_serializing_if = "Option::is_none")]
468 pub paused: Option<String>,
469
470 #[serde(skip_serializing_if = "Option::is_none")]
473 pub root_folder: Option<String>,
474
475 #[serde(skip_serializing_if = "Option::is_none")]
477 pub rename: Option<String>,
478
479 #[serde(rename = "upLimit")]
481 #[serde(skip_serializing_if = "Option::is_none")]
482 pub up_limit: Option<i64>,
483
484 #[serde(rename = "dlLimit")]
486 #[serde(skip_serializing_if = "Option::is_none")]
487 pub download_limit: Option<i64>,
488
489 #[serde(rename = "ratioLimit")]
491 #[serde(skip_serializing_if = "Option::is_none")]
492 pub ratio_limit: Option<f64>,
493
494 #[serde(rename = "seedingTimeLimit")]
496 #[serde(skip_serializing_if = "Option::is_none")]
497 pub seeding_time_limit: Option<i64>,
498
499 #[serde(rename = "autoTMM")]
501 #[serde(skip_serializing_if = "Option::is_none")]
502 pub auto_torrent_management: Option<bool>,
503
504 #[serde(rename = "sequentialDownload")]
507 #[serde(skip_serializing_if = "Option::is_none")]
508 pub sequential_download: Option<String>,
509
510 #[serde(rename = "firstLastPiecePrio")]
513 #[serde(skip_serializing_if = "Option::is_none")]
514 pub first_last_piece_priority: Option<String>,
515}
516
517#[cfg_attr(feature = "builder", derive(typed_builder::TypedBuilder))]
518#[derive(Debug, Clone, PartialEq, serde::Serialize)]
519#[serde(rename_all = "camelCase")]
520pub struct SetTorrentSharedLimitArg {
521 #[cfg_attr(feature = "builder", builder(setter(into)))]
522 pub hashes: Hashes,
523 #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
524 pub ratio_limit: Option<RatioLimit>,
525 #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
526 pub seeding_time_limit: Option<SeedingTimeLimit>,
527 #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
528 pub inactive_seeding_time_limit: Option<SeedingTimeLimit>,
529}
530
531#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
532pub enum RatioLimit {
533 Global,
534 NoLimit,
535 Limited(f64),
536}
537
538impl Serialize for RatioLimit {
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_f64(*limit),
547 }
548 }
549}
550
551#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
552pub enum SeedingTimeLimit {
553 Global,
554 NoLimit,
555 Limited(u64),
557}
558
559impl Serialize for SeedingTimeLimit {
560 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
561 where
562 S: serde::Serializer,
563 {
564 match self {
565 Self::Global => serializer.serialize_i64(-2),
566 Self::NoLimit => serializer.serialize_i64(-1),
567 Self::Limited(limit) => serializer.serialize_u64(*limit),
568 }
569 }
570}
571
572#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
573pub(crate) struct HashArg<'a> {
574 hash: &'a str,
575}
576
577impl<'a> HashArg<'a> {
578 pub(crate) fn new(hash: &'a str) -> Self {
579 Self { hash }
580 }
581}
582
583#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
584pub(crate) struct HashesArg {
585 hashes: Hashes,
586}
587
588impl HashesArg {
589 pub(crate) fn new(hashes: impl Into<Hashes> + Send + Sync) -> Self {
590 Self {
591 hashes: hashes.into(),
592 }
593 }
594}