Spaces:
Running
Running
Upload 2 files
Browse files- src/main.rs +17 -13
src/main.rs
CHANGED
|
@@ -134,12 +134,12 @@ async fn api_client_for_arl(state: &AppState, arl: Option<String>) -> Arc<Mutex<
|
|
| 134 |
c
|
| 135 |
}
|
| 136 |
|
| 137 |
-
fn extract_media_url(media_json: &serde_json::Value) -> String {
|
| 138 |
let data = media_json.get("data").and_then(|v| v.as_array());
|
| 139 |
let Some(data_arr) = data else {
|
| 140 |
-
return String::new();
|
| 141 |
};
|
| 142 |
-
for data_item in data_arr {
|
| 143 |
let media_arr = data_item.get("media").and_then(|v| v.as_array());
|
| 144 |
let Some(media_arr) = media_arr else {
|
| 145 |
continue;
|
|
@@ -168,13 +168,13 @@ fn extract_media_url(media_json: &serde_json::Value) -> String {
|
|
| 168 |
for p in preferred {
|
| 169 |
let p = p.trim();
|
| 170 |
if !p.is_empty() {
|
| 171 |
-
return p.to_string();
|
| 172 |
}
|
| 173 |
}
|
| 174 |
}
|
| 175 |
}
|
| 176 |
}
|
| 177 |
-
String::new()
|
| 178 |
}
|
| 179 |
|
| 180 |
fn extract_media_format(media_json: &serde_json::Value) -> String {
|
|
@@ -242,7 +242,7 @@ async fn fetch_media_url(
|
|
| 242 |
client: &APIClient,
|
| 243 |
formats: &[Format],
|
| 244 |
track_tokens: Vec<&str>,
|
| 245 |
-
) -> Result<String, String> {
|
| 246 |
if client.license_token.is_empty() {
|
| 247 |
return Err("license_token empty".to_string());
|
| 248 |
}
|
|
@@ -263,9 +263,9 @@ async fn fetch_media_url(
|
|
| 263 |
format!("media_bad_json:{}:{}", status.as_u16(), snip)
|
| 264 |
})?;
|
| 265 |
|
| 266 |
-
let url = extract_media_url(&media_json);
|
| 267 |
if !url.is_empty() {
|
| 268 |
-
return Ok(url);
|
| 269 |
}
|
| 270 |
|
| 271 |
Err(format!(
|
|
@@ -312,19 +312,23 @@ async fn media_url_for_track(
|
|
| 312 |
.unwrap_or(0);
|
| 313 |
|
| 314 |
let mut tokens = Vec::new();
|
|
|
|
| 315 |
if !track_token.is_empty() {
|
| 316 |
tokens.push(track_token);
|
|
|
|
| 317 |
}
|
| 318 |
if !fallback_token.is_empty() {
|
| 319 |
tokens.push(fallback_token);
|
|
|
|
|
|
|
| 320 |
}
|
| 321 |
|
| 322 |
if tokens.is_empty() {
|
| 323 |
return Err("empty TRACK_TOKEN and FALLBACK".to_string());
|
| 324 |
}
|
| 325 |
|
| 326 |
-
let url = fetch_media_url(client, formats, tokens).await?;
|
| 327 |
-
let id_to_use =
|
| 328 |
Ok((url, id_to_use))
|
| 329 |
}
|
| 330 |
|
|
@@ -626,7 +630,7 @@ async fn stream_info(State(state): State<AppState>, Query(q): Query<StreamParams
|
|
| 626 |
// Upstream (опционально — если DZMEDIA_UPSTREAM задан)
|
| 627 |
if let Ok(text) = upstream_get_url_text(&formats, &vec![q.id]).await {
|
| 628 |
if let Ok(v) = serde_json::from_str::<serde_json::Value>(&text) {
|
| 629 |
-
url = extract_media_url(&v);
|
| 630 |
used = extract_media_format(&v);
|
| 631 |
}
|
| 632 |
}
|
|
@@ -833,14 +837,14 @@ async fn stream(
|
|
| 833 |
let upstream = match req.send().await {
|
| 834 |
Ok(r) => r,
|
| 835 |
Err(_) => {
|
| 836 |
-
return (StatusCode::
|
| 837 |
.into_response()
|
| 838 |
}
|
| 839 |
};
|
| 840 |
|
| 841 |
if !upstream.status().is_success() {
|
| 842 |
return (
|
| 843 |
-
StatusCode::
|
| 844 |
"Upstream status not OK".to_string(),
|
| 845 |
)
|
| 846 |
.into_response();
|
|
|
|
| 134 |
c
|
| 135 |
}
|
| 136 |
|
| 137 |
+
fn extract_media_url(media_json: &serde_json::Value) -> (String, usize) {
|
| 138 |
let data = media_json.get("data").and_then(|v| v.as_array());
|
| 139 |
let Some(data_arr) = data else {
|
| 140 |
+
return (String::new(), 0);
|
| 141 |
};
|
| 142 |
+
for (i, data_item) in data_arr.iter().enumerate() {
|
| 143 |
let media_arr = data_item.get("media").and_then(|v| v.as_array());
|
| 144 |
let Some(media_arr) = media_arr else {
|
| 145 |
continue;
|
|
|
|
| 168 |
for p in preferred {
|
| 169 |
let p = p.trim();
|
| 170 |
if !p.is_empty() {
|
| 171 |
+
return (p.to_string(), i);
|
| 172 |
}
|
| 173 |
}
|
| 174 |
}
|
| 175 |
}
|
| 176 |
}
|
| 177 |
+
(String::new(), 0)
|
| 178 |
}
|
| 179 |
|
| 180 |
fn extract_media_format(media_json: &serde_json::Value) -> String {
|
|
|
|
| 242 |
client: &APIClient,
|
| 243 |
formats: &[Format],
|
| 244 |
track_tokens: Vec<&str>,
|
| 245 |
+
) -> Result<(String, usize), String> {
|
| 246 |
if client.license_token.is_empty() {
|
| 247 |
return Err("license_token empty".to_string());
|
| 248 |
}
|
|
|
|
| 263 |
format!("media_bad_json:{}:{}", status.as_u16(), snip)
|
| 264 |
})?;
|
| 265 |
|
| 266 |
+
let (url, idx) = extract_media_url(&media_json);
|
| 267 |
if !url.is_empty() {
|
| 268 |
+
return Ok((url, idx));
|
| 269 |
}
|
| 270 |
|
| 271 |
Err(format!(
|
|
|
|
| 312 |
.unwrap_or(0);
|
| 313 |
|
| 314 |
let mut tokens = Vec::new();
|
| 315 |
+
let mut token_ids = Vec::new();
|
| 316 |
if !track_token.is_empty() {
|
| 317 |
tokens.push(track_token);
|
| 318 |
+
token_ids.push(id);
|
| 319 |
}
|
| 320 |
if !fallback_token.is_empty() {
|
| 321 |
tokens.push(fallback_token);
|
| 322 |
+
let fid = if fallback_id > 0 { fallback_id } else { id };
|
| 323 |
+
token_ids.push(fid);
|
| 324 |
}
|
| 325 |
|
| 326 |
if tokens.is_empty() {
|
| 327 |
return Err("empty TRACK_TOKEN and FALLBACK".to_string());
|
| 328 |
}
|
| 329 |
|
| 330 |
+
let (url, idx) = fetch_media_url(client, formats, tokens).await?;
|
| 331 |
+
let id_to_use = *token_ids.get(idx).unwrap_or(&id);
|
| 332 |
Ok((url, id_to_use))
|
| 333 |
}
|
| 334 |
|
|
|
|
| 630 |
// Upstream (опционально — если DZMEDIA_UPSTREAM задан)
|
| 631 |
if let Ok(text) = upstream_get_url_text(&formats, &vec![q.id]).await {
|
| 632 |
if let Ok(v) = serde_json::from_str::<serde_json::Value>(&text) {
|
| 633 |
+
url = extract_media_url(&v).0;
|
| 634 |
used = extract_media_format(&v);
|
| 635 |
}
|
| 636 |
}
|
|
|
|
| 837 |
let upstream = match req.send().await {
|
| 838 |
Ok(r) => r,
|
| 839 |
Err(_) => {
|
| 840 |
+
return (StatusCode::NOT_FOUND, "Upstream request failed".to_string())
|
| 841 |
.into_response()
|
| 842 |
}
|
| 843 |
};
|
| 844 |
|
| 845 |
if !upstream.status().is_success() {
|
| 846 |
return (
|
| 847 |
+
StatusCode::NOT_FOUND,
|
| 848 |
"Upstream status not OK".to_string(),
|
| 849 |
)
|
| 850 |
.into_response();
|