Overview
TheuseTimeline hook provides a convenient interface to control playback, access track information, and manage the audio timeline. It returns an object with reactive getters and control methods.
Usage
import { useTimeline } from 'discord-player';
export default createCommand({
data: new SlashCommandBuilder()
.setName('pause')
.setDescription('Pause playback'),
async execute({ interaction }) {
const timeline = useTimeline();
if (!timeline) {
return interaction.reply('No music is playing!');
}
timeline.pause();
return interaction.reply('⏸️ Paused playback');
}
});
Signature
function useTimeline(): GuildQueueTimeline | null
function useTimeline(options: Partial<TimelineDispatcherOptions>): GuildQueueTimeline | null
Parameters
Return Value
The timeline object if a queue exists, or
null if no queue is found.Show properties
Show properties
Current playback timestamp with current and total time information.
Current volume level (0-200).
Whether playback is currently paused.
Currently playing track, or
null if none.Pause playback. Returns
true if successful.Resume playback. Returns
true if successful.Set volume (0-200). Returns
true if successful.Seek to a specific position in milliseconds. Returns
true if successful.When to Use
UseuseTimeline when you need to:
- Control playback (pause, resume)
- Get current timestamp or track info
- Seek to a specific position
- Adjust volume
- Check playback state
Example: Pause/Resume Command
import { useTimeline } from 'discord-player';
export default createCommand({
data: new SlashCommandBuilder()
.setName('pause')
.setDescription('Pause or resume playback'),
async execute({ interaction }) {
const timeline = useTimeline();
if (!timeline) {
return interaction.reply('❌ No music is playing!');
}
if (!timeline.track) {
return interaction.reply('❌ No track is currently playing!');
}
if (timeline.paused) {
timeline.resume();
return interaction.reply('▶️ Resumed playback');
} else {
timeline.pause();
return interaction.reply('⏸️ Paused playback');
}
}
});
Example: Volume Command
import { useTimeline } from 'discord-player';
export default createCommand({
data: new SlashCommandBuilder()
.setName('volume')
.setDescription('Set or view the volume')
.addIntegerOption(option =>
option.setName('level')
.setDescription('Volume level (0-200)')
.setMinValue(0)
.setMaxValue(200)
),
async execute({ interaction }) {
const timeline = useTimeline();
if (!timeline) {
return interaction.reply('No music is playing!');
}
const level = interaction.options.getInteger('level');
if (level === null) {
return interaction.reply(`🔊 Current volume: **${timeline.volume}%**`);
}
const success = timeline.setVolume(level);
if (!success) {
return interaction.reply('❌ Failed to set volume!');
}
return interaction.reply(`🔊 Volume set to **${level}%**`);
}
});
Example: Seek Command
import { useTimeline } from 'discord-player';
import ms from 'ms';
export default createCommand({
data: new SlashCommandBuilder()
.setName('seek')
.setDescription('Seek to a position in the track')
.addStringOption(option =>
option.setName('time')
.setDescription('Time to seek to (e.g., 1m30s, 90s)')
.setRequired(true)
),
async execute({ interaction }) {
const timeline = useTimeline();
if (!timeline) {
return interaction.reply('No music is playing!');
}
if (!timeline.track) {
return interaction.reply('No track is playing!');
}
const timeStr = interaction.options.getString('time', true);
const timeMs = ms(timeStr);
if (!timeMs) {
return interaction.reply('Invalid time format! Use formats like: 1m30s, 90s, 2m');
}
await interaction.deferReply();
const success = await timeline.setPosition(timeMs);
if (!success) {
return interaction.followUp('❌ Failed to seek!');
}
return interaction.followUp(`⏩ Seeked to ${timeStr}`);
}
});
Example: Now Playing with Timeline
import { useTimeline } from 'discord-player';
export default createCommand({
data: new SlashCommandBuilder()
.setName('nowplaying')
.setDescription('Show current track info'),
async execute({ interaction }) {
const timeline = useTimeline();
if (!timeline) {
return interaction.reply('No music is playing!');
}
if (!timeline.track) {
return interaction.reply('No track is playing!');
}
const track = timeline.track;
const { current, total } = timeline.timestamp;
return interaction.reply({
embeds: [{
title: '🎵 Now Playing',
description: `**${track.title}**\n${track.author}`,
fields: [
{
name: 'Duration',
value: `${current.label} / ${total.label}`,
inline: true
},
{
name: 'Volume',
value: `${timeline.volume}%`,
inline: true
},
{
name: 'Status',
value: timeline.paused ? '⏸️ Paused' : '▶️ Playing',
inline: true
}
],
thumbnail: { url: track.thumbnail }
}]
});
}
});
Example: With Filters Ignored
import { useTimeline } from 'discord-player';
export default createCommand({
data: new SlashCommandBuilder()
.setName('timestamp')
.setDescription('Get raw timestamp without filter effects'),
async execute({ interaction }) {
const timeline = useTimeline({ ignoreFilters: true });
if (!timeline) {
return interaction.reply('No music is playing!');
}
const { current, total } = timeline.timestamp;
return interaction.reply(
`Raw timestamp: ${current.label} / ${total.label}`
);
}
});
Example: Progress Bar
import { useTimeline } from 'discord-player';
import { useQueue } from 'discord-player';
export default createCommand({
data: new SlashCommandBuilder()
.setName('progress')
.setDescription('Show playback progress'),
async execute({ interaction }) {
const timeline = useTimeline();
const queue = useQueue();
if (!timeline || !queue) {
return interaction.reply('No music is playing!');
}
if (!timeline.track) {
return interaction.reply('No track is playing!');
}
const { current, total } = timeline.timestamp;
const progress = queue.node.createProgressBar();
return interaction.reply({
embeds: [{
title: timeline.track.title,
description: `${progress}\n\n${current.label} / ${total.label}`,
footer: {
text: timeline.paused ? '⏸️ Paused' : '▶️ Playing'
}
}]
});
}
});
Related Hooks
- useQueue - Access guild queue
- useVolume - Dedicated volume control
- useHistory - Access queue history
- useMainPlayer - Access main player