Use Download class to download track previews into a Temp/ directory.

AudioSystem.getAudioInputStream(URL) was causing issues, so just download the files using the more robust Download class.

Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2016-10-13 04:12:47 -04:00
parent 89e8341686
commit dabfb827ee
7 changed files with 93 additions and 36 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
/Skins/ /Skins/
/SongPacks/ /SongPacks/
/Songs/ /Songs/
/Temp/
/.opsu.log /.opsu.log
/.opsu.cfg /.opsu.cfg
/.opsu.db* /.opsu.db*

View File

@ -63,6 +63,7 @@ The following files and folders will be created by opsu! as needed:
files within this directory to the replay directory and saves the scores in files within this directory to the replay directory and saves the scores in
the scores database. Replays can be imported from osu! as well as opsu!. the scores database. Replays can be imported from osu! as well as opsu!.
* `Natives/`: The native libraries directory. * `Natives/`: The native libraries directory.
* `Temp/`: The temporary files directory. Deleted when opsu! exits.
## Building ## Building
opsu! is distributed as both a [Maven](https://maven.apache.org/) and opsu! is distributed as both a [Maven](https://maven.apache.org/) and

View File

@ -27,11 +27,15 @@ import itdelatrisu.opsu.downloads.Updater;
import itdelatrisu.opsu.render.CurveRenderState; import itdelatrisu.opsu.render.CurveRenderState;
import itdelatrisu.opsu.ui.UI; import itdelatrisu.opsu.ui.UI;
import java.io.IOException;
import org.codehaus.plexus.util.FileUtils;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
import org.newdawn.slick.AppGameContainer; import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.Game; import org.newdawn.slick.Game;
import org.newdawn.slick.SlickException; import org.newdawn.slick.SlickException;
import org.newdawn.slick.opengl.InternalTextureLoader; import org.newdawn.slick.opengl.InternalTextureLoader;
import org.newdawn.slick.util.Log;
/** /**
* AppGameContainer extension that sends critical errors to ErrorHandler. * AppGameContainer extension that sends critical errors to ErrorHandler.
@ -142,6 +146,15 @@ public class Container extends AppGameContainer {
if (!Options.isWatchServiceEnabled()) if (!Options.isWatchServiceEnabled())
BeatmapWatchService.destroy(); BeatmapWatchService.destroy();
BeatmapWatchService.removeListeners(); BeatmapWatchService.removeListeners();
// delete temporary directory
if (Options.TEMP_DIR.isDirectory()) {
try {
FileUtils.deleteDirectory(Options.TEMP_DIR);
} catch (IOException e) {
Log.warn(String.format("Failed to delete temp dir: %s", Options.TEMP_DIR.getAbsolutePath()), e);
}
}
} }
@Override @Override

View File

@ -94,6 +94,9 @@ public class Options {
/** Directory where natives are unpacked. */ /** Directory where natives are unpacked. */
public static final File NATIVE_DIR = new File(CACHE_DIR, "Natives/"); public static final File NATIVE_DIR = new File(CACHE_DIR, "Natives/");
/** Directory where temporary files are stored (deleted on exit). */
public static final File TEMP_DIR = new File(CACHE_DIR, "Temp/");
/** Font file name. */ /** Font file name. */
public static final String FONT_NAME = "DroidSansFallback.ttf"; public static final String FONT_NAME = "DroidSansFallback.ttf";

View File

@ -22,6 +22,9 @@ import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Options; import itdelatrisu.opsu.Options;
import itdelatrisu.opsu.audio.HitSound.SampleSet; import itdelatrisu.opsu.audio.HitSound.SampleSet;
import itdelatrisu.opsu.beatmap.HitObject; import itdelatrisu.opsu.beatmap.HitObject;
import itdelatrisu.opsu.downloads.Download;
import itdelatrisu.opsu.downloads.Download.DownloadListener;
import itdelatrisu.opsu.ui.UI;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -345,24 +348,58 @@ public class SoundController {
} }
/** /**
* Plays a track from a URL. * Plays a track from a remote URL.
* If a track is currently playing, it will be stopped. * If a track is currently playing, it will be stopped.
* @param url the resource URL * @param url the remote URL
* @param name the track file name
* @param isMP3 true if MP3, false if WAV * @param isMP3 true if MP3, false if WAV
* @param listener the line listener * @param listener the line listener
* @return the MultiClip being played * @return true if playing, false otherwise
* @throws SlickException if any error occurred * @throws SlickException if any error occurred
*/ */
public static synchronized MultiClip playTrack(URL url, boolean isMP3, LineListener listener) throws SlickException { public static synchronized boolean playTrack(String url, String name, boolean isMP3, LineListener listener)
throws SlickException {
// stop previous track
stopTrack(); stopTrack();
try {
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url); // download new track
currentTrack = loadClip(url.getFile(), audioIn, isMP3); File dir = Options.TEMP_DIR;
playClip(currentTrack, Options.getMusicVolume() * Options.getMasterVolume(), listener); if (!dir.isDirectory())
return currentTrack; dir.mkdir();
} catch (Exception e) { String filename = String.format("%s.%s", name, isMP3 ? "mp3" : "wav");
throw new SlickException(String.format("Failed to load clip '%s'.", url.getFile(), e)); final File downloadFile = new File(dir, filename);
boolean complete;
if (downloadFile.isFile()) {
complete = true; // file already downloaded
} else {
Download download = new Download(url, downloadFile.getAbsolutePath());
download.setListener(new DownloadListener() {
@Override
public void completed() {}
@Override
public void error() {
UI.sendBarNotification("Failed to download track preview.");
}
});
try {
download.start().join();
} catch (InterruptedException e) {}
complete = (download.getStatus() == Download.Status.COMPLETE);
} }
// play the track
if (complete) {
try {
AudioInputStream audioIn = AudioSystem.getAudioInputStream(downloadFile);
currentTrack = loadClip(filename, audioIn, isMP3);
playClip(currentTrack, Options.getMusicVolume() * Options.getMasterVolume(), listener);
return true;
} catch (Exception e) {
throw new SlickException(String.format("Failed to load clip '%s'.", url));
}
}
return false;
} }
/** /**

View File

@ -167,12 +167,13 @@ public class Download {
/** /**
* Starts the download from the "waiting" status. * Starts the download from the "waiting" status.
* @return the started download thread, or {@code null} if none started
*/ */
public void start() { public Thread start() {
if (status != Status.WAITING) if (status != Status.WAITING)
return; return null;
new Thread() { Thread t = new Thread() {
@Override @Override
public void run() { public void run() {
// open connection // open connection
@ -274,7 +275,9 @@ public class Download {
listener.error(); listener.error();
} }
} }
}.start(); };
t.start();
return t;
} }
/** /**

View File

@ -47,8 +47,6 @@ import itdelatrisu.opsu.ui.UI;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener; import javax.sound.sampled.LineListener;
@ -663,15 +661,18 @@ public class DownloadsMenu extends BasicGameState {
SoundController.stopTrack(); SoundController.stopTrack();
} else { } else {
// play preview // play preview
try { final String url = serverMenu.getSelectedItem().getPreviewURL(node.getID());
final URL url = new URL(serverMenu.getSelectedItem().getPreviewURL(node.getID())); MusicController.pause();
MusicController.pause(); new Thread() {
new Thread() { @Override
@Override public void run() {
public void run() { try {
try { previewID = -1;
previewID = -1; boolean playing = SoundController.playTrack(
SoundController.playTrack(url, true, new LineListener() { url,
Integer.toString(node.getID()),
true,
new LineListener() {
@Override @Override
public void update(LineEvent event) { public void update(LineEvent event) {
if (event.getType() == LineEvent.Type.STOP) { if (event.getType() == LineEvent.Type.STOP) {
@ -681,18 +682,16 @@ public class DownloadsMenu extends BasicGameState {
} }
} }
} }
}); }
);
if (playing)
previewID = node.getID(); previewID = node.getID();
} catch (SlickException e) { } catch (SlickException e) {
UI.sendBarNotification("Failed to load track preview. See log for details."); UI.sendBarNotification("Failed to load track preview. See log for details.");
Log.error(e); Log.error(e);
}
} }
}.start(); }
} catch (MalformedURLException e) { }.start();
UI.sendBarNotification("Could not load track preview (bad URL).");
Log.error(e);
}
} }
return; return;
} }