Spaces:
Running
Running
Upload soundcloud.rs
Browse files- src/soundcloud.rs +28 -20
src/soundcloud.rs
CHANGED
|
@@ -46,19 +46,21 @@ pub struct User {
|
|
| 46 |
|
| 47 |
/// /soundcloud/search - поиск треков на SoundCloud через yt-dlp
|
| 48 |
pub async fn search(Query(params): Query<SearchParams>) -> impl IntoResponse {
|
| 49 |
-
tracing::info!("SoundCloud search: {}", params.q);
|
| 50 |
|
| 51 |
// Используем yt-dlp для поиска на SoundCloud
|
|
|
|
|
|
|
|
|
|
| 52 |
let output = match Command::new("yt-dlp")
|
| 53 |
.args([
|
| 54 |
"--quiet",
|
| 55 |
"--no-warnings",
|
| 56 |
"--print", "%(id)s|||%(title)s|||%(uploader)s|||%(duration)s|||%(webpage_url)s",
|
| 57 |
-
|
| 58 |
-
&format!("scsearch{}:{}", params.limit, params.q),
|
| 59 |
])
|
| 60 |
.stdout(Stdio::piped())
|
| 61 |
-
.stderr(Stdio::
|
| 62 |
.output()
|
| 63 |
.await
|
| 64 |
{
|
|
@@ -67,42 +69,48 @@ pub async fn search(Query(params): Query<SearchParams>) -> impl IntoResponse {
|
|
| 67 |
tracing::error!("yt-dlp search failed: {}", e);
|
| 68 |
return (
|
| 69 |
StatusCode::INTERNAL_SERVER_ERROR,
|
| 70 |
-
Json(json!({ "error": "Search failed" })),
|
| 71 |
).into_response();
|
| 72 |
}
|
| 73 |
};
|
| 74 |
|
| 75 |
if !output.status.success() {
|
| 76 |
-
|
|
|
|
| 77 |
return (
|
| 78 |
StatusCode::INTERNAL_SERVER_ERROR,
|
| 79 |
-
Json(json!({ "error": "Search failed" })),
|
| 80 |
).into_response();
|
| 81 |
}
|
| 82 |
|
| 83 |
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
|
|
|
|
|
| 84 |
let mut tracks = Vec::new();
|
| 85 |
|
| 86 |
-
for line in stdout.lines() {
|
| 87 |
let parts: Vec<&str> = line.split("|||").collect();
|
| 88 |
if parts.len() >= 5 {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
if let Ok(duration) = parts[3].parse::<u64>() {
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
});
|
| 101 |
-
}
|
| 102 |
}
|
| 103 |
}
|
| 104 |
}
|
| 105 |
|
|
|
|
| 106 |
(StatusCode::OK, Json(json!({ "tracks": tracks }))).into_response()
|
| 107 |
}
|
| 108 |
|
|
|
|
| 46 |
|
| 47 |
/// /soundcloud/search - поиск треков на SoundCloud через yt-dlp
|
| 48 |
pub async fn search(Query(params): Query<SearchParams>) -> impl IntoResponse {
|
| 49 |
+
tracing::info!("SoundCloud search: q='{}' limit={}", params.q, params.limit);
|
| 50 |
|
| 51 |
// Используем yt-dlp для поиска на SoundCloud
|
| 52 |
+
// Формат: scsearch5:query означает "искать на SoundCloud 5 результатов по запросу query"
|
| 53 |
+
let search_query = format!("scsearch{}:{}", params.limit, params.q);
|
| 54 |
+
|
| 55 |
let output = match Command::new("yt-dlp")
|
| 56 |
.args([
|
| 57 |
"--quiet",
|
| 58 |
"--no-warnings",
|
| 59 |
"--print", "%(id)s|||%(title)s|||%(uploader)s|||%(duration)s|||%(webpage_url)s",
|
| 60 |
+
&search_query,
|
|
|
|
| 61 |
])
|
| 62 |
.stdout(Stdio::piped())
|
| 63 |
+
.stderr(Stdio::piped())
|
| 64 |
.output()
|
| 65 |
.await
|
| 66 |
{
|
|
|
|
| 69 |
tracing::error!("yt-dlp search failed: {}", e);
|
| 70 |
return (
|
| 71 |
StatusCode::INTERNAL_SERVER_ERROR,
|
| 72 |
+
Json(json!({ "error": format!("Search failed: {}", e) })),
|
| 73 |
).into_response();
|
| 74 |
}
|
| 75 |
};
|
| 76 |
|
| 77 |
if !output.status.success() {
|
| 78 |
+
let stderr = String::from_utf8_lossy(&output.stderr);
|
| 79 |
+
tracing::error!("yt-dlp search exit: {} stderr: {}", output.status, stderr);
|
| 80 |
return (
|
| 81 |
StatusCode::INTERNAL_SERVER_ERROR,
|
| 82 |
+
Json(json!({ "error": format!("Search failed: exit {}", output.status) })),
|
| 83 |
).into_response();
|
| 84 |
}
|
| 85 |
|
| 86 |
let stdout = String::from_utf8_lossy(&output.stdout);
|
| 87 |
+
tracing::info!("yt-dlp output: {} lines", stdout.lines().count());
|
| 88 |
+
|
| 89 |
let mut tracks = Vec::new();
|
| 90 |
|
| 91 |
+
for (idx, line) in stdout.lines().enumerate() {
|
| 92 |
let parts: Vec<&str> = line.split("|||").collect();
|
| 93 |
if parts.len() >= 5 {
|
| 94 |
+
// ID может быть числом или строкой, используем строку как permalink
|
| 95 |
+
let id_str = parts[0];
|
| 96 |
+
let id_num = id_str.parse::<u64>().unwrap_or(idx as u64);
|
| 97 |
+
|
| 98 |
if let Ok(duration) = parts[3].parse::<u64>() {
|
| 99 |
+
tracks.push(Track {
|
| 100 |
+
id: id_num,
|
| 101 |
+
title: parts[1].to_string(),
|
| 102 |
+
permalink: id_str.to_string(),
|
| 103 |
+
permalink_url: parts[4].to_string(),
|
| 104 |
+
duration: duration * 1000, // в миллисекундах для совместимости
|
| 105 |
+
user: User {
|
| 106 |
+
username: parts[2].to_string(),
|
| 107 |
+
},
|
| 108 |
+
});
|
|
|
|
|
|
|
| 109 |
}
|
| 110 |
}
|
| 111 |
}
|
| 112 |
|
| 113 |
+
tracing::info!("Found {} tracks", tracks.len());
|
| 114 |
(StatusCode::OK, Json(json!({ "tracks": tracks }))).into_response()
|
| 115 |
}
|
| 116 |
|