1use crate::api::error::EpicAPIError;
2use crate::api::types::download_manifest::DownloadManifest;
3use crate::api::types::fab_asset_manifest::DownloadInfo;
4use crate::api::types::fab_library::FabLibrary;
5use crate::api::EpicAPI;
6use log::{debug, error, warn};
7use std::borrow::BorrowMut;
8use std::str::FromStr;
9use url::Url;
10
11impl EpicAPI {
12 pub async fn fab_asset_manifest(
13 &self,
14 artifact_id: &str,
15 namespace: &str,
16 asset_id: &str,
17 platform: Option<&str>,
18 ) -> Result<Vec<DownloadInfo>, EpicAPIError> {
19 let url = format!("https://www.fab.com/e/artifacts/{}/manifest", artifact_id);
20 match self
21 .authorized_post_client(Url::parse(&url).unwrap())
22 .json(&serde_json::json!({
23 "item_id": asset_id,
24 "namespace": namespace,
25 "platform": platform.unwrap_or("Windows"),
26 }))
27 .send()
28 .await
29 {
30 Ok(response) => {
31 if response.status() == reqwest::StatusCode::OK {
32 let text = response.text().await.unwrap();
33 match serde_json::from_str::<
34 crate::api::types::fab_asset_manifest::FabAssetManifest,
35 >(&text)
36 {
37 Ok(manifest) => Ok(manifest.download_info),
38 Err(e) => {
39 error!("{:?}", e);
40 debug!("{}", text);
41 Err(EpicAPIError::Unknown)
42 }
43 }
44 } else if response.status() == reqwest::StatusCode::FORBIDDEN {
45 Err(EpicAPIError::FabTimeout)
46 } else {
47 debug!("{:?}", response.headers());
48 warn!(
49 "{} result: {}",
50 response.status(),
51 response.text().await.unwrap()
52 );
53 Err(EpicAPIError::Unknown)
54 }
55 }
56 Err(e) => {
57 error!("{:?}", e);
58 Err(EpicAPIError::Unknown)
59 }
60 }
61 }
62
63 pub async fn fab_download_manifest(
64 &self,
65 download_info: DownloadInfo,
66 distribution_point_url: &str,
67 ) -> Result<DownloadManifest, EpicAPIError> {
68 match download_info.get_distribution_point_by_base_url(distribution_point_url) {
69 None => {
70 error!("Distribution point not found");
71 Err(EpicAPIError::Unknown)
72 }
73 Some(point) => {
74 if point.signature_expiration < time::OffsetDateTime::now_utc() {
75 error!("Expired signature");
76 Err(EpicAPIError::Unknown)
77 } else {
78 let client = EpicAPI::build_client().build().unwrap();
79 match client
80 .get(Url::from_str(&point.manifest_url).unwrap())
81 .send()
82 .await
83 {
84 Ok(response) => {
85 if response.status() == reqwest::StatusCode::OK {
86 match response.bytes().await {
87 Ok(data) => match DownloadManifest::parse(data.to_vec()) {
88 None => {
89 error!("Unable to parse the Download Manifest");
90 Err(EpicAPIError::Unknown)
91 }
92 Some(man) => Ok(man),
93 },
94 Err(_) => Err(EpicAPIError::Unknown),
95 }
96 } else {
97 warn!(
98 "{} result: {}",
99 response.status(),
100 response.text().await.unwrap()
101 );
102 Err(EpicAPIError::Unknown)
103 }
104 }
105 Err(_) => Err(EpicAPIError::Unknown),
106 }
107 }
108 }
109 }
110 }
111
112 pub async fn fab_library_items(
113 &mut self,
114 account_id: String,
115 ) -> Result<FabLibrary, EpicAPIError> {
116 let mut library = FabLibrary::default();
117
118 loop {
119 let url = match &library.cursors.next {
120 None => {
121 format!(
122 "https://www.fab.com/e/accounts/{}/ue/library?count=100",
123 account_id
124 )
125 }
126 Some(c) => {
127 format!(
128 "https://www.fab.com/e/accounts/{}/ue/library?cursor={}&count=100",
129 account_id, c
130 )
131 }
132 };
133
134 match self
135 .authorized_get_client(Url::parse(&url).unwrap())
136 .send()
137 .await
138 {
139 Ok(response) => {
140 if response.status() == reqwest::StatusCode::OK {
141 let text = response.text().await.unwrap();
142 match serde_json::from_str::<FabLibrary>(&text) {
143 Ok(mut api_library) => {
144 library.cursors.next = api_library.cursors.next;
145 library.results.append(api_library.results.borrow_mut());
146 }
147 Err(e) => {
148 error!("{:?}", e);
149 debug!("{}", text);
150 library.cursors.next = None;
151 }
152 }
153 } else {
154 debug!("{:?}", response.headers());
155 warn!(
156 "{} result: {}",
157 response.status(),
158 response.text().await.unwrap()
159 );
160 }
161 }
162 Err(e) => {
163 error!("{:?}", e);
164 library.cursors.next = None;
165 }
166 }
167 if library.cursors.next.is_none() {
168 break;
169 }
170 }
171
172 Ok(library)
173 }
174}