Skip to content

Commit

Permalink
Added Ripple download server.
Browse files Browse the repository at this point in the history
Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
  • Loading branch information
itdelatrisu committed Jul 3, 2017
1 parent cf52c2f commit 8300af9
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 0 deletions.
15 changes: 15 additions & 0 deletions src/itdelatrisu/opsu/downloads/Download.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ public interface DownloadListener {
/** The additional HTTP request headers. */
private Map<String, String> requestHeaders;

/** Whether SSL certificate validation should be disabled. */
private boolean disableSSLCertValidation = false;

/** The readable byte channel. */
private ReadableByteChannelWrapper rbc;

Expand Down Expand Up @@ -181,6 +184,12 @@ public Download(String remoteURL, String localPath, String rename) {
*/
public void setRequestHeaders(Map<String, String> headers) { this.requestHeaders = headers; }

/**
* Switches validation of SSL certificates on or off.
* @param enabled whether to validate SSL certificates
*/
public void setSSLCertValidation(boolean enabled) { this.disableSSLCertValidation = !enabled; }

/**
* Starts the download from the "waiting" status.
* @return the started download thread, or {@code null} if none started
Expand All @@ -195,6 +204,9 @@ public void run() {
// open connection
HttpURLConnection conn = null;
try {
if (disableSSLCertValidation)
Utils.setSSLCertValidation(false);

URL downloadURL = url;
int redirectCount = 0;
boolean isRedirect = false;
Expand Down Expand Up @@ -254,6 +266,9 @@ else if (redirectCount > MAX_REDIRECTS)
if (listener != null)
listener.error();
return;
} finally {
if (disableSSLCertValidation)
Utils.setSSLCertValidation(true);
}

// download file
Expand Down
1 change: 1 addition & 0 deletions src/itdelatrisu/opsu/downloads/DownloadNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ public void error() {
}
});
download.setRequestHeaders(server.getDownloadRequestHeaders());
download.setSSLCertValidation(!server.disableSSLInDownloads());
this.download = download;
if (Options.useUnicodeMetadata()) // load glyphs
Fonts.loadGlyphs(Fonts.LARGE, getTitle());
Expand Down
6 changes: 6 additions & 0 deletions src/itdelatrisu/opsu/downloads/servers/DownloadServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ public String getPreviewURL(int beatmapSetID) {
*/
public boolean isDownloadInBrowser() { return false; }

/**
* Returns whether SSL certificate validation should be disabled for downloads.
* @return true if validation should be disabled
*/
public boolean disableSSLInDownloads() { return false; }

@Override
public String toString() { return getName(); }
}
141 changes: 141 additions & 0 deletions src/itdelatrisu/opsu/downloads/servers/RippleServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* opsu! - an open-source osu! client
* Copyright (C) 2014-2017 Jeffrey Han
*
* opsu! is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opsu! is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
*/

package itdelatrisu.opsu.downloads.servers;

import itdelatrisu.opsu.ErrorHandler;
import itdelatrisu.opsu.Utils;
import itdelatrisu.opsu.downloads.DownloadNode;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.newdawn.slick.util.Log;

/**
* Download server: https://ripple.moe/
*/
public class RippleServer extends DownloadServer {
/** Server name. */
private static final String SERVER_NAME = "Ripple";

/** Formatted download URL: {@code beatmapSetID} */
private static final String DOWNLOAD_URL = "https://storage.ripple.moe/%d.osz";

/** Formatted search URL: {@code query,amount,offset} */
private static final String SEARCH_URL = "https://storage.ripple.moe/api/search?query=%s&mode=0&amount=%d&offset=%d";

/**
* Maximum beatmaps displayed per page.
* Supports up to 100, but response sizes become very large (>100KB).
*/
private static final int PAGE_LIMIT = 20;

/** Total result count from the last query. */
private int totalResults = -1;

/** Constructor. */
public RippleServer() {}

@Override
public String getName() { return SERVER_NAME; }

@Override
public String getDownloadURL(int beatmapSetID) {
return String.format(DOWNLOAD_URL, beatmapSetID);
}

@Override
public DownloadNode[] resultList(String query, int page, boolean rankedOnly) throws IOException {
DownloadNode[] nodes = null;
try {
Utils.setSSLCertValidation(false);

// read JSON
int offset = (page - 1) * PAGE_LIMIT;
String search = String.format(SEARCH_URL, URLEncoder.encode(query, "UTF-8"), PAGE_LIMIT, offset);
if (rankedOnly)
search += "&status=1";
JSONObject json = Utils.readJsonObjectFromUrl(new URL(search));
if (json == null || !json.has("Ok") || !json.getBoolean("Ok") || !json.has("Sets") || json.isNull("Sets")) {
this.totalResults = -1;
return null;
}

// parse result list
JSONArray arr = json.getJSONArray("Sets");
nodes = new DownloadNode[arr.length()];
for (int i = 0; i < nodes.length; i++) {
JSONObject item = arr.getJSONObject(i);
nodes[i] = new DownloadNode(
item.getInt("SetID"), formatDate(item.getString("LastUpdate")),
item.getString("Title"), null, item.getString("Artist"), null,
item.getString("Creator")
);
}

// store total result count
int resultCount = nodes.length + offset;
if (nodes.length == PAGE_LIMIT)
resultCount++;
this.totalResults = resultCount;
} catch (MalformedURLException | UnsupportedEncodingException e) {
ErrorHandler.error(String.format("Problem loading result list for query '%s'.", query), e, true);
} catch (JSONException e) {
Log.error(e);
} finally {
Utils.setSSLCertValidation(true);
}
return nodes;
}

@Override
public int minQueryLength() { return 3; }

@Override
public int totalResults() { return totalResults; }

@Override
public boolean disableSSLInDownloads() { return true; }

/**
* Returns a formatted date string from a raw date.
* @param s the raw date string (e.g. "2015-09-30T09:39:04Z")
* @return the formatted date, or the raw string if it could not be parsed
*/
private String formatDate(String s) {
try {
DateFormat f = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Date d = f.parse(s);
DateFormat fmt = new SimpleDateFormat("d MMM yyyy HH:mm:ss");
return fmt.format(d);
} catch (StringIndexOutOfBoundsException | ParseException e) {
return s;
}
}
}
2 changes: 2 additions & 0 deletions src/itdelatrisu/opsu/states/DownloadsMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import itdelatrisu.opsu.downloads.DownloadNode;
import itdelatrisu.opsu.downloads.servers.BloodcatServer;
import itdelatrisu.opsu.downloads.servers.DownloadServer;
import itdelatrisu.opsu.downloads.servers.RippleServer;
import itdelatrisu.opsu.downloads.servers.MengSkyServer;
import itdelatrisu.opsu.downloads.servers.MnetworkServer;
import itdelatrisu.opsu.options.Options;
Expand Down Expand Up @@ -87,6 +88,7 @@ public class DownloadsMenu extends BasicGameState {
/** Available beatmap download servers. */
private static final DownloadServer[] SERVERS = {
new MengSkyServer(),
new RippleServer(),
new MnetworkServer(),
new BloodcatServer()
};
Expand Down

0 comments on commit 8300af9

Please # to comment.