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 url::Url;
9
10impl EpicAPI {
11 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 let parsed_url = Url::parse(&url).map_err(|_| EpicAPIError::InvalidParams)?;
21 match self
22 .authorized_post_client(parsed_url)
23 .json(&serde_json::json!({
24 "item_id": asset_id,
25 "namespace": namespace,
26 "platform": platform.unwrap_or("Windows"),
27 }))
28 .send()
29 .await
30 {
31 Ok(response) => {
32 if response.status() == reqwest::StatusCode::OK {
33 let text = response.text().await.unwrap_or_default();
34 match serde_json::from_str::<
35 crate::api::types::fab_asset_manifest::FabAssetManifest,
36 >(&text)
37 {
38 Ok(manifest) => Ok(manifest.download_info),
39 Err(e) => {
40 error!("{:?}", e);
41 debug!("{}", text);
42 Err(EpicAPIError::DeserializationError(format!("{}", e)))
43 }
44 }
45 } else if response.status() == reqwest::StatusCode::FORBIDDEN {
46 Err(EpicAPIError::FabTimeout)
47 } else {
48 debug!("{:?}", response.headers());
49 let status = response.status();
50 let body = response.text().await.unwrap_or_default();
51 warn!("{} result: {}", status, body);
52 Err(EpicAPIError::HttpError { status, body })
53 }
54 }
55 Err(e) => {
56 error!("{:?}", e);
57 Err(EpicAPIError::NetworkError(e))
58 }
59 }
60 }
61
62 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::InvalidParams)
72 }
73 Some(point) => {
74 if point.signature_expiration < time::OffsetDateTime::now_utc() {
75 error!("Expired signature");
76 Err(EpicAPIError::InvalidParams)
77 } else {
78 let data = self.get_bytes(&point.manifest_url).await?;
79 match DownloadManifest::parse(data) {
80 None => {
81 error!("Unable to parse the Download Manifest");
82 Err(EpicAPIError::DeserializationError(
83 "Unable to parse the Download Manifest".to_string(),
84 ))
85 }
86 Some(man) => Ok(man),
87 }
88 }
89 }
90 }
91 }
92
93 pub async fn fab_library_items(
95 &mut self,
96 account_id: String,
97 ) -> Result<FabLibrary, EpicAPIError> {
98 let mut library = FabLibrary::default();
99
100 loop {
101 let url = match &library.cursors.next {
102 None => {
103 format!(
104 "https://www.fab.com/e/accounts/{}/ue/library?count=100",
105 account_id
106 )
107 }
108 Some(c) => {
109 format!(
110 "https://www.fab.com/e/accounts/{}/ue/library?cursor={}&count=100",
111 account_id, c
112 )
113 }
114 };
115
116 match self.authorized_get_json::<FabLibrary>(&url).await {
117 Ok(mut api_library) => {
118 library.cursors.next = api_library.cursors.next;
119 library.results.append(api_library.results.borrow_mut());
120 }
121 Err(e) => {
122 error!("{:?}", e);
123 library.cursors.next = None;
124 }
125 }
126 if library.cursors.next.is_none() {
127 break;
128 }
129 }
130
131 Ok(library)
132 }
133
134 pub async fn fab_file_download_info(
136 &self,
137 listing_id: &str,
138 format_id: &str,
139 file_id: &str,
140 ) -> Result<DownloadInfo, EpicAPIError> {
141 let url = format!(
142 "https://www.fab.com/p/egl/listings/{}/asset-formats/{}/files/{}/download-info",
143 listing_id, format_id, file_id
144 );
145 self.authorized_get_json(&url).await
146 }
147}