package org.transdroid.search.rarbg; import android.content.SharedPreferences; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.params.CoreProtocolPNames; import org.json.JSONArray; import org.json.JSONObject; import org.transdroid.search.BuildConfig; import org.transdroid.search.ISearchAdapter; import org.transdroid.search.SearchResult; import org.transdroid.search.SortOrder; import org.transdroid.util.HttpHelper; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; /** * An adapter that provides access to RARBG searches via their official rarbg/torrentapi JSON api. Project: https://github.com/rarbg/torrentapi Doc: * https://torrentapi.org/apidocs_v2.txt */ public class RarbgAdapter implements ISearchAdapter { private static final String BASE_URL = "https://torrentapi.org/pubapi_v2.php"; // Access is provided using a token, requested via get_token and valid for up to 15 minutes private static String accessToken; private HttpClient httpclient; @Override public String getSiteName() { return "RARBG"; } @Override public boolean isPrivateSite() { return false; } @Override public boolean usesToken() { return false; } @Override public List<SearchResult> search(SharedPreferences prefs, String query, SortOrder order, int maxResults) throws Exception { if (httpclient == null) { httpclient = HttpHelper.buildDefaultSearchHttpClient(false); httpclient.getParams().setParameter(CoreProtocolPNames.USER_AGENT, "Torrent Search (by Transdroid) " + BuildConfig.VERSION_NAME); } if (accessToken == null) { requestAccessToken(); } List<SearchResult> results = performSearch(query, order); if (results == null) { // Special case: our access token was invalid; request a new token and try again accessToken = null; requestAccessToken(); results = performSearch(query, order); if (results == null) throw new IOException("RARBG returned a problem with out access token, even after requesting a new one and retrying"); } return results; } private void requestAccessToken() throws Exception { // Ask for a new token HttpResponse response = httpclient.execute(new HttpGet(BASE_URL + "?get_token=get_token")); if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { InputStream instream = response.getEntity().getContent(); String error = HttpHelper.convertStreamToString(instream); instream.close(); throw new IOException( "RARBG did not supply us with a token to their torrentapi: HTTP " + response.getStatusLine().getStatusCode() + ": " + error); } // Read JSON response InputStream instream = response.getEntity().getContent(); String json = HttpHelper.convertStreamToString(instream); instream.close(); JSONObject structure = new JSONObject(json); if (structure.has("token")) { accessToken = structure.getString("token"); return; } throw new IOException("RARBG did not supply us with a token to their torrentapi: instead we got: " + json); } private List<SearchResult> performSearch(String query, SortOrder order) throws Exception { // Ask for extended search results String q = String.format(Locale.US, "?mode=search&search_string=%1$s&ranked=0&sort=%2$s&format=json_extended&token=%3$s", URLEncoder.encode(query, "UTF-8"), (order == SortOrder.BySeeders ? "seeders" : "last"), accessToken); HttpResponse response = httpclient.execute(new HttpGet(BASE_URL + q)); // Read JSON response InputStream instream = response.getEntity().getContent(); String json = HttpHelper.convertStreamToString(instream); instream.close(); JSONObject structure = new JSONObject(json); // Check for error reponses if (structure.has("error_code") && structure.getInt("error_code") == 2) { // Null: we need to refresh our access token return null; } if (structure.has("error_code") && structure.getInt("error_code") == 20) { // No results found return new ArrayList<>(); } if (structure.has("error")) { // Critical error: throw exception with the message throw new IOException("RARBG error: " + structure.getString("error")); } // Parse results List<SearchResult> results = new ArrayList<>(); JSONArray array = structure.getJSONArray("torrent_results"); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", Locale.US); for (int i = 0; i < array.length(); i++) { JSONObject item = array.getJSONObject(i); long sizeBytes = item.getLong("size"); String size; if (sizeBytes > 1024 * 1024 * 1024) { size = String.format(Locale.getDefault(), "%1$.1f GB", sizeBytes / (1024D * 1024D * 1024D)); } else if (sizeBytes > 1024 * 1024) { size = String.format(Locale.getDefault(), "%1$.1f MB", sizeBytes / (1024D * 1024D)); } else if (sizeBytes > 1024) { size = String.format(Locale.getDefault(), "%1$.1f kB", sizeBytes / 1024D); } else { size = String.format(Locale.getDefault(), "%1$.1f B", (double) sizeBytes); } Date date = null; try { date = dateFormat.parse(item.getString("pubdate")); } catch (Exception e) { // Ignore; we rather have no date and results than stop on this error } results.add(new SearchResult( item.getString("title"), item.getString("download"), item.getString("info_page"), size, date, item.getInt("seeders"), item.getInt("leechers"))); } return results; } @Override public String buildRssFeedUrlFromSearch(String query, SortOrder order) { // Not supported by RARABG return null; } @Override public InputStream getTorrentFile(SharedPreferences prefs, String url) throws Exception { // Only for private sites return null; } }