Fall back to SoundCloud when YouTube hides search results
YouTube applies restricted-mode filtering to programmatic search: the same query that returns a clip in a signed-in browser comes back as an empty list over the API, with exit code 0 and no message at all, and cookies do not change it. Verified locally — "gudium идол" returns three results, "gudium аудиопорно" returns none, and so does the plain youtube.com/results page for the same words. An empty YouTube search now retries on SoundCloud, and when both come up empty the message says the query may have been filtered and suggests pasting a link, instead of implying the track does not exist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3e53f34374
commit
f84489e27e
+24
-4
@@ -74,9 +74,9 @@ export async function resolveQuery(
|
||||
if (localHits.length > 0 && localHits[0]) return { tracks: [localHits[0]], playlist: null };
|
||||
}
|
||||
|
||||
const tracks = await ytdlp.search(text, forced ?? "youtube", 1, requestedBy);
|
||||
if (tracks.length === 0) throw new UserFacingError("Ничего не найдено");
|
||||
return { tracks, playlist: null };
|
||||
const tracks = await searchWithFallback(text, forced, 1, requestedBy);
|
||||
if (tracks.length === 0) throw new UserFacingError(NOTHING_FOUND);
|
||||
return { tracks: tracks.slice(0, 1), playlist: null };
|
||||
}
|
||||
|
||||
/** Multi-result search used by the `search` command and the web panel. */
|
||||
@@ -97,7 +97,7 @@ export async function searchTracks(
|
||||
if (forced === "local") return local.search(text, limit, requestedBy);
|
||||
|
||||
const [remote, localHits] = await Promise.all([
|
||||
ytdlp.search(text, forced ?? "youtube", limit, requestedBy),
|
||||
searchWithFallback(text, forced, limit, requestedBy),
|
||||
local.isEnabled() && forced === null
|
||||
? local.search(text, 3, requestedBy).catch(() => [])
|
||||
: Promise.resolve([]),
|
||||
@@ -105,6 +105,26 @@ export async function searchTracks(
|
||||
return [...localHits, ...remote].slice(0, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* YouTube silently returns nothing for queries its restricted mode dislikes —
|
||||
* same query, same words, results in a browser but an empty list over the API.
|
||||
* Falling back to SoundCloud rescues a good share of those.
|
||||
*/
|
||||
async function searchWithFallback(
|
||||
text: string,
|
||||
forced: "youtube" | "soundcloud" | "local" | null,
|
||||
limit: number,
|
||||
requestedBy: Requester,
|
||||
): Promise<Track[]> {
|
||||
const primary = forced === "soundcloud" ? "soundcloud" : "youtube";
|
||||
const tracks = await ytdlp.search(text, primary, limit, requestedBy);
|
||||
if (tracks.length > 0 || forced !== null) return tracks;
|
||||
return ytdlp.search(text, "soundcloud", limit, requestedBy).catch(() => []);
|
||||
}
|
||||
|
||||
export const NOTHING_FOUND =
|
||||
"Ничего не найдено. YouTube иногда прячет результаты от ботов (например, из-за ограниченного режима) — попробуйте другие слова или вставьте ссылку на трек.";
|
||||
|
||||
export interface PlaybackInput {
|
||||
/** Either a file path / URL for ffmpeg, or a piped stream. */
|
||||
input: string | Readable;
|
||||
|
||||
Reference in New Issue
Block a user