Skip to content

Commit e571bfb

Browse files
committed
feat: added support for 'did you mean' on play and video to retry using that suggested query instead to get any results before failing and also this refractor from creating new MessageMedia from file buffer it now uses the MessageMedia fromFilePath
1 parent d81419b commit e571bfb

2 files changed

Lines changed: 56 additions & 43 deletions

File tree

src/commands/play.ts

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ import { Message } from "../../types/message";
33
import fs from "fs";
44
import path from "path";
55
import { exec } from "child_process";
6-
// fallback import for compatibility
7-
// youtubei.js currently does not support ESM directly
8-
const { Innertube, UniversalCache, Utils } = require("youtubei.js");
6+
import { Innertube, UniversalCache, Utils } from "youtubei.js";
97
import util from "util";
8+
import log from "../components/utils/log";
109

1110
const execPromise = util.promisify(exec);
1211

@@ -19,6 +18,24 @@ export const info = {
1918
cooldown: 5000,
2019
};
2120

21+
async function search(yt: Innertube, query: string) {
22+
log.info("Play", `Searching for ${query}`);
23+
const results = await yt.music.search(query, { type: "song" });
24+
const firstShelf = results.contents?.[0];
25+
const audio: any =
26+
firstShelf && "contents" in firstShelf
27+
? firstShelf.contents?.[0]
28+
: undefined;
29+
30+
if (audio?.id) return audio;
31+
32+
const didYouMean: any = results.contents?.[0].contents?.[0];
33+
if (!didYouMean) return undefined;
34+
35+
log.info("Play", `Did you mean ${didYouMean.corrected_query.text}`);
36+
return await search(yt, didYouMean.corrected_query.text);
37+
}
38+
2239
export default async function play(msg: Message) {
2340
const query = msg.body.replace(/^play\b\s*/i, "").trim();
2441
if (!query) {
@@ -31,18 +48,17 @@ export default async function play(msg: Message) {
3148
generate_session_locally: true,
3249
player_id: "0004de42",
3350
});
34-
const search = await yt.music.search(query, { type: "song" });
3551

36-
const audio = search.contents[0].contents[0];
37-
if (!audio.id) {
38-
await msg.reply("Unable to find resources for the given query.");
52+
const audio: any = await search(yt, query);
53+
if (!audio) {
54+
await msg.reply(`No youtube music found for "${query}".`);
3955
return;
4056
}
4157

42-
// Only allow audios shorter than 10 minutes (600 seconds)
43-
if (audio.length && audio.length.seconds > 600) {
58+
// Only allow audios shorter than 20 minutes (1200 seconds)
59+
if (audio.duration && audio.duration.seconds > 1200) {
4460
await msg.reply(
45-
"Sorry, it seems like im not able to get any search results.",
61+
"Opps, the music is quite long we can only process max of 20 minutes.",
4662
);
4763
return;
4864
}
@@ -57,7 +73,7 @@ export default async function play(msg: Message) {
5773

5874
if (!stream) {
5975
await Promise.all([
60-
msg.reply("Failed to download the audio stream."),
76+
msg.reply("Failed to download the audio."),
6177
msg.react(""),
6278
]);
6379
return;
@@ -88,13 +104,7 @@ export default async function play(msg: Message) {
88104
`ffmpeg -y -i "${tempPath}" -vn -ar 44100 -ac 2 -b:a 192k "${tempPath}.mp3"`,
89105
);
90106

91-
const audioBuffer = fs.readFileSync(`${tempPath}.mp3`);
92-
const media = new MessageMedia(
93-
"audio/mpeg",
94-
audioBuffer.toString("base64"),
95-
`${audio.title}.mp3`,
96-
);
97-
107+
const media = MessageMedia.fromFilePath(`${tempPath}.mp3`);
98108
await msg.reply(media, undefined, {
99109
caption: audio.title,
100110
});

src/commands/video.ts

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@ import { MessageMedia } from "whatsapp-web.js";
22
import { Message } from "../../types/message";
33
import fs from "fs";
44
import path from "path";
5-
// fallback import for compatibility
6-
// youtubei.js currently does not support ESM directly
7-
const { Innertube, UniversalCache, Utils } = require("youtubei.js");
8-
import { exec } from "child_process";
9-
import util from "util";
10-
11-
const execPromise = util.promisify(exec);
5+
import { Innertube, UniversalCache, Utils } from "youtubei.js";
6+
import log from "../components/utils/log";
127

138
export const info = {
149
command: "video",
@@ -19,6 +14,20 @@ export const info = {
1914
cooldown: 5000,
2015
};
2116

17+
async function search(yt: Innertube, query: string) {
18+
log.info("Video", `Searching for ${query}`);
19+
const results = await yt.search(query, { type: "video" });
20+
const video: any = results.results?.[0];
21+
22+
if (video?.video_id) return video;
23+
24+
const didYouMean: any = results.results?.[0];
25+
if (!didYouMean) return undefined;
26+
27+
log.info("Video", `Did you mean ${didYouMean.corrected_query.text}`);
28+
return await search(yt, didYouMean.corrected_query.text);
29+
}
30+
2231
export default async function (msg: Message) {
2332
const query = msg.body.replace(/^video\b\s*/i, "").trim();
2433
if (!query) {
@@ -31,20 +40,17 @@ export default async function (msg: Message) {
3140
generate_session_locally: true,
3241
player_id: "0004de42",
3342
});
34-
const search = await yt.search(query, { type: "video" });
3543

36-
const video = search.results[0];
37-
if (!video.video_id) {
38-
await msg.reply(
39-
"Sorry, it seems like im not able to get any search results.",
40-
);
44+
const video: any = await search(yt, query);
45+
if (!video) {
46+
await msg.reply(`No youtube video found for "${query}".`);
4147
return;
4248
}
4349

44-
// Only allow videos shorter than 10 minutes (600 seconds)
45-
if (video.length && video.length.seconds > 600) {
50+
// Only allow audios shorter than 20 minutes (1200 seconds)
51+
if (video.duration && video.duration.seconds > 1200) {
4652
await msg.reply(
47-
"Sorry, only videos shorter than 10 minutes can be downloaded.",
53+
"Opps, the video is quite long we can only process max of 20 minutes.",
4854
);
4955
return;
5056
}
@@ -58,7 +64,10 @@ export default async function (msg: Message) {
5864
});
5965

6066
if (!stream) {
61-
await msg.reply("Failed to download the video stream.");
67+
await Promise.all([
68+
msg.reply("Failed to download the video."),
69+
msg.react(""),
70+
]);
6271
return;
6372
}
6473

@@ -79,15 +88,9 @@ export default async function (msg: Message) {
7988
writeStream.on("error", reject);
8089
});
8190

82-
const audioBuffer = fs.readFileSync(`${tempPath}`);
83-
const media = new MessageMedia(
84-
"audio/mpeg",
85-
audioBuffer.toString("base64"),
86-
`${video.title}.mp4`,
87-
);
88-
91+
const media = MessageMedia.fromFilePath(tempPath);
8992
await msg.reply(media, undefined, {
90-
caption: `${video.title}`,
93+
caption: video.title.text,
9194
});
9295

9396
fs.promises.unlink(tempPath);

0 commit comments

Comments
 (0)