/*
* This file is part of Popcorn Time.
*
* Popcorn Time 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.
*
* Popcorn Time 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 Popcorn Time. If not, see <http://www.gnu.org/licenses/>.
*/
package pct.droid.base.providers.media;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import com.google.gson.JsonSyntaxException;
import com.google.gson.internal.LinkedTreeMap;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Protocol;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import pct.droid.base.PopcornApplication;
import pct.droid.base.R;
import pct.droid.base.providers.media.models.Genre;
import pct.droid.base.providers.media.models.Media;
import pct.droid.base.providers.media.models.Movie;
import pct.droid.base.providers.subs.SubsProvider;
import pct.droid.base.providers.subs.YSubsProvider;
import pct.droid.base.utils.LocaleUtils;
public class YTSProvider extends MediaProvider {
private static final YTSProvider sMediaProvider = new YTSProvider();
private static Integer CURRENT_API = 0;
private static final String[][] API_URLS = {
{"https://yts.popcorntimece.ch/api/v2/", "json"}
};
public static String CURRENT_URL = API_URLS[CURRENT_API][0];
private static final SubsProvider sSubsProvider = new YSubsProvider();
private static Filters sFilters = new Filters();
@Override
protected OkHttpClient getClient() {
OkHttpClient client = super.getClient().clone();
// Use only HTTP 1.1 for YTS
List<Protocol> proto = new ArrayList<>();
proto.add(Protocol.HTTP_1_1);
client.setProtocols(proto);
return client;
}
@Override
protected Call enqueue(Request request, com.squareup.okhttp.Callback requestCallback) {
Context context = PopcornApplication.getAppContext();
PackageInfo pInfo;
String versionName = "0.0.0";
try {
pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
versionName = pInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
request = request.newBuilder().removeHeader("User-Agent").addHeader("User-Agent", String.format("Mozilla/5.0 (Linux; U; Android %s; %s; %s Build/%s) AppleWebkit/534.30 (KHTML, like Gecko) PT/%s", Build.VERSION.RELEASE, LocaleUtils.getCurrentAsString(), Build.MODEL, Build.DISPLAY, versionName)).build();
return super.enqueue(request, requestCallback);
}
@Override
public Call getList(final ArrayList<Media> existingList, Filters filters, final Callback callback) {
sFilters = filters;
final ArrayList<Media> currentList;
if (existingList == null) {
currentList = new ArrayList<>();
} else {
currentList = (ArrayList<Media>) existingList.clone();
}
ArrayList<NameValuePair> params = new ArrayList<>();
params.add(new NameValuePair("limit", "30"));
if (filters == null) {
filters = new Filters();
}
if (filters.keywords != null) {
params.add(new NameValuePair("query_term", filters.keywords));
}
if (filters.genre != null) {
params.add(new NameValuePair("genre", filters.genre));
}
if (filters.order == Filters.Order.ASC) {
params.add(new NameValuePair("order_by", "asc"));
} else {
params.add(new NameValuePair("order_by", "desc"));
}
if(filters.langCode != null) {
params.add(new NameValuePair("lang", filters.langCode));
}
String sort;
switch (filters.sort) {
default:
case POPULARITY:
sort = "seeds";
break;
case YEAR:
sort = "year";
break;
case DATE:
sort = "date_added";
break;
case RATING:
sort = "rating";
break;
case ALPHABET:
sort = "title";
break;
case TRENDING:
sort = "trending_score";
break;
}
params.add(new NameValuePair("sort_by", sort));
if (filters.page != null) {
params.add(new NameValuePair("page", Integer.toString(filters.page)));
}
Request.Builder requestBuilder = new Request.Builder();
String query = buildQuery(params);
requestBuilder.url(CURRENT_URL + "list_movies_pct." + API_URLS[CURRENT_API][1] + "?" + query);
requestBuilder.tag(MEDIA_CALL);
return fetchList(currentList, requestBuilder, filters, callback);
}
/**
* Fetch the list of movies from YTS
*
* @param currentList Current shown list to be extended
* @param requestBuilder Request to be executed
* @param callback Network callback
* @return Call
*/
private Call fetchList(final ArrayList<Media> currentList, final Request.Builder requestBuilder, final Filters filters, final Callback callback) {
return enqueue(requestBuilder.build(), new com.squareup.okhttp.Callback() {
@Override
public void onFailure(Request request, IOException e) {
String url = requestBuilder.build().urlString();
if (CURRENT_API >= API_URLS.length - 1) {
callback.onFailure(e);
} else {
if(url.contains(API_URLS[CURRENT_API][0])) {
url = url.replace(API_URLS[CURRENT_API][0], API_URLS[CURRENT_API + 1][0]);
url = url.replace(API_URLS[CURRENT_API][1], API_URLS[CURRENT_API + 1][1]);
CURRENT_API++;
} else {
url = url.replace(API_URLS[CURRENT_API - 1][0], API_URLS[CURRENT_API][0]);
url = url.replace(API_URLS[CURRENT_API - 1][1], API_URLS[CURRENT_API][1]);
}
requestBuilder.url(url);
fetchList(currentList, requestBuilder, filters, callback);
}
}
@Override
public void onResponse(Response response) throws IOException {
if (response.isSuccessful()) {
String responseStr;
try {
responseStr = response.body().string();
} catch (SocketException e) {
onFailure(response.request(), new IOException("Socket failed"));
return;
}
YTSReponse result;
try {
result = mGson.fromJson(responseStr, YTSReponse.class);
} catch (IllegalStateException e) {
onFailure(response.request(), new IOException("JSON Failed"));
return;
} catch (JsonSyntaxException e) {
onFailure(response.request(), new IOException("JSON Failed"));
return;
}
if(result == null) {
callback.onFailure(new NetworkErrorException("No response"));
} else if (result.status != null && result.status.equals("error")) {
callback.onFailure(new NetworkErrorException(result.status_message));
} else if(result.data != null && ((result.data.get("movies") != null && ((ArrayList<LinkedTreeMap<String, Object>>)result.data.get("movies")).size() <= 0) || ((Double)result.data.get("movie_count")).intValue() <= currentList.size())) {
callback.onFailure(new NetworkErrorException("No movies found"));
} else {
ArrayList<Media> formattedData = result.formatForPopcorn(currentList);
callback.onSuccess(filters, formattedData, true);
return;
}
}
onFailure(response.request(), new IOException("Couldn't connect to YTS"));
}
});
}
@Override
public Call getDetail(ArrayList<Media> currentList, Integer index, Callback callback) {
ArrayList<Media> returnList = new ArrayList<>();
returnList.add(currentList.get(index));
callback.onSuccess(null, returnList, true);
return null;
}
private class YTSReponse {
public String status;
public String status_message;
public LinkedTreeMap<String, Object> data;
/**
* Test if there is an item that already exists
*
* @param results List with items
* @param id Id of item to check for
* @return Return the index of the item in the results
*/
private int isInResults(ArrayList<Media> results, String id) {
int i = 0;
for (Media item : results) {
if (item.videoId.equals(id)) return i;
i++;
}
return -1;
}
/**
* Format data for the application
*
* @param existingList List to be extended
* @return List with items
*/
public ArrayList<Media> formatForPopcorn(ArrayList<Media> existingList) {
ArrayList<LinkedTreeMap<String, Object>> movies = new ArrayList<>();
if (data != null) {
movies = (ArrayList<LinkedTreeMap<String, Object>>) data.get("movies");
}
for (LinkedTreeMap<String, Object> item : movies) {
Movie movie = new Movie(sMediaProvider, sSubsProvider);
movie.videoId = (String) item.get("imdb_code");
movie.imdbId = movie.videoId;
int existingItem = isInResults(existingList, movie.videoId);
if (existingItem == -1) {
movie.title = (String) item.get("title_english");
Double year = (Double) item.get("year");
movie.year = Integer.toString(year.intValue());
movie.rating = item.get("rating").toString();
movie.genre = ((ArrayList<String>) item.get("genres")).get(0);
movie.image = ((String) item.get("medium_cover_image")).replace("medium", "large");
movie.headerImage = (String) item.get("background_image_original");
movie.trailer = "https://youtube.com/watch?v=" + item.get("yt_trailer_code");
Double runtime = (Double) item.get("runtime");
movie.runtime = Integer.toString(runtime.intValue());
movie.synopsis = (String) item.get("description_full");
movie.certification = (String) item.get("mpa_rating");
movie.fullImage = movie.image;
ArrayList<LinkedTreeMap<String, Object>> torrents =
(ArrayList<LinkedTreeMap<String, Object>>) item.get("torrents");
if (torrents != null) {
for (LinkedTreeMap<String, Object> torrentObj : torrents) {
String quality = (String) torrentObj.get("quality");
if (quality == null) continue;
Media.Torrent torrent = new Media.Torrent();
torrent.seeds = ((Double) torrentObj.get("seeds")).intValue();
torrent.peers = ((Double) torrentObj.get("peers")).intValue();
torrent.hash = (String) torrentObj.get("hash");
try {
torrent.url = "magnet:?xt=urn:btih:" + torrent.hash + "&dn=" + URLEncoder.encode(item.get("title").toString(), "utf-8") + "&tr=http://exodus.desync.com:6969/announce&tr=udp://tracker.openbittorrent.com:80/announce&tr=udp://open.demonii.com:1337/announce&tr=udp://exodus.desync.com:6969/announce&tr=udp://tracker.yify-torrents.com/announce";
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
torrent.url = (String) torrentObj.get("url");
}
movie.torrents.put(quality, torrent);
}
}
existingList.add(movie);
}
}
return existingList;
}
}
@Override
public int getLoadingMessage() {
return R.string.loading_movies;
}
@Override
public List<NavInfo> getNavigation() {
List<NavInfo> tabs = new ArrayList<>();
tabs.add(new NavInfo(R.id.yts_filter_trending,Filters.Sort.TRENDING, Filters.Order.DESC, PopcornApplication.getAppContext().getString(R.string.trending),R.drawable.yts_filter_trending));
tabs.add(new NavInfo(R.id.yts_filter_popular_now,Filters.Sort.POPULARITY, Filters.Order.DESC, PopcornApplication.getAppContext().getString(R.string.popular),R.drawable.yts_filter_popular_now));
tabs.add(new NavInfo(R.id.yts_filter_top_rated,Filters.Sort.RATING, Filters.Order.DESC, PopcornApplication.getAppContext().getString(R.string.top_rated),R.drawable.yts_filter_top_rated));
tabs.add(new NavInfo(R.id.yts_filter_release_date,Filters.Sort.DATE, Filters.Order.DESC, PopcornApplication.getAppContext().getString(R.string.release_date),R.drawable.yts_filter_release_date));
tabs.add(new NavInfo(R.id.yts_filter_year,Filters.Sort.YEAR, Filters.Order.DESC, PopcornApplication.getAppContext().getString(R.string.year),R.drawable.yts_filter_year));
tabs.add(new NavInfo(R.id.yts_filter_a_to_z,Filters.Sort.ALPHABET, Filters.Order.ASC, PopcornApplication.getAppContext().getString(R.string.a_to_z),R.drawable.yts_filter_a_to_z));
return tabs;
}
@Override
public List<Genre> getGenres() {
List<Genre> returnList = new ArrayList<>();
returnList.add(new Genre(null, R.string.genre_all));
returnList.add(new Genre("action", R.string.genre_action));
returnList.add(new Genre("adventure", R.string.genre_adventure));
returnList.add(new Genre("animation", R.string.genre_animation));
returnList.add(new Genre("biography", R.string.genre_biography));
returnList.add(new Genre("comedy", R.string.genre_comedy));
returnList.add(new Genre("crime", R.string.genre_crime));
returnList.add(new Genre("documentary", R.string.genre_documentary));
returnList.add(new Genre("drama", R.string.genre_drama));
returnList.add(new Genre("family", R.string.genre_family));
returnList.add(new Genre("fantasy", R.string.genre_fantasy));
returnList.add(new Genre("filmnoir", R.string.genre_film_noir));
returnList.add(new Genre("history", R.string.genre_history));
returnList.add(new Genre("horror", R.string.genre_horror));
returnList.add(new Genre("music", R.string.genre_music));
returnList.add(new Genre("musical", R.string.genre_musical));
returnList.add(new Genre("mystery", R.string.genre_mystery));
returnList.add(new Genre("romance", R.string.genre_romance));
returnList.add(new Genre("scifi", R.string.genre_sci_fi));
returnList.add(new Genre("short", R.string.genre_short));
returnList.add(new Genre("sport", R.string.genre_sport));
returnList.add(new Genre("thriller", R.string.genre_thriller));
returnList.add(new Genre("war", R.string.genre_war));
returnList.add(new Genre("western", R.string.genre_western));
return returnList;
}
}