Try multiple manual download formats before falling back to auto, and display the bitrate and download size to the user.
This commit is contained in:
parent
83bb9e5805
commit
2bd51ea856
|
@ -36,6 +36,7 @@ public class Downloader implements Consumer<Song>
|
|||
|
||||
private static final int BITRATE_TARGET = 64_000;
|
||||
private static final Pattern FORMAT_PATTERN = Pattern.compile("^([\\w]+)\\s+([\\w]+)\\s+(\\w+ ?\\w*)\\s+(.*)$");
|
||||
public static final Pattern DESTINATION_PATTERN = Pattern.compile("Destination: (.*\\.wav)");
|
||||
|
||||
private final List<Song> downloadQueue = new LinkedList<>();
|
||||
private final LinkedBlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>();
|
||||
|
@ -62,9 +63,8 @@ public class Downloader implements Consumer<Song>
|
|||
{
|
||||
try
|
||||
{
|
||||
List<Format> formats = getFormats(song);
|
||||
Format chosenFormat = chooseFormat(formats);
|
||||
download(song, chosenFormat);
|
||||
getFormats(song);
|
||||
download(song);
|
||||
} catch (Exception ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
|
@ -78,19 +78,20 @@ public class Downloader implements Consumer<Song>
|
|||
this.next = next;
|
||||
}
|
||||
|
||||
private Format chooseFormat(List<Format> formats)
|
||||
private void chooseFormats(Song song)
|
||||
{
|
||||
List<Format> formats = song.getFormats();
|
||||
if (formats.isEmpty())
|
||||
return null;
|
||||
System.out.println("Choosing from " + formats.size() + " formats:");
|
||||
System.out.println(formats);
|
||||
return;
|
||||
// System.out.println("Choosing from " + formats.size() + " formats:");
|
||||
// System.out.println(formats);
|
||||
formats.sort((Format a, Format b) ->
|
||||
{
|
||||
// audio only preferred to video
|
||||
System.out.println("sort entered; a=" + a.toString() + " b=" + b.toString());
|
||||
// System.out.println("sort entered; a=" + a.toString() + " b=" + b.toString());
|
||||
int comp = 0;
|
||||
comp = Boolean.compare(a.isAudioOnly(), b.isAudioOnly());
|
||||
System.out.println("\tCompared on audio only: " + comp);
|
||||
// System.out.println("\tCompared on audio only: " + comp);
|
||||
if (comp == 0)
|
||||
{
|
||||
// known preferred to unknown
|
||||
|
@ -105,7 +106,7 @@ public class Downloader implements Consumer<Song>
|
|||
int aDist = Math.abs(a.getBitrate() - BITRATE_TARGET);
|
||||
int bDist = Math.abs(b.getBitrate() - BITRATE_TARGET);
|
||||
comp = Integer.compare(bDist, aDist);
|
||||
System.out.println("\tCompared on bitrate distance: " + comp);
|
||||
// System.out.println("\tCompared on bitrate distance: " + comp);
|
||||
}
|
||||
}
|
||||
if (comp == 0)
|
||||
|
@ -120,17 +121,17 @@ public class Downloader implements Consumer<Song>
|
|||
else // smaller is better
|
||||
{
|
||||
comp = Long.compare(b.getSize(), a.getSize());
|
||||
System.out.println("\tCompared on filesize: " + comp);
|
||||
// System.out.println("\tCompared on filesize: " + comp);
|
||||
}
|
||||
}
|
||||
System.out.println("\tOverall: " + comp);
|
||||
return comp;
|
||||
// System.out.println("\tOverall: " + comp);
|
||||
return -comp;
|
||||
});
|
||||
System.out.println("Sorting done! Formats:" + formats);
|
||||
return formats.get(formats.size() - 1);
|
||||
song.setFormats(formats);
|
||||
// System.out.println("Sorting done! Formats:" + formats);
|
||||
}
|
||||
|
||||
private List<Format> getFormats(Song song)
|
||||
private void getFormats(Song song)
|
||||
{
|
||||
//
|
||||
try
|
||||
|
@ -155,15 +156,16 @@ public class Downloader implements Consumer<Song>
|
|||
continue;
|
||||
formats.add(new Format(split[0], split[1], split[2], split[3]));
|
||||
}
|
||||
song.setFormats(formats);
|
||||
// Matcher matcher = FORMAT_PATTERN.matcher(output);
|
||||
// while (matcher.find())
|
||||
// formats.add(new Format(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4)));
|
||||
return formats;
|
||||
// return formats;
|
||||
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Logger.getLogger(Downloader.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return List.of();
|
||||
// return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,15 +195,19 @@ public class Downloader implements Consumer<Song>
|
|||
return downloadDir;
|
||||
}
|
||||
|
||||
private void download(Song song, Format format)
|
||||
private void download(Song song)
|
||||
{
|
||||
final String formatCodes = format != null ? format.getCode() + "/" : "";
|
||||
chooseFormats(song);
|
||||
String formatCodes = "";
|
||||
final List<Format> formats = song.getFormats();
|
||||
for (int i = 0; i < 3 && i < song.getFormats().size(); i++)
|
||||
formatCodes += formats.get(i).getCode() + "/";
|
||||
|
||||
try
|
||||
{
|
||||
messageHandler.accept(song, null);
|
||||
String cmd = "/usr/bin/youtube-dl -x"
|
||||
+ " -f " + formatCodes + "worstaudio/bestaudio/worst/best "
|
||||
+ " -f " + formatCodes + "worstaudio/bestaudio/worst/best"
|
||||
+ " --audio-format=wav"
|
||||
+ " --no-playlist"
|
||||
+ " -o " + getDownloadDir().getAbsolutePath() + "/%(title)s.%(ext)s "
|
||||
|
@ -212,7 +218,7 @@ public class Downloader implements Consumer<Song>
|
|||
String output = new String(in.readAllBytes(), Charset.defaultCharset());
|
||||
String error = new String(exec.getErrorStream().readAllBytes(), Charset.defaultCharset());
|
||||
System.out.println(output);
|
||||
Matcher matcher = Pattern.compile("Destination: (.*\\.wav)").matcher(output);
|
||||
Matcher matcher = DESTINATION_PATTERN.matcher(output);
|
||||
if (matcher.find())
|
||||
song.setLocation(new File(matcher.group(1)));
|
||||
else if (exec.exitValue() != 0)
|
||||
|
|
|
@ -12,6 +12,8 @@ import java.util.*;
|
|||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import moe.nekojimi.chords.commands.Command;
|
||||
import moe.nekojimi.chords.commands.JoinCommand;
|
||||
import moe.nekojimi.musicsearcher.Query;
|
||||
import moe.nekojimi.musicsearcher.Result;
|
||||
import moe.nekojimi.musicsearcher.providers.MetaSearcher;
|
||||
|
@ -41,6 +43,8 @@ public class Main extends ListenerAdapter
|
|||
private final Searcher searcher;
|
||||
private JDA jda;
|
||||
|
||||
private final Map<String, Command> commands = new HashMap<>();
|
||||
|
||||
private VoiceChannel currentVoiceChannel = null;
|
||||
|
||||
private List<Result> lastSearchResults;
|
||||
|
@ -91,12 +95,27 @@ public class Main extends ListenerAdapter
|
|||
if (song.getLocation() != null)
|
||||
channel.sendMessage(/*bracketNo + */"Finished downloading " + song + " for " + song.getRequestedBy() + ", added to queue!").queue();
|
||||
else
|
||||
channel.sendMessage(/*bracketNo + */"Now downloading " + song + " for " + song.getRequestedBy() + " ...").queue();
|
||||
{
|
||||
Format format = song.getBestFormat();
|
||||
String formatDetails = "";
|
||||
if (format != null)
|
||||
{
|
||||
formatDetails = " (" + format.getBitrate() / 1000 + "k, " + String.format("%.2f", format.getSize() / (1024.0 * 1024.0)) + "MiB)";
|
||||
}
|
||||
channel.sendMessage(/*bracketNo + */"Now downloading " + song + formatDetails + " for " + song.getRequestedBy() + " ...").queue();
|
||||
}
|
||||
else
|
||||
channel.sendMessage(/*bracketNo + */"Failed to download " + song + " for " + song.getRequestedBy() + "! Reason: " + ex.getMessage()).queue();
|
||||
|
||||
});
|
||||
searcher = MetaSearcher.loadYAML(new File("searchproviders.yml"));
|
||||
|
||||
addCommand(new JoinCommand(this));
|
||||
}
|
||||
|
||||
private void addCommand(Command command)
|
||||
{
|
||||
commands.put(command.getKeyword(), command);
|
||||
}
|
||||
|
||||
public void setJda(JDA jda)
|
||||
|
@ -122,17 +141,30 @@ public class Main extends ListenerAdapter
|
|||
|
||||
try
|
||||
{
|
||||
String[] split = content.split("\\s+", 2);
|
||||
String cmd = split[0].toLowerCase();
|
||||
|
||||
if (!cmd.startsWith("!"))
|
||||
return; // doesn't start with prefix char
|
||||
|
||||
cmd = cmd.substring(1); // strip prefix char
|
||||
|
||||
String arg = "";
|
||||
if (content.contains(" "))
|
||||
arg = content.split(" ", 2)[1];
|
||||
if (split.length > 1)
|
||||
arg = split[1];
|
||||
|
||||
if (content.startsWith("!join "))
|
||||
onJoinCommand(event, guild, arg);
|
||||
if (commands.containsKey(cmd))
|
||||
{
|
||||
Command command = commands.get(cmd);
|
||||
command.call(event, List.of(arg));
|
||||
}
|
||||
|
||||
// else if (content.startsWith("!join "))
|
||||
// onJoinCommand(event, guild, arg);
|
||||
else if (content.equals("!leave"))
|
||||
onLeaveCommand(event);
|
||||
else if (content.equals("!join"))
|
||||
onJoinCommand(event);
|
||||
// else if (content.equals("!join"))
|
||||
// onJoinCommand(event);
|
||||
else if (content.startsWith("!play "))
|
||||
onPlayCommand(event, guild, arg);
|
||||
else if (content.startsWith("!queue"))
|
||||
|
@ -157,20 +189,20 @@ public class Main extends ListenerAdapter
|
|||
* @param event
|
||||
* The event for this command
|
||||
*/
|
||||
private void onJoinCommand(GuildMessageReceivedEvent event)
|
||||
{
|
||||
// Note: None of these can be null due to our configuration with the JDABuilder!
|
||||
Member member = event.getMember(); // Member is the context of the user for the specific guild, containing voice state and roles
|
||||
GuildVoiceState voiceState = member.getVoiceState(); // Check the current voice state of the user
|
||||
VoiceChannel channel = voiceState.getChannel(); // Use the channel the user is currently connected to
|
||||
// if (channel != null)
|
||||
// {
|
||||
// connectTo(channel); // Join the channel of the user
|
||||
// onConnecting(channel, event.getChannel()); // Tell the user about our success
|
||||
// } else
|
||||
// onUnknownChannel(event.getChannel(), "your voice channel"); // Tell the user about our failure
|
||||
onJoinCommand(event, member.getGuild(), channel.getName());
|
||||
}
|
||||
// private void onJoinCommand(GuildMessageReceivedEvent event)
|
||||
// {
|
||||
// // Note: None of these can be null due to our configuration with the JDABuilder!
|
||||
// Member member = event.getMember(); // Member is the context of the user for the specific guild, containing voice state and roles
|
||||
// GuildVoiceState voiceState = member.getVoiceState(); // Check the current voice state of the user
|
||||
// VoiceChannel channel = voiceState.getChannel(); // Use the channel the user is currently connected to
|
||||
//// if (channel != null)
|
||||
//// {
|
||||
//// connectTo(channel); // Join the channel of the user
|
||||
//// onConnecting(channel, event.getChannel()); // Tell the user about our success
|
||||
//// } else
|
||||
//// onUnknownChannel(event.getChannel(), "your voice channel"); // Tell the user about our failure
|
||||
// onJoinCommand(event, member.getGuild(), channel.getName());
|
||||
// }
|
||||
|
||||
/**
|
||||
* Handle command with arguments.
|
||||
|
@ -182,35 +214,34 @@ public class Main extends ListenerAdapter
|
|||
* @param arg
|
||||
* The input argument
|
||||
*/
|
||||
private void onJoinCommand(GuildMessageReceivedEvent event, Guild guild, String arg)
|
||||
{
|
||||
boolean isNumber = arg.matches("\\d+"); // This is a regular expression that ensures the input consists of digits
|
||||
VoiceChannel channel = null;
|
||||
if (isNumber) // The input is an id?
|
||||
channel = guild.getVoiceChannelById(arg);
|
||||
if (channel == null) // Then the input must be a name?
|
||||
{
|
||||
List<VoiceChannel> channels = guild.getVoiceChannelsByName(arg, true);
|
||||
if (!channels.isEmpty()) // Make sure we found at least one exact match
|
||||
channel = channels.get(0); // We found a channel! This cannot be null.
|
||||
}
|
||||
|
||||
TextChannel textChannel = event.getChannel();
|
||||
if (channel == null) // I have no idea what you want mr user
|
||||
{
|
||||
onUnknownChannel(textChannel, arg); // Let the user know about our failure
|
||||
return;
|
||||
}
|
||||
connectTo(channel); // We found a channel to connect to!
|
||||
onConnecting(channel, textChannel); // Let the user know, we were successful!
|
||||
}
|
||||
// private void onJoinCommand(GuildMessageReceivedEvent event, Guild guild, String arg)
|
||||
// {
|
||||
// boolean isNumber = arg.matches("\\d+"); // This is a regular expression that ensures the input consists of digits
|
||||
// VoiceChannel channel = null;
|
||||
// if (isNumber) // The input is an id?
|
||||
// channel = guild.getVoiceChannelById(arg);
|
||||
// if (channel == null) // Then the input must be a name?
|
||||
// {
|
||||
// List<VoiceChannel> channels = guild.getVoiceChannelsByName(arg, true);
|
||||
// if (!channels.isEmpty()) // Make sure we found at least one exact match
|
||||
// channel = channels.get(0); // We found a channel! This cannot be null.
|
||||
// }
|
||||
//
|
||||
// TextChannel textChannel = event.getChannel();
|
||||
// if (channel == null) // I have no idea what you want mr user
|
||||
// {
|
||||
// onUnknownChannel(textChannel, arg); // Let the user know about our failure
|
||||
// return;
|
||||
// }
|
||||
// connectTo(channel); // We found a channel to connect to!
|
||||
// onConnecting(channel, textChannel); // Let the user know, we were successful!
|
||||
// }
|
||||
|
||||
private void onLeaveCommand(GuildMessageReceivedEvent event)
|
||||
{
|
||||
if (currentVoiceChannel != null)
|
||||
{
|
||||
disconnect();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -404,43 +435,19 @@ public class Main extends ListenerAdapter
|
|||
+ "!remove <Index> - Remove the song at position <Index> from the queue.\n"
|
||||
+ "!skip - Skip the current song and play the next one.\n"
|
||||
+ "!restart - Try playing the current song again in case it goes wrong.\n";
|
||||
// for (String key: commands.keySet())
|
||||
// {
|
||||
// help += "!" + key + ":"
|
||||
// }
|
||||
event.getChannel().sendMessage(help).queue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform user about successful connection.
|
||||
*
|
||||
* @param channel
|
||||
* The voice channel we connected to
|
||||
* @param textChannel
|
||||
* The text channel to send the message in
|
||||
*/
|
||||
private void onConnecting(VoiceChannel channel, TextChannel textChannel)
|
||||
{
|
||||
textChannel.sendMessage("Connecting to " + channel.getName()).queue(); // never forget to queue()!
|
||||
}
|
||||
|
||||
/**
|
||||
* The channel to connect to is not known to us.
|
||||
*
|
||||
* @param channel
|
||||
* The message channel (text channel abstraction) to send failure
|
||||
* information to
|
||||
* @param comment
|
||||
* The information of this channel
|
||||
*/
|
||||
private void onUnknownChannel(MessageChannel channel, String comment)
|
||||
{
|
||||
channel.sendMessage("Unable to connect to ``" + comment + "``, no such channel!").queue(); // never forget to queue()!
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to requested channel and start echo handler
|
||||
*
|
||||
* @param channel
|
||||
* The channel to connect to
|
||||
*/
|
||||
private void connectTo(VoiceChannel channel)
|
||||
public void connectTo(VoiceChannel channel)
|
||||
{
|
||||
Guild guild = channel.getGuild();
|
||||
// Get an audio manager for this guild, this will be created upon first use for each guild
|
||||
|
@ -460,7 +467,7 @@ public class Main extends ListenerAdapter
|
|||
currentVoiceChannel = channel;
|
||||
}
|
||||
|
||||
private void disconnect()
|
||||
public void disconnect()
|
||||
{
|
||||
if (currentVoiceChannel != null)
|
||||
{
|
||||
|
@ -473,4 +480,39 @@ public class Main extends ListenerAdapter
|
|||
}
|
||||
}
|
||||
|
||||
public MusicHandler getMusicHandler()
|
||||
{
|
||||
return musicHandler;
|
||||
}
|
||||
|
||||
public Downloader getDownloader()
|
||||
{
|
||||
return downloader;
|
||||
}
|
||||
|
||||
public Searcher getSearcher()
|
||||
{
|
||||
return searcher;
|
||||
}
|
||||
|
||||
public JDA getJda()
|
||||
{
|
||||
return jda;
|
||||
}
|
||||
|
||||
public VoiceChannel getCurrentVoiceChannel()
|
||||
{
|
||||
return currentVoiceChannel;
|
||||
}
|
||||
|
||||
public List<Result> getLastSearchResults()
|
||||
{
|
||||
return lastSearchResults;
|
||||
}
|
||||
|
||||
public int getTrackNumber()
|
||||
{
|
||||
return trackNumber;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ package moe.nekojimi.chords;
|
|||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -19,6 +21,7 @@ public class Song
|
|||
private final URL url;
|
||||
private File location = null;
|
||||
private int number;
|
||||
private List<Format> formats = new ArrayList<>();
|
||||
|
||||
private String requestedBy;
|
||||
private String requestedIn;
|
||||
|
@ -127,4 +130,22 @@ public class Song
|
|||
return "[" + number + "] " + ret;
|
||||
// return url.toExternalForm();
|
||||
}
|
||||
|
||||
public List<Format> getFormats()
|
||||
{
|
||||
return formats;
|
||||
}
|
||||
|
||||
public void setFormats(List<Format> formats)
|
||||
{
|
||||
this.formats = formats;
|
||||
}
|
||||
|
||||
public Format getBestFormat()
|
||||
{
|
||||
if (formats.isEmpty())
|
||||
return null;
|
||||
return formats.get(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue