/*
* This file is part of Transdroid Torrent Search
* <http://code.google.com/p/transdroid-search/>
*
* Transdroid Torrent Search is free software: you can redistribute
* it and/or modify it under the terms of the GNU Lesser General
* Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later
* version.
*
* Transdroid Torrent Search 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Transdroid. If not, see <http://www.gnu.org/licenses/>.
*/
package org.transdroid.search.hdtorrents;
import java.io.InputStream;
import java.net.URLEncoder;
import java.security.InvalidParameterException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.security.auth.login.LoginException;
import org.apache.http.HttpResponse;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.transdroid.search.ISearchAdapter;
import org.transdroid.search.SearchResult;
import org.transdroid.search.SortOrder;
import org.transdroid.search.TorrentSite;
import org.transdroid.search.gui.SettingsHelper;
import org.transdroid.util.HttpHelper;
import android.content.SharedPreferences;
import android.util.Log;
/**
* An adapter that provides access to HD-Torrents.org searches by parsing the raw HTML output.
*/
public class HdTorrentsAdapter implements ISearchAdapter {
private static final String LOG_TAG = HdTorrentsAdapter.class.getName();
private static final String LOGIN_URL = "https://hd-torrents.org/login.php";
private static final String LOGIN_POST_USERNAME = "uid";
private static final String LOGIN_POST_PASSWORD = "pwd";
private static final String SEARCH_URL = "https://hd-torrents.org/torrents.php?search=%s&active=1&options=0";
private static final String SEARCH_SORT_BY_SEEDERS_SUFFIX = "&order=seeds&by=DESC";
private static final String START_STRING = "nd();\">";
private static final String END_STRING = "<a href=\"torrent_history.php?";
private static final String TORRENT_STRING = ".torrent";
private static final String DATE_START_SEARCH_STRING = "add_wishlist_star.png border=0 alt=\"torrent\"/></A></TD>";
private static final String DATE_START_STRING = "<b";
private static final String DATE_END_STRING = "><b";
private static final String SIZE_START_STRING = "mainblockcontent\">";
private static final String SIZE_END_STRING = "</td>";
private static final String PEER_START_STRING = "Click here to view peers details\"><b>";
private static final String PEER_END_STRING = "</b>";
private static final String IMDB_START_STRING = "http://www.imdb.com/";
private static final String IMDB_END_STRING = "\"";
private static final String URL_PREFIX = "https://hd-torrents.org/";
private static final int CONNECTION_TIMEOUT = 8000;
// =========================================================
// ISearchAdapter
// =========================================================
@Override
public String getSiteName() {
return "HD-Torrents";
}
@Override
public boolean isPrivateSite() {
return true;
}
@Override
public boolean usesToken() {
return false;
}
@Override
public List<SearchResult> search(SharedPreferences prefs, String query, SortOrder order, int maxResults) throws Exception {
DefaultHttpClient client = prepareRequest(prefs);
// build search query
String encodedQuery = URLEncoder.encode(query, "UTF-8");
String url = String.format(SEARCH_URL, encodedQuery);
// if (order == SortOrder.BySeeders) url += SEARCH_SORT_BY_SEEDERS_SUFFIX;
// make request
Log.d(LOG_TAG, "Executing search request from: " + url);
HttpResponse response = client.execute(new HttpGet(url));
// parse HTML response into a list of torrents
String html = HttpHelper.convertStreamToString(response.getEntity().getContent());
return parseHtml(html, maxResults);
}
@Override
public String buildRssFeedUrlFromSearch(String query, SortOrder order) {
// no rss based search for HD-Torrents.org. there is a live RSS feed for all
// torrents on the site but it does not provide search capability
return null;
}
@Override
public InputStream getTorrentFile(SharedPreferences prefs, String url) throws Exception {
// Provide an authenticated file handle to the requested url
DefaultHttpClient client = prepareRequest(prefs);
HttpResponse response = client.execute(new HttpGet(url));
return response.getEntity().getContent();
}
// =========================================================
// LOGIN LOGIC
// =========================================================
private DefaultHttpClient prepareRequest(SharedPreferences prefs) throws Exception {
Log.d(LOG_TAG, "Preparing login attempt.");
// retrieve stored login info
String username = SettingsHelper.getSiteUser(prefs, TorrentSite.HdTorrents);
String password = SettingsHelper.getSitePass(prefs, TorrentSite.HdTorrents);
// verify we have login credentials. does this ever get hit?
if (username == null || password == null) {
throw new InvalidParameterException(
"No username or password was provided, while this is required for this private site.");
}
// setup our http client
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
HttpConnectionParams.setSoTimeout(params, CONNECTION_TIMEOUT);
DefaultHttpClient client = new DefaultHttpClient(params);
// login to HD-Torrents.org populating the HttpClient with the required cookies
login(client, username, password);
return client;
}
/**
* Attempts to log in to HD-Torrents.org with the given credentials. On success
* the given DefaultHttpClient should hold all required cookies to access
* the site.
*/
private void login(DefaultHttpClient client, String username, String password) throws Exception {
Log.d(LOG_TAG, "Attempting to login.");
HttpPost request = new HttpPost(LOGIN_URL);
request.setEntity(new UrlEncodedFormEntity(Arrays
.asList(new BasicNameValuePair[] {
new BasicNameValuePair(LOGIN_POST_USERNAME, username),
new BasicNameValuePair(LOGIN_POST_PASSWORD, password) })));
client.execute(request);
// verify we have the cookies needed to log in
boolean success = false, uid = false, pass = false, hash = false;
for (Cookie cookie : client.getCookieStore().getCookies()) {
if ("uid".equals(cookie.getName())) uid = true;
if ("pass".equals(cookie.getName())) pass = true;
if ("hashx".equals(cookie.getName())) hash = true;
}
// if we don't have the correct cookies, login failed. notify user with a toast and toss an exception.
success = uid && pass && hash;
if (!success) {
Log.e(LOG_TAG, "Failed to log into HD-Torrents as '" + username + "'. Did not receive expected login cookies!");
throw new LoginException("Failed to log into HD-Torrents as '" + username + "'. Did not receive expected login cookies!");
}
Log.d(LOG_TAG, "Successfully logged in to HD-Torrents");
}
// =========================================================
// SEARCH LOGIC
// =========================================================
protected List<SearchResult> parseHtml(String html, int maxResults) throws Exception {
Log.d(LOG_TAG, "Parsing search results");
List<SearchResult> results = new ArrayList<SearchResult>();
final DateFormat parseDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
int resultStart = html.indexOf(START_STRING) + START_STRING.length();
while (resultStart >= 0 && results.size() < maxResults) {
int resultEnd = html.indexOf(END_STRING, resultStart);
while (resultEnd - resultStart > 5000) {
resultStart = html.indexOf(START_STRING, resultStart + START_STRING.length() + 2) + START_STRING.length();
resultEnd = html.indexOf(END_STRING, resultStart);
}
String itemString = html.substring(resultStart, resultEnd + END_STRING.length());
String title = null;
try {
title = itemString.substring(0, itemString.indexOf("</A>"));
} catch (Exception e) {}
String downloadUrl = null;
try {
int downloadStart = itemString.indexOf("=download") + 1;
int downloadEnd = itemString.indexOf(TORRENT_STRING) + TORRENT_STRING.length();
downloadUrl = URL_PREFIX + itemString.substring(downloadStart, downloadEnd);
} catch (Exception e) {}
Date date = null;
int seeders = -1;
int leechers = -1;
String size = null;
try {
int dateSearchStart = itemString.indexOf(DATE_START_SEARCH_STRING) + DATE_START_SEARCH_STRING.length();
String dateSearchString = itemString.substring(dateSearchStart, itemString.length());
int dateEnd = dateSearchString.indexOf(DATE_END_STRING);
String dateString = dateSearchString.substring(dateSearchString.indexOf(DATE_START_STRING) + DATE_START_STRING.length(), dateEnd);
date = parseDateFormat.parse(dateString);
int sizeStart = itemString.indexOf(SIZE_START_STRING, dateSearchStart + dateEnd) + SIZE_START_STRING.length();
int sizeEnd = itemString.indexOf(SIZE_END_STRING, sizeStart);
size = itemString.substring(sizeStart, sizeEnd);
int seedStart = itemString.indexOf(PEER_START_STRING, sizeEnd) + PEER_START_STRING.length();
int seedEnd = itemString.indexOf(PEER_END_STRING, seedStart);
int leechStart = itemString.indexOf(PEER_START_STRING, seedEnd) + PEER_START_STRING.length();
int leechEnd = itemString.indexOf(PEER_END_STRING, leechStart);
String leechersString = itemString.substring(leechStart, leechEnd);
String seedersString = itemString.substring(seedStart, seedEnd);
seeders = Integer.parseInt(seedersString);
leechers = Integer.parseInt(leechersString);
} catch (Exception e) {}
String imdbString = null;
try {
int imbdStart = itemString.indexOf(IMDB_START_STRING);
if (imbdStart >= 10) {
imdbString = itemString.substring(imbdStart, itemString.indexOf(IMDB_END_STRING, imbdStart));
}
} catch (Exception e) {}
if (title != null && downloadUrl != null) {
SearchResult result = new SearchResult(title, downloadUrl, imdbString, size, date, seeders, leechers);
results.add(result);
}
int nextResultStart = html.indexOf(START_STRING, resultEnd) + START_STRING.length();
if (nextResultStart < resultStart) {
resultStart = -1;
}
else {
resultStart = nextResultStart;
}
}
return results;
}
}