discord-multipurpose-bot/commands/music/seek.js
Ayden cb5a906850
Some checks are pending
Docker / build (push) Waiting to run
Feat/Add Music Commands (#1)
* add simple music functionality

* update workflow

* update Dockerfile

* update Dockerfile

* update Dockerfile

* update Dockerfile

* add few more music commands

* add lyrics command

* update lyrics command

* add loop, and add categories to all commands

* change discord status

* seperate distube and change startup console theme

* Update README

* UPDATE LICENSE file

* fix docker compose image, add better error handling for distube and update tagging workflow

* switch to node-alpine image for docker

* switch to node-alpine image for docker

* update ascii

* music commands imporvements, implement live lyrics, some guards and bot leaving on empty

* use ffmpeg package rather than ffmpeg-static
2025-09-21 01:26:18 +01:00

98 lines
2.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { SlashCommandBuilder } = require("discord.js");
const { requireVC, requireQueue } = require("../../utils/musicGuards");
/** Parse "90", "1:30", "01:02:03", "+30", "-10", "+1:00", "-0:30" */
function parseTime(input) {
const t = input.trim();
const sign = t.startsWith("+") ? 1 : t.startsWith("-") ? -1 : 0;
const core = sign ? t.slice(1) : t;
if (/^\d+$/.test(core)) {
const secs = Number(core);
return { seconds: sign ? sign * secs : secs, relative: Boolean(sign) };
}
if (/^\d{1,2}:\d{1,2}(:\d{1,2})?$/.test(core)) {
const parts = core.split(":").map(Number);
let secs = 0;
if (parts.length === 3) {
const [hh, mm, ss] = parts;
secs = hh * 3600 + mm * 60 + ss;
} else {
const [mm, ss] = parts;
secs = mm * 60 + ss;
}
return { seconds: sign ? sign * secs : secs, relative: Boolean(sign) };
}
return null;
}
function fmt(seconds) {
seconds = Math.max(0, Math.floor(seconds));
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
return h > 0
? `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`
: `${m}:${String(s).padStart(2, "0")}`;
}
module.exports = {
data: new SlashCommandBuilder()
.setName("seek")
.setDescription("Seek to a timestamp or jump by seconds.")
.addStringOption((opt) =>
opt
.setName("to")
.setDescription("e.g. 90, 1:30, 01:02:03, +30, -10")
.setRequired(true)
),
category: "Music",
async execute(interaction, client) {
try {
await interaction.deferReply();
requireVC(interaction);
const queue = requireQueue(client, interaction);
const song = queue.songs?.[0];
if (!song || !Number.isFinite(song.duration) || song.isLive) {
throw new Error("❌ This stream/track doesnt support seeking.");
}
const input = interaction.options.getString("to", true);
const parsed = parseTime(input);
if (!parsed) {
throw new Error(
"❌ Invalid time. Use `90`, `1:30`, `01:02:03`, `+30`, or `-10`."
);
}
const current = Math.floor(queue.currentTime ?? 0);
const duration = Math.floor(song.duration);
let target = parsed.relative ? current + parsed.seconds : parsed.seconds;
target = Math.max(0, Math.min(duration - 1, Math.floor(target)));
await queue.seek(target);
try {
const live = require("../../utils/liveLyricsManager");
live.seek(queue, target);
} catch {}
await interaction.followUp(
`⏭️ Seeked to **${fmt(target)}** (track length \`${fmt(duration)}\`).`
);
} catch (e) {
const msg = e?.message ?? "❌ Failed to seek.";
if (interaction.deferred || interaction.replied) {
await interaction.followUp({ content: msg, ephemeral: true });
} else {
await interaction.reply({ content: msg, ephemeral: true });
}
}
},
};