diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
index 2d2d62d04..c8ea39510 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -53,5 +53,12 @@ body:
required: true
- label: I have searched for similar issues [on the issues page](https://github.com/jagrosh/MusicBot/issues?q=is%3Aissue)
required: true
- - label: "I am running the latest version of the bot: ![Release](https://img.shields.io/github/release/jagrosh/MusicBot.svg)"
+ - label: I am using an official release from [the releases page](https://github.com/jagrosh/MusicBot/releases)
required: true
+ - type: input
+ id: version
+ attributes:
+ label: JMusicBot Version
+ description: Which version of JMusicBot are you running?
+ validations:
+ required: true
diff --git a/pom.xml b/pom.xml
index efcbf9282..b6a5f99c1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,9 +33,9 @@
https://m2.duncte123.dev/releases
- arbjergDev-snapshots
+ arbjergDev
Lavalink Repository
- https://maven.lavalink.dev/snapshots
+ https://maven.lavalink.dev/releases
@@ -57,7 +57,12 @@
dev.arbjerg
lavaplayer
- 0eaeee195f0315b2617587aa3537fa202df07ddc-SNAPSHOT
+ 2.2.1
+
+
+ dev.lavalink.youtube
+ common
+ 1.4.0
com.github.jagrosh
diff --git a/src/main/java/com/jagrosh/jmusicbot/BotConfig.java b/src/main/java/com/jagrosh/jmusicbot/BotConfig.java
index 9a57beff9..f6a8757ac 100644
--- a/src/main/java/com/jagrosh/jmusicbot/BotConfig.java
+++ b/src/main/java/com/jagrosh/jmusicbot/BotConfig.java
@@ -16,8 +16,8 @@
package com.jagrosh.jmusicbot;
import com.jagrosh.jmusicbot.entities.Prompt;
-import com.jagrosh.jmusicbot.utils.FormatUtil;
import com.jagrosh.jmusicbot.utils.OtherUtil;
+import com.jagrosh.jmusicbot.utils.TimeUtil;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.typesafe.config.*;
import java.io.IOException;
@@ -40,9 +40,11 @@ public class BotConfig
private Path path = null;
private String token, prefix, altprefix, helpWord, playlistsFolder, logLevel,
- successEmoji, warningEmoji, errorEmoji, loadingEmoji, searchingEmoji;
+ successEmoji, warningEmoji, errorEmoji, loadingEmoji, searchingEmoji,
+ evalEngine;
private boolean stayInChannel, songInGame, npImages, updatealerts, useEval, dbots;
private long owner, maxSeconds, aloneTimeUntilStop;
+ private int maxYTPlaylistPages;
private double skipratio;
private OnlineStatus status;
private Activity game;
@@ -88,7 +90,9 @@ public void load()
updatealerts = config.getBoolean("updatealerts");
logLevel = config.getString("loglevel");
useEval = config.getBoolean("eval");
+ evalEngine = config.getString("evalengine");
maxSeconds = config.getLong("maxtime");
+ maxYTPlaylistPages = config.getInt("maxytplaylistpages");
aloneTimeUntilStop = config.getLong("alonetimeuntilstop");
playlistsFolder = config.getString("playlistsfolder");
aliases = config.getConfig("aliases");
@@ -324,6 +328,11 @@ public boolean useEval()
return useEval;
}
+ public String getEvalEngine()
+ {
+ return evalEngine;
+ }
+
public boolean useNPImages()
{
return npImages;
@@ -334,9 +343,14 @@ public long getMaxSeconds()
return maxSeconds;
}
+ public int getMaxYTPlaylistPages()
+ {
+ return maxYTPlaylistPages;
+ }
+
public String getMaxTime()
{
- return FormatUtil.formatTime(maxSeconds * 1000);
+ return TimeUtil.formatTime(maxSeconds * 1000);
}
public long getAloneTimeUntilStop()
diff --git a/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java b/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java
index f1e866bb9..262ef1ecb 100644
--- a/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java
+++ b/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java
@@ -199,6 +199,7 @@ private static CommandClient createCommandClient(BotConfig config, SettingsManag
new RemoveCmd(bot),
new SearchCmd(bot),
new SCSearchCmd(bot),
+ new SeekCmd(bot),
new ShuffleCmd(bot),
new SkipCmd(bot),
new TiliiCmd(bot),
diff --git a/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java b/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java
index 310389dbe..aa8305261 100644
--- a/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java
+++ b/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java
@@ -18,9 +18,12 @@
import com.jagrosh.jmusicbot.playlist.PlaylistLoader.Playlist;
import com.jagrosh.jmusicbot.queue.AbstractQueue;
import com.jagrosh.jmusicbot.settings.QueueType;
+import com.jagrosh.jmusicbot.utils.TimeUtil;
import com.jagrosh.jmusicbot.settings.RepeatMode;
+import com.jagrosh.jmusicbot.utils.OtherUtil;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
+import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
@@ -39,6 +42,7 @@
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
+import org.slf4j.LoggerFactory;
/**
*
@@ -50,6 +54,7 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
public final static String PAUSE_EMOJI = "\u23F8"; // ⏸
public final static String STOP_EMOJI = "\u23F9"; // ⏹
+
private final List defaultQueue = new LinkedList<>();
private final Set votes = new HashSet<>();
@@ -197,6 +202,11 @@ public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason
}
}
+ @Override
+ public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) {
+ LoggerFactory.getLogger("AudioHandler").error("Track " + track.getIdentifier() + " has failed to play", exception);
+ }
+
@Override
public void onTrackStart(AudioPlayer player, AudioTrack track)
{
@@ -246,7 +256,7 @@ public Message getNowPlaying(JDA jda)
double progress = (double)audioPlayer.getPlayingTrack().getPosition()/track.getDuration();
eb.setDescription(getStatusEmoji()
+ " "+FormatUtil.progressBar(progress)
- + " `[" + FormatUtil.formatTime(track.getPosition()) + "/" + FormatUtil.formatTime(track.getDuration()) + "]` "
+ + " `[" + TimeUtil.formatTime(track.getPosition()) + "/" + TimeUtil.formatTime(track.getDuration()) + "]` "
+ FormatUtil.volumeIcon(audioPlayer.getVolume()));
return mb.setEmbeds(eb.build()).build();
diff --git a/src/main/java/com/jagrosh/jmusicbot/audio/PlayerManager.java b/src/main/java/com/jagrosh/jmusicbot/audio/PlayerManager.java
index f4a5605ce..56999a92b 100644
--- a/src/main/java/com/jagrosh/jmusicbot/audio/PlayerManager.java
+++ b/src/main/java/com/jagrosh/jmusicbot/audio/PlayerManager.java
@@ -17,10 +17,19 @@
import com.dunctebot.sourcemanagers.DuncteBotSources;
import com.jagrosh.jmusicbot.Bot;
+import com.sedmelluq.discord.lavaplayer.container.MediaContainerRegistry;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
-import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager;
+import com.sedmelluq.discord.lavaplayer.source.bandcamp.BandcampAudioSourceManager;
+import com.sedmelluq.discord.lavaplayer.source.beam.BeamAudioSourceManager;
+import com.sedmelluq.discord.lavaplayer.source.getyarn.GetyarnAudioSourceManager;
+import com.sedmelluq.discord.lavaplayer.source.http.HttpAudioSourceManager;
+import com.sedmelluq.discord.lavaplayer.source.nico.NicoAudioSourceManager;
+import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager;
+import com.sedmelluq.discord.lavaplayer.source.twitch.TwitchStreamAudioSourceManager;
+import com.sedmelluq.discord.lavaplayer.source.vimeo.VimeoAudioSourceManager;
+import dev.lavalink.youtube.YoutubeAudioSourceManager;
import net.dv8tion.jda.api.entities.Guild;
/**
@@ -39,10 +48,23 @@ public PlayerManager(Bot bot)
public void init()
{
TransformativeAudioSourceManager.createTransforms(bot.getConfig().getTransforms()).forEach(t -> registerSourceManager(t));
- AudioSourceManagers.registerRemoteSources(this);
+
+ YoutubeAudioSourceManager yt = new YoutubeAudioSourceManager(true);
+ yt.setPlaylistPageCount(bot.getConfig().getMaxYTPlaylistPages());
+ registerSourceManager(yt);
+
+ registerSourceManager(SoundCloudAudioSourceManager.createDefault());
+ registerSourceManager(new BandcampAudioSourceManager());
+ registerSourceManager(new VimeoAudioSourceManager());
+ registerSourceManager(new TwitchStreamAudioSourceManager());
+ registerSourceManager(new BeamAudioSourceManager());
+ registerSourceManager(new GetyarnAudioSourceManager());
+ registerSourceManager(new NicoAudioSourceManager());
+ registerSourceManager(new HttpAudioSourceManager(MediaContainerRegistry.DEFAULT_REGISTRY));
+
AudioSourceManagers.registerLocalSource(this);
+
DuncteBotSources.registerAll(this, "en-US");
- source(YoutubeAudioSourceManager.class).setPlaylistPageCount(10);
}
public Bot getBot()
diff --git a/src/main/java/com/jagrosh/jmusicbot/audio/QueuedTrack.java b/src/main/java/com/jagrosh/jmusicbot/audio/QueuedTrack.java
index 546d62ef4..81e4f75c2 100644
--- a/src/main/java/com/jagrosh/jmusicbot/audio/QueuedTrack.java
+++ b/src/main/java/com/jagrosh/jmusicbot/audio/QueuedTrack.java
@@ -15,10 +15,10 @@
*/
package com.jagrosh.jmusicbot.audio;
+import com.jagrosh.jmusicbot.utils.TimeUtil;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
import com.jagrosh.jmusicbot.queue.Queueable;
-import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.api.entities.User;
/**
@@ -28,22 +28,22 @@
public class QueuedTrack implements Queueable
{
private final AudioTrack track;
-
- public QueuedTrack(AudioTrack track, User owner)
- {
- this(track, new RequestMetadata(owner));
- }
-
+ private final RequestMetadata requestMetadata;
+
public QueuedTrack(AudioTrack track, RequestMetadata rm)
{
this.track = track;
this.track.setUserData(rm == null ? RequestMetadata.EMPTY : rm);
+
+ this.requestMetadata = rm;
+ if (this.track.isSeekable() && rm != null)
+ track.setPosition(rm.requestInfo.startTimestamp);
}
@Override
public long getIdentifier()
{
- return track.getUserData() == null ? 0L : track.getUserData(RequestMetadata.class).getOwner();
+ return requestMetadata.getOwner();
}
public AudioTrack getTrack()
@@ -51,10 +51,15 @@ public AudioTrack getTrack()
return track;
}
+ public RequestMetadata getRequestMetadata()
+ {
+ return requestMetadata;
+ }
+
@Override
public String toString()
{
- String entry = "`[" + FormatUtil.formatTime(track.getDuration()) + "]` ";
+ String entry = "`[" + TimeUtil.formatTime(track.getDuration()) + "]` ";
AudioTrackInfo trackInfo = track.getInfo();
entry = entry + (trackInfo.uri.startsWith("http") ? "[**" + trackInfo.title + "**]("+trackInfo.uri+")" : "**" + trackInfo.title + "**");
return entry + " - <@" + track.getUserData(RequestMetadata.class).getOwner() + ">";
diff --git a/src/main/java/com/jagrosh/jmusicbot/audio/RequestMetadata.java b/src/main/java/com/jagrosh/jmusicbot/audio/RequestMetadata.java
index f54ea6b26..3768cb48c 100644
--- a/src/main/java/com/jagrosh/jmusicbot/audio/RequestMetadata.java
+++ b/src/main/java/com/jagrosh/jmusicbot/audio/RequestMetadata.java
@@ -15,40 +15,67 @@
*/
package com.jagrosh.jmusicbot.audio;
+import com.jagrosh.jdautilities.command.CommandEvent;
+import com.jagrosh.jmusicbot.utils.TimeUtil;
+import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import net.dv8tion.jda.api.entities.User;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
*
* @author John Grosh (john.a.grosh@gmail.com)
*/
public class RequestMetadata
{
- public static final RequestMetadata EMPTY = new RequestMetadata(null);
+ public static final RequestMetadata EMPTY = new RequestMetadata(null, null);
public final UserInfo user;
+ public final RequestInfo requestInfo;
- public RequestMetadata(User user)
+ public RequestMetadata(User user, RequestInfo requestInfo)
{
this.user = user == null ? null : new UserInfo(user.getIdLong(), user.getName(), user.getDiscriminator(), user.getEffectiveAvatarUrl());
+ this.requestInfo = requestInfo;
}
public long getOwner()
{
return user == null ? 0L : user.id;
}
+
+ public static RequestMetadata fromResultHandler(AudioTrack track, CommandEvent event)
+ {
+ return new RequestMetadata(event.getAuthor(), new RequestInfo(event.getArgs(), track.getInfo().uri));
+ }
- public class RequestInfo
+ public static class RequestInfo
{
public final String query, url;
-
- private RequestInfo(String query, String url)
+ public final long startTimestamp;
+
+ public RequestInfo(String query, String url)
+ {
+ this(query, url, tryGetTimestamp(query));
+ }
+
+ private RequestInfo(String query, String url, long startTimestamp)
{
- this.query = query;
this.url = url;
+ this.query = query;
+ this.startTimestamp = startTimestamp;
+ }
+
+ private static final Pattern youtubeTimestampPattern = Pattern.compile("youtu(?:\\.be|be\\..+)/.*\\?.*(?!.*list=)t=([\\dhms]+)");
+ private static long tryGetTimestamp(String url)
+ {
+ Matcher matcher = youtubeTimestampPattern.matcher(url);
+ return matcher.find() ? TimeUtil.parseUnitTime(matcher.group(1)) : 0;
}
}
- public class UserInfo
+ public static class UserInfo
{
public final long id;
public final String username, discrim, avatar;
diff --git a/src/main/java/com/jagrosh/jmusicbot/audio/TransformativeAudioSourceManager.java b/src/main/java/com/jagrosh/jmusicbot/audio/TransformativeAudioSourceManager.java
index dc57f6476..7e4734d69 100644
--- a/src/main/java/com/jagrosh/jmusicbot/audio/TransformativeAudioSourceManager.java
+++ b/src/main/java/com/jagrosh/jmusicbot/audio/TransformativeAudioSourceManager.java
@@ -16,10 +16,10 @@
package com.jagrosh.jmusicbot.audio;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
-import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager;
import com.sedmelluq.discord.lavaplayer.track.AudioItem;
import com.sedmelluq.discord.lavaplayer.track.AudioReference;
import com.typesafe.config.Config;
+import dev.lavalink.youtube.YoutubeAudioSourceManager;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/dj/PlaynextCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/dj/PlaynextCmd.java
index 91d79be14..0d5924edc 100644
--- a/src/main/java/com/jagrosh/jmusicbot/commands/dj/PlaynextCmd.java
+++ b/src/main/java/com/jagrosh/jmusicbot/commands/dj/PlaynextCmd.java
@@ -19,8 +19,10 @@
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.audio.QueuedTrack;
+import com.jagrosh.jmusicbot.audio.RequestMetadata;
import com.jagrosh.jmusicbot.commands.DJCommand;
import com.jagrosh.jmusicbot.utils.FormatUtil;
+import com.jagrosh.jmusicbot.utils.TimeUtil;
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
@@ -79,13 +81,13 @@ private void loadSingle(AudioTrack track)
if(bot.getConfig().isTooLong(track))
{
m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
- +FormatUtil.formatTime(track.getDuration())+"` > `"+FormatUtil.formatTime(bot.getConfig().getMaxSeconds()*1000)+"`")).queue();
+ + TimeUtil.formatTime(track.getDuration())+"` > `"+ TimeUtil.formatTime(bot.getConfig().getMaxSeconds()*1000)+"`")).queue();
return;
}
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
- int pos = handler.addTrackToFront(new QueuedTrack(track, event.getAuthor()))+1;
+ int pos = handler.addTrackToFront(new QueuedTrack(track, RequestMetadata.fromResultHandler(track, event)))+1;
String addMsg = FormatUtil.filter(event.getClient().getSuccess()+" Added **"+track.getInfo().title
- +"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0?"to begin playing":" to the queue at position "+pos));
+ +"** (`"+ TimeUtil.formatTime(track.getDuration())+"`) "+(pos==0?"to begin playing":" to the queue at position "+pos));
m.editMessage(addMsg).queue();
}
diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/music/PlayCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/music/PlayCmd.java
index 6f9a3d297..67c8c8a1b 100644
--- a/src/main/java/com/jagrosh/jmusicbot/commands/music/PlayCmd.java
+++ b/src/main/java/com/jagrosh/jmusicbot/commands/music/PlayCmd.java
@@ -15,6 +15,8 @@
*/
package com.jagrosh.jmusicbot.commands.music;
+import com.jagrosh.jmusicbot.audio.RequestMetadata;
+import com.jagrosh.jmusicbot.utils.TimeUtil;
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity;
@@ -108,13 +110,13 @@ private void loadSingle(AudioTrack track, AudioPlaylist playlist)
if(bot.getConfig().isTooLong(track))
{
m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
- +FormatUtil.formatTime(track.getDuration())+"` > `"+FormatUtil.formatTime(bot.getConfig().getMaxSeconds()*1000)+"`")).queue();
+ + TimeUtil.formatTime(track.getDuration())+"` > `"+ TimeUtil.formatTime(bot.getConfig().getMaxSeconds()*1000)+"`")).queue();
return;
}
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
- int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()))+1;
+ int pos = handler.addTrack(new QueuedTrack(track, RequestMetadata.fromResultHandler(track, event)))+1;
String addMsg = FormatUtil.filter(event.getClient().getSuccess()+" Added **"+track.getInfo().title
- +"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0?"to begin playing":" to the queue at position "+pos));
+ +"** (`"+ TimeUtil.formatTime(track.getDuration())+"`) "+(pos==0?"to begin playing":" to the queue at position "+pos));
if(playlist==null || !event.getSelfMember().hasPermission(event.getTextChannel(), Permission.MESSAGE_ADD_REACTION))
m.editMessage(addMsg).queue();
else
@@ -144,7 +146,7 @@ private int loadPlaylist(AudioPlaylist playlist, AudioTrack exclude)
if(!bot.getConfig().isTooLong(track) && !track.equals(exclude))
{
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
- handler.addTrack(new QueuedTrack(track, event.getAuthor()));
+ handler.addTrack(new QueuedTrack(track, RequestMetadata.fromResultHandler(track, event)));
count[0]++;
}
});
@@ -243,7 +245,7 @@ public void doCommand(CommandEvent event)
event.getChannel().sendMessage(loadingEmoji+" Loading playlist **"+event.getArgs()+"**... ("+playlist.getItems().size()+" items)").queue(m ->
{
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
- playlist.loadTracks(bot.getPlayerManager(), (at)->handler.addTrack(new QueuedTrack(at, event.getAuthor())), () -> {
+ playlist.loadTracks(bot.getPlayerManager(), (at)->handler.addTrack(new QueuedTrack(at, RequestMetadata.fromResultHandler(at, event))), () -> {
StringBuilder builder = new StringBuilder(playlist.getTracks().isEmpty()
? event.getClient().getWarning()+" No tracks were loaded!"
: event.getClient().getSuccess()+" Loaded **"+playlist.getTracks().size()+"** tracks!");
diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/music/QueueCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/music/QueueCmd.java
index 2b18aaf4f..351681294 100644
--- a/src/main/java/com/jagrosh/jmusicbot/commands/music/QueueCmd.java
+++ b/src/main/java/com/jagrosh/jmusicbot/commands/music/QueueCmd.java
@@ -27,6 +27,7 @@
import com.jagrosh.jmusicbot.settings.RepeatMode;
import com.jagrosh.jmusicbot.settings.Settings;
import com.jagrosh.jmusicbot.utils.FormatUtil;
+import com.jagrosh.jmusicbot.utils.TimeUtil;
import net.dv8tion.jda.api.MessageBuilder;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Message;
@@ -112,7 +113,7 @@ private String getQueueTitle(AudioHandler ah, String success, int songslength, l
.append(ah.getPlayer().getPlayingTrack().getInfo().title).append("**\n");
}
return FormatUtil.filter(sb.append(success).append(" Current Queue | ").append(songslength)
- .append(" entries | `").append(FormatUtil.formatTime(total)).append("` ")
+ .append(" entries | `").append(TimeUtil.formatTime(total)).append("` ")
.append("| ").append(queueType.getEmoji()).append(" `").append(queueType.getUserFriendlyName()).append('`')
.append(repeatmode.getEmoji() != null ? " | "+repeatmode.getEmoji() : "").toString());
}
diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/music/SearchCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/music/SearchCmd.java
index 9358698aa..265be6045 100644
--- a/src/main/java/com/jagrosh/jmusicbot/commands/music/SearchCmd.java
+++ b/src/main/java/com/jagrosh/jmusicbot/commands/music/SearchCmd.java
@@ -15,6 +15,8 @@
*/
package com.jagrosh.jmusicbot.commands.music;
+import com.jagrosh.jmusicbot.audio.RequestMetadata;
+import com.jagrosh.jmusicbot.utils.TimeUtil;
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity;
@@ -88,13 +90,13 @@ public void trackLoaded(AudioTrack track)
if(bot.getConfig().isTooLong(track))
{
m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
- +FormatUtil.formatTime(track.getDuration())+"` > `"+bot.getConfig().getMaxTime()+"`")).queue();
+ + TimeUtil.formatTime(track.getDuration())+"` > `"+bot.getConfig().getMaxTime()+"`")).queue();
return;
}
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
- int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()))+1;
+ int pos = handler.addTrack(new QueuedTrack(track, RequestMetadata.fromResultHandler(track, event)))+1;
m.editMessage(FormatUtil.filter(event.getClient().getSuccess()+" Added **"+track.getInfo().title
- +"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
+ +"** (`"+ TimeUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
: " to the queue at position "+pos))).queue();
}
@@ -110,13 +112,13 @@ public void playlistLoaded(AudioPlaylist playlist)
if(bot.getConfig().isTooLong(track))
{
event.replyWarning("This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `"
- +FormatUtil.formatTime(track.getDuration())+"` > `"+bot.getConfig().getMaxTime()+"`");
+ + TimeUtil.formatTime(track.getDuration())+"` > `"+bot.getConfig().getMaxTime()+"`");
return;
}
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
- int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()))+1;
+ int pos = handler.addTrack(new QueuedTrack(track, RequestMetadata.fromResultHandler(track, event)))+1;
event.replySuccess("Added **" + FormatUtil.filter(track.getInfo().title)
- + "** (`" + FormatUtil.formatTime(track.getDuration()) + "`) " + (pos==0 ? "to begin playing"
+ + "** (`" + TimeUtil.formatTime(track.getDuration()) + "`) " + (pos==0 ? "to begin playing"
: " to the queue at position "+pos));
})
.setCancel((msg) -> {})
@@ -125,7 +127,7 @@ public void playlistLoaded(AudioPlaylist playlist)
for(int i=0; i<4 && i.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jagrosh.jmusicbot.commands.music;
+
+import com.jagrosh.jdautilities.command.CommandEvent;
+import com.jagrosh.jmusicbot.Bot;
+import com.jagrosh.jmusicbot.audio.AudioHandler;
+import com.jagrosh.jmusicbot.commands.DJCommand;
+import com.jagrosh.jmusicbot.commands.MusicCommand;
+import com.jagrosh.jmusicbot.utils.TimeUtil;
+import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
+
+
+/**
+ * @author Whew., Inc.
+ */
+public class SeekCmd extends MusicCommand
+{
+ public SeekCmd(Bot bot)
+ {
+ super(bot);
+ this.name = "seek";
+ this.help = "seeks the current song";
+ this.arguments = "[+ | -] |<0h0m0s | 0m0s | 0s>";
+ this.aliases = bot.getConfig().getAliases(this.name);
+ this.beListening = true;
+ this.bePlaying = true;
+ }
+
+ @Override
+ public void doCommand(CommandEvent event)
+ {
+ AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
+ AudioTrack playingTrack = handler.getPlayer().getPlayingTrack();
+ if (!playingTrack.isSeekable())
+ {
+ event.replyError("This track is not seekable.");
+ return;
+ }
+
+
+ if (!DJCommand.checkDJPermission(event) && playingTrack.getUserData(Long.class) != event.getAuthor().getIdLong())
+ {
+ event.replyError("You cannot seek **" + playingTrack.getInfo().title + "** because you didn't add it!");
+ return;
+ }
+
+ String args = event.getArgs();
+ TimeUtil.SeekTime seekTime = TimeUtil.parseTime(args);
+ if (seekTime == null)
+ {
+ event.replyError("Invalid seek! Expected format: " + arguments + "\nExamples: `1:02:23` `+1:10` `-90`, `1h10m`, `+90s`");
+ return;
+ }
+
+ long currentPosition = playingTrack.getPosition();
+ long trackDuration = playingTrack.getDuration();
+
+ long seekMilliseconds = seekTime.relative ? currentPosition + seekTime.milliseconds : seekTime.milliseconds;
+ if (seekMilliseconds > trackDuration)
+ {
+ event.replyError("Cannot seek to `" + TimeUtil.formatTime(seekMilliseconds) + "` because the current track is `" + TimeUtil.formatTime(trackDuration) + "` long!");
+ }
+ else
+ {
+ try
+ {
+ playingTrack.setPosition(seekMilliseconds);
+ }
+ catch (Exception e)
+ {
+ event.replyError("An error occurred while trying to seek!");
+ e.printStackTrace();
+ return;
+ }
+ }
+ event.replySuccess("Successfully seeked to `" + TimeUtil.formatTime(playingTrack.getPosition()) + "/" + TimeUtil.formatTime(playingTrack.getDuration()) + "`!");
+ }
+}
diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/music/TiliiCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/music/TiliiCmd.java
index 0cf12a376..183de5db6 100644
--- a/src/main/java/com/jagrosh/jmusicbot/commands/music/TiliiCmd.java
+++ b/src/main/java/com/jagrosh/jmusicbot/commands/music/TiliiCmd.java
@@ -26,10 +26,12 @@
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.audio.QueuedTrack;
+import com.jagrosh.jmusicbot.audio.RequestMetadata;
import com.jagrosh.jmusicbot.commands.DJCommand;
import com.jagrosh.jmusicbot.commands.MusicCommand;
import com.jagrosh.jmusicbot.playlist.PlaylistLoader.Playlist;
import com.jagrosh.jmusicbot.utils.FormatUtil;
+import com.jagrosh.jmusicbot.utils.TimeUtil;
import java.io.BufferedReader;
import java.io.IOException;
@@ -146,14 +148,14 @@ private void loadSingle(AudioTrack track, AudioPlaylist playlist) {
if (bot.getConfig().isTooLong(track)) {
m.editMessage(FormatUtil.filter(event.getClient().getWarning() + " This track (**"
+ track.getInfo().title + "**) is longer than the allowed maximum: `"
- + FormatUtil.formatTime(track.getDuration()) + "` > `"
- + FormatUtil.formatTime(bot.getConfig().getMaxSeconds() * 1000) + "`")).queue();
+ + TimeUtil.formatTime(track.getDuration()) + "` > `"
+ + TimeUtil.formatTime(bot.getConfig().getMaxSeconds() * 1000) + "`")).queue();
return;
}
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
- int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor())) + 1;
+ int pos = handler.addTrack(new QueuedTrack(track, RequestMetadata.fromResultHandler(track, event))) + 1;
String addMsg = FormatUtil.filter(event.getClient().getSuccess() + " Added **" + track.getInfo().title
- + "** (`" + FormatUtil.formatTime(track.getDuration()) + "`) "
+ + "** (`" + TimeUtil.formatTime(track.getDuration()) + "`) "
+ (pos == 0 ? "to begin playing" : " to the queue at position " + pos));
if (playlist == null
|| !event.getSelfMember().hasPermission(event.getTextChannel(), Permission.MESSAGE_ADD_REACTION))
@@ -186,7 +188,7 @@ private int loadPlaylist(AudioPlaylist playlist, AudioTrack exclude) {
playlist.getTracks().stream().forEach((track) -> {
if (!bot.getConfig().isTooLong(track) && !track.equals(exclude)) {
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
- handler.addTrack(new QueuedTrack(track, event.getAuthor()));
+ handler.addTrack(new QueuedTrack(track, RequestMetadata.fromResultHandler(track, event)));
count[0]++;
}
});
@@ -274,7 +276,9 @@ public void doCommand(CommandEvent event) {
+ playlist.getItems().size() + " items)").queue(m -> {
AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler();
playlist.loadTracks(bot.getPlayerManager(),
- (at) -> handler.addTrack(new QueuedTrack(at, event.getAuthor())), () -> {
+ (at) -> handler
+ .addTrack(new QueuedTrack(at, RequestMetadata.fromResultHandler(at, event))),
+ () -> {
StringBuilder builder = new StringBuilder(playlist.getTracks().isEmpty()
? event.getClient().getWarning() + " No tracks were loaded!"
: event.getClient().getSuccess() + " Loaded **"
diff --git a/src/main/java/com/jagrosh/jmusicbot/commands/owner/EvalCmd.java b/src/main/java/com/jagrosh/jmusicbot/commands/owner/EvalCmd.java
index 1a7f0294c..8143f078c 100644
--- a/src/main/java/com/jagrosh/jmusicbot/commands/owner/EvalCmd.java
+++ b/src/main/java/com/jagrosh/jmusicbot/commands/owner/EvalCmd.java
@@ -29,6 +29,7 @@
public class EvalCmd extends OwnerCommand
{
private final Bot bot;
+ private final String engine;
public EvalCmd(Bot bot)
{
@@ -36,13 +37,20 @@ public EvalCmd(Bot bot)
this.name = "eval";
this.help = "evaluates nashorn code";
this.aliases = bot.getConfig().getAliases(this.name);
+ this.engine = bot.getConfig().getEvalEngine();
this.guildOnly = false;
}
@Override
protected void execute(CommandEvent event)
{
- ScriptEngine se = new ScriptEngineManager().getEngineByName("Nashorn");
+ ScriptEngine se = new ScriptEngineManager().getEngineByName(engine);
+ if(se == null)
+ {
+ event.replyError("The eval engine provided in the config (`"+engine+"`) doesn't exist. This could be due to an invalid "
+ + "engine name, or the engine not existing in your version of java (`"+System.getProperty("java.version")+"`).");
+ return;
+ }
se.put("bot", bot);
se.put("event", event);
se.put("jda", event.getJDA());
diff --git a/src/main/java/com/jagrosh/jmusicbot/utils/FormatUtil.java b/src/main/java/com/jagrosh/jmusicbot/utils/FormatUtil.java
index 709be7437..e840a9f21 100644
--- a/src/main/java/com/jagrosh/jmusicbot/utils/FormatUtil.java
+++ b/src/main/java/com/jagrosh/jmusicbot/utils/FormatUtil.java
@@ -27,41 +27,29 @@
* @author John Grosh
*/
public class FormatUtil {
-
- public static String formatTime(long duration)
- {
- if(duration == Long.MAX_VALUE)
- return "LIVE";
- long seconds = Math.round(duration/1000.0);
- long hours = seconds/(60*60);
- seconds %= 60*60;
- long minutes = seconds/60;
- seconds %= 60;
- return (hours>0 ? hours+":" : "") + (minutes<10 ? "0"+minutes : minutes) + ":" + (seconds<10 ? "0"+seconds : seconds);
- }
- public static String formatUsername(String username, String discrim)
+ public static String formatUsername(String username, String discrim)
{
- if(discrim == null || discrim.equals("0000"))
+ if(discrim == null || discrim.equals("0000"))
{
return username;
}
- else
+ else
{
return username + "#" + discrim;
}
}
-
+
public static String formatUsername(UserInfo userinfo)
{
return formatUsername(userinfo.username, userinfo.discrim);
}
-
+
public static String formatUsername(User user)
{
return formatUsername(user.getName(), user.getDiscriminator());
}
-
+
public static String progressBar(double percent)
{
String str = "";
diff --git a/src/main/java/com/jagrosh/jmusicbot/utils/LogBackTurboFilter.java b/src/main/java/com/jagrosh/jmusicbot/utils/LogBackTurboFilter.java
deleted file mode 100644
index c991a07ec..000000000
--- a/src/main/java/com/jagrosh/jmusicbot/utils/LogBackTurboFilter.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2024 John Grosh .
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.jagrosh.jmusicbot.utils;
-
-import ch.qos.logback.classic.Level;
-import ch.qos.logback.classic.Logger;
-import ch.qos.logback.classic.turbo.TurboFilter;
-import ch.qos.logback.core.spi.FilterReply;
-import org.slf4j.Marker;
-
-/**
- * A TurboFilter, currently only used to suppress specific log messages from libraries.
- *
- * @author Michaili K.
- */
-public class LogBackTurboFilter extends TurboFilter
-{
- @Override
- public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t)
- {
- // Suppresses the auth token warning from the YoutubeAudioSourceManager
- // https://github.com/jagrosh/MusicBot/pull/1490#issuecomment-1974070225
- if (logger.getName().equals("com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAccessTokenTracker")
- && format.equals("YouTube auth tokens can't be retrieved because email and password is not set in YoutubeAudioSourceManager, age restricted videos will throw exceptions.")
- ) {
- return FilterReply.DENY;
- }
-
- return FilterReply.NEUTRAL;
- }
-}
diff --git a/src/main/java/com/jagrosh/jmusicbot/utils/TimeUtil.java b/src/main/java/com/jagrosh/jmusicbot/utils/TimeUtil.java
new file mode 100644
index 000000000..081118713
--- /dev/null
+++ b/src/main/java/com/jagrosh/jmusicbot/utils/TimeUtil.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2020 John Grosh .
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jagrosh.jmusicbot.utils;
+
+public class TimeUtil
+{
+
+ public static String formatTime(long duration)
+ {
+ if(duration == Long.MAX_VALUE)
+ return "LIVE";
+ long seconds = Math.round(duration/1000.0);
+ long hours = seconds/(60*60);
+ seconds %= 60*60;
+ long minutes = seconds/60;
+ seconds %= 60;
+ return (hours>0 ? hours+":" : "") + (minutes<10 ? "0"+minutes : minutes) + ":" + (seconds<10 ? "0"+seconds : seconds);
+ }
+
+ /**
+ * Parses a seek time string into milliseconds and determines if it's relative.
+ * Supports "colon time" (HH:MM:SS) or "unit time" (1h20m)
+ * @param args time string
+ * @return SeekTime object, or null if the string could not be parsed
+ */
+ public static SeekTime parseTime(String args)
+ {
+ if (args.length() == 0) return null;
+ String timestamp = args;
+ boolean relative = false; // seek forward or backward
+ boolean isSeekingBackwards = false;
+ char first = timestamp.charAt(0);
+ if (first == '+' || first == '-')
+ {
+ relative = true;
+ isSeekingBackwards = first == '-';
+ timestamp = timestamp.substring(1);
+ }
+
+ long milliseconds = parseColonTime(timestamp);
+ if(milliseconds == -1) milliseconds = parseUnitTime(timestamp);
+ if(milliseconds == -1) return null;
+
+ milliseconds *= isSeekingBackwards ? -1 : 1;
+
+ return new SeekTime(milliseconds, relative);
+ }
+
+ /**
+ * @param timestamp timestamp formatted as: [+ | -] <HH:MM:SS | MM:SS | SS>
+ * @return Time in milliseconds
+ */
+ public static long parseColonTime(String timestamp)
+ {
+ String[] timestampSplitArray = timestamp.split(":+");
+ if(timestampSplitArray.length > 3 )
+ return -1;
+ double[] timeUnitArray = new double[3]; // hours, minutes, seconds
+ for(int index = 0; index < timestampSplitArray.length; index++)
+ {
+ String unit = timestampSplitArray[index];
+ if (unit.startsWith("+") || unit.startsWith("-")) return -1;
+ unit = unit.replace(",", ".");
+ try
+ {
+ timeUnitArray[index + 3 - timestampSplitArray.length] = Double.parseDouble(unit);
+ }
+ catch (NumberFormatException e)
+ {
+ return -1;
+ }
+ }
+ return Math.round(timeUnitArray[0] * 3600000 + timeUnitArray[1] * 60000 + timeUnitArray[2] * 1000);
+ }
+
+ /**
+ *
+ * @param timestr time string formatted as a unit time, e.g. 20m10, 1d5h20m14s or 1h and 20m
+ * @return Time in milliseconds
+ */
+ public static long parseUnitTime(String timestr)
+ {
+ timestr = timestr.replaceAll("(?i)(\\s|,|and)","")
+ .replaceAll("(?is)(-?\\d+|[a-z]+)", "$1 ")
+ .trim();
+ String[] vals = timestr.split("\\s+");
+ int time = 0;
+ try
+ {
+ for(int j=0; j j+1)
+ {
+ if(vals[j+1].toLowerCase().startsWith("m"))
+ num*=60;
+ else if(vals[j+1].toLowerCase().startsWith("h"))
+ num*=60*60;
+ else if(vals[j+1].toLowerCase().startsWith("d"))
+ num*=60*60*24;
+ }
+
+ time+=num*1000;
+ }
+ }
+ catch(Exception ex)
+ {
+ return -1;
+ }
+ return time;
+ }
+
+ public static class SeekTime
+ {
+ public final long milliseconds;
+ public final boolean relative;
+
+ private SeekTime(long milliseconds, boolean relative)
+ {
+ this.milliseconds = milliseconds;
+ this.relative = relative;
+ }
+ }
+}
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 1d03fcc22..4a7033f5d 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -14,6 +14,4 @@
-
-
diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf
index 19206d3ab..a4fda1e02 100644
--- a/src/main/resources/reference.conf
+++ b/src/main/resources/reference.conf
@@ -98,6 +98,15 @@ stayinchannel = false
maxtime = 0
+// This sets the maximum number of pages of songs that can be loaded from a YouTube
+// playlist. Each page can contain up to 100 tracks. Playing a playlist with more
+// pages than the maximum will stop loading after the provided number of pages.
+// For example, if the max was set to 15 and a playlist contained 1850 tracks,
+// only the first 1500 tracks (15 pages) would be loaded. By default, this is
+// set to 10 pages (1000 tracks).
+
+maxytplaylistpages = 10
+
// This sets the ratio of users that must vote to skip the currently playing song.
// Guild owners can define their own skip ratios, but this will be used if a guild
@@ -215,6 +224,7 @@ transforms = {}
// IF SOMEONE ASKS YOU TO ENABLE THIS, THERE IS AN 11/10 CHANCE THEY ARE TRYING TO SCAM YOU
eval=false
+evalengine="Nashorn"
/// END OF JMUSICBOT CONFIG ///
diff --git a/src/test/java/com/jagrosh/jmusicbot/TimeUtilTest.java b/src/test/java/com/jagrosh/jmusicbot/TimeUtilTest.java
new file mode 100644
index 000000000..781b9046d
--- /dev/null
+++ b/src/test/java/com/jagrosh/jmusicbot/TimeUtilTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2020 John Grosh .
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jagrosh.jmusicbot;
+
+
+import com.jagrosh.jmusicbot.utils.TimeUtil;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author Whew., Inc.
+ */
+public class TimeUtilTest
+{
+ @Test
+ public void singleDigit()
+ {
+ TimeUtil.SeekTime seek = TimeUtil.parseTime("5");
+ assertNotNull(seek);
+ assertEquals(5000, seek.milliseconds);
+ }
+
+ @Test
+ public void multipleDigits()
+ {
+ TimeUtil.SeekTime seek = TimeUtil.parseTime("99:9:999");
+ assertNotNull(seek);
+ assertEquals(357939000, seek.milliseconds);
+
+ seek = TimeUtil.parseTime("99h9m999s");
+ assertNotNull(seek);
+ assertEquals(357939000, seek.milliseconds);
+ }
+
+ @Test
+ public void decimalDigits()
+ {
+ TimeUtil.SeekTime seek = TimeUtil.parseTime("99.5:9.0:999.777");
+ assertNotNull(seek);
+ assertEquals(359739777, seek.milliseconds);
+ }
+
+ @Test
+ public void seeking()
+ {
+ TimeUtil.SeekTime seek = TimeUtil.parseTime("5");
+ assertNotNull(seek);
+ assertFalse(seek.relative);
+ assertEquals(5000, seek.milliseconds);
+ }
+
+ @Test
+ public void relativeSeekingForward()
+ {
+ TimeUtil.SeekTime seek = TimeUtil.parseTime("+5");
+ assertNotNull(seek);
+ assertTrue(seek.relative);
+ assertEquals(5000, seek.milliseconds);
+ }
+
+ @Test
+ public void relativeSeekingBackward()
+ {
+ TimeUtil.SeekTime seek = TimeUtil.parseTime("-5");
+ assertNotNull(seek);
+ assertTrue(seek.relative);
+ assertEquals(-5000, seek.milliseconds);
+ }
+
+ @Test
+ public void parseTimeArgumentLength()
+ {
+ TimeUtil.SeekTime seek = TimeUtil.parseTime("");
+ assertNull(seek);
+ }
+
+ @Test
+ public void timestampTotalUnits()
+ {
+ TimeUtil.SeekTime seek = TimeUtil.parseTime("1:1:1:1");
+ assertNull(seek);
+
+ seek = TimeUtil.parseTime("1h2m3m4s5s");
+ assertNotNull(seek);
+ assertEquals(3909000, seek.milliseconds);
+ }
+
+ @Test
+ public void relativeSymbol()
+ {
+ TimeUtil.SeekTime seek = TimeUtil.parseTime("+-1:-+1:+-1");
+ assertNull(seek);
+ }
+
+ @Test
+ public void timestampNumberFormat()
+ {
+ TimeUtil.SeekTime seek = TimeUtil.parseTime("1:1:a");
+ assertNull(seek);
+
+ seek = TimeUtil.parseTime("1a2s");
+ assertNotNull(seek);
+ assertEquals(3000, seek.milliseconds);
+ }
+}