mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-26 16:47:30 +00:00
implement app feed endpoint instead of browser
This commit is contained in:
parent
71264bef96
commit
22fc894c9d
@ -1,34 +1,55 @@
|
|||||||
package awais.instagrabber.asyncs;
|
package awais.instagrabber.asyncs;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import awais.instagrabber.customviews.helpers.PostFetcher;
|
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||||
import awais.instagrabber.interfaces.FetchListener;
|
import awais.instagrabber.interfaces.FetchListener;
|
||||||
import awais.instagrabber.models.FeedModel;
|
import awais.instagrabber.models.FeedModel;
|
||||||
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||||
import awais.instagrabber.webservices.GraphQLService;
|
import awais.instagrabber.utils.Constants;
|
||||||
|
import awais.instagrabber.utils.CookieUtils;
|
||||||
|
import awais.instagrabber.webservices.FeedService;
|
||||||
import awais.instagrabber.webservices.ServiceCallback;
|
import awais.instagrabber.webservices.ServiceCallback;
|
||||||
|
|
||||||
|
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||||
|
|
||||||
public class FeedPostFetchService implements PostFetcher.PostFetchService {
|
public class FeedPostFetchService implements PostFetcher.PostFetchService {
|
||||||
private static final String TAG = "FeedPostFetchService";
|
private static final String TAG = "FeedPostFetchService";
|
||||||
private final GraphQLService graphQLService;
|
private final FeedService feedService;
|
||||||
private String nextCursor;
|
private String nextCursor;
|
||||||
private boolean hasNextPage;
|
private boolean hasNextPage;
|
||||||
|
|
||||||
public FeedPostFetchService() {
|
public FeedPostFetchService() {
|
||||||
graphQLService = GraphQLService.getInstance();
|
feedService = FeedService.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||||
graphQLService.fetchFeed(25, nextCursor, new ServiceCallback<PostsFetchResponse>() {
|
final List<FeedModel> feedModels = new ArrayList<>();
|
||||||
|
final String cookie = settingsHelper.getString(Constants.COOKIE);
|
||||||
|
final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie);
|
||||||
|
feedModels.clear();
|
||||||
|
feedService.fetch(csrfToken, nextCursor, new ServiceCallback<PostsFetchResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final PostsFetchResponse result) {
|
public void onSuccess(final PostsFetchResponse result) {
|
||||||
if (result == null) return;
|
if (result == null && feedModels.size() > 0) {
|
||||||
|
fetchListener.onResult(feedModels);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (result == null) return;
|
||||||
nextCursor = result.getNextCursor();
|
nextCursor = result.getNextCursor();
|
||||||
hasNextPage = result.hasNextPage();
|
hasNextPage = result.hasNextPage();
|
||||||
|
feedModels.addAll(result.getFeedModels());
|
||||||
if (fetchListener != null) {
|
if (fetchListener != null) {
|
||||||
fetchListener.onResult(result.getFeedModels());
|
if (feedModels.size() < 15 && hasNextPage) {
|
||||||
|
feedService.fetch(csrfToken, nextCursor, this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fetchListener.onResult(feedModels);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package awais.instagrabber.repositories;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.FieldMap;
|
||||||
|
import retrofit2.http.FormUrlEncoded;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
|
||||||
|
public interface FeedRepository {
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/api/v1/feed/timeline/")
|
||||||
|
Call<String> fetch(@FieldMap final Map<String, String> signedForm);
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
package awais.instagrabber.webservices;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import awais.instagrabber.models.FeedModel;
|
||||||
|
import awais.instagrabber.models.PostChild;
|
||||||
|
import awais.instagrabber.models.ProfileModel;
|
||||||
|
import awais.instagrabber.models.enums.MediaItemType;
|
||||||
|
import awais.instagrabber.repositories.FeedRepository;
|
||||||
|
import awais.instagrabber.repositories.responses.PostsFetchResponse;
|
||||||
|
import awais.instagrabber.utils.Constants;
|
||||||
|
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||||
|
import awais.instagrabber.utils.TextUtils;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
import retrofit2.Retrofit;
|
||||||
|
|
||||||
|
public class FeedService extends BaseService {
|
||||||
|
private static final String TAG = "FeedService";
|
||||||
|
|
||||||
|
private final FeedRepository repository;
|
||||||
|
|
||||||
|
private static FeedService instance;
|
||||||
|
|
||||||
|
private FeedService() {
|
||||||
|
final Retrofit retrofit = getRetrofitBuilder()
|
||||||
|
.baseUrl("https://i.instagram.com")
|
||||||
|
.build();
|
||||||
|
repository = retrofit.create(FeedRepository.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FeedService getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new FeedService();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fetch(final String csrfToken,
|
||||||
|
final String cursor,
|
||||||
|
final ServiceCallback<PostsFetchResponse> callback) {
|
||||||
|
final Map<String, String> form = new HashMap<>();
|
||||||
|
form.put("_uuid", UUID.randomUUID().toString());
|
||||||
|
form.put("_csrftoken", csrfToken);
|
||||||
|
form.put("phone_id", UUID.randomUUID().toString());
|
||||||
|
form.put("device_id", UUID.randomUUID().toString());
|
||||||
|
form.put("client_session_id", UUID.randomUUID().toString());
|
||||||
|
form.put("is_prefetch", "0");
|
||||||
|
form.put("timezone_offset", String.valueOf(TimeZone.getDefault().getRawOffset() / 1000));
|
||||||
|
if (!TextUtils.isEmpty(cursor)) {
|
||||||
|
form.put("max_id", cursor);
|
||||||
|
form.put("reason", "pagination");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
form.put("is_pull_to_refresh", "1");
|
||||||
|
form.put("reason", "pull_to_refresh");
|
||||||
|
}
|
||||||
|
final Call<String> request = repository.fetch(form);
|
||||||
|
request.enqueue(new Callback<String>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
|
||||||
|
try {
|
||||||
|
// Log.d(TAG, "onResponse: body: " + response.body());
|
||||||
|
final PostsFetchResponse postsFetchResponse = parseResponse(response);
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onSuccess(postsFetchResponse);
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(TAG, "onResponse", e);
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onFailure(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private PostsFetchResponse parseResponse(@NonNull final Response<String> response) throws JSONException {
|
||||||
|
if (TextUtils.isEmpty(response.body())) {
|
||||||
|
Log.e(TAG, "parseResponse: feed response body is empty with status code: " + response.code());
|
||||||
|
return new PostsFetchResponse(Collections.emptyList(), false, null);
|
||||||
|
}
|
||||||
|
return parseResponseBody(response.body());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private PostsFetchResponse parseResponseBody(@NonNull final String body)
|
||||||
|
throws JSONException {
|
||||||
|
final JSONObject root = new JSONObject(body);
|
||||||
|
final boolean moreAvailable = root.optBoolean("more_available");
|
||||||
|
final String nextMaxId = root.optString("next_max_id");
|
||||||
|
final JSONArray feedItems = root.optJSONArray("items");
|
||||||
|
final List<FeedModel> feedModels = new ArrayList<>();
|
||||||
|
for (int i = 0; i < feedItems.length(); ++i) {
|
||||||
|
final JSONObject itemJson = feedItems.optJSONObject(i);
|
||||||
|
if (itemJson == null || itemJson.has("injected")
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final FeedModel feedModel = ResponseBodyUtils.parseItem(itemJson);
|
||||||
|
if (feedModel != null) {
|
||||||
|
feedModels.add(feedModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new PostsFetchResponse(feedModels, moreAvailable, nextMaxId);
|
||||||
|
}
|
||||||
|
}
|
@ -88,42 +88,6 @@ public class GraphQLService extends BaseService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fetchFeed(final int maxItemsToLoad,
|
|
||||||
final String cursor,
|
|
||||||
final ServiceCallback<PostsFetchResponse> callback) {
|
|
||||||
if (loadFromMock) {
|
|
||||||
final Handler handler = new Handler();
|
|
||||||
handler.postDelayed(() -> {
|
|
||||||
final ClassLoader classLoader = getClass().getClassLoader();
|
|
||||||
if (classLoader == null) {
|
|
||||||
Log.e(TAG, "fetch: classLoader is null!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try (InputStream resourceAsStream = classLoader.getResourceAsStream("feed_response.json");
|
|
||||||
Reader in = new InputStreamReader(resourceAsStream, StandardCharsets.UTF_8)) {
|
|
||||||
final int bufferSize = 1024;
|
|
||||||
final char[] buffer = new char[bufferSize];
|
|
||||||
final StringBuilder out = new StringBuilder();
|
|
||||||
int charsRead;
|
|
||||||
while ((charsRead = in.read(buffer, 0, buffer.length)) > 0) {
|
|
||||||
out.append(buffer, 0, charsRead);
|
|
||||||
}
|
|
||||||
callback.onSuccess(parseResponseBody(out.toString(), Constants.EXTRAS_USER, "edge_web_feed_timeline"));
|
|
||||||
} catch (IOException | JSONException e) {
|
|
||||||
Log.e(TAG, "fetch: ", e);
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetch("c699b185975935ae2a457f24075de8c7",
|
|
||||||
"{\"fetch_media_item_count\":" + maxItemsToLoad + "," +
|
|
||||||
"\"fetch_like\":3,\"has_stories\":false,\"has_stories\":false,\"has_threaded_comments\":true," +
|
|
||||||
"\"fetch_media_item_cursor\":\"" + (cursor == null ? "" : cursor) + "\"}",
|
|
||||||
Constants.EXTRAS_USER,
|
|
||||||
"edge_web_feed_timeline",
|
|
||||||
callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void fetchLocationPosts(@NonNull final String locationId,
|
public void fetchLocationPosts(@NonNull final String locationId,
|
||||||
final String maxId,
|
final String maxId,
|
||||||
final ServiceCallback<PostsFetchResponse> callback) {
|
final ServiceCallback<PostsFetchResponse> callback) {
|
||||||
|
@ -89,8 +89,6 @@ public class LocationService extends BaseService {
|
|||||||
final JSONObject root = new JSONObject(body);
|
final JSONObject root = new JSONObject(body);
|
||||||
final boolean moreAvailable = root.optBoolean("more_available");
|
final boolean moreAvailable = root.optBoolean("more_available");
|
||||||
final String nextMaxId = root.optString("next_max_id");
|
final String nextMaxId = root.optString("next_max_id");
|
||||||
final int numResults = root.optInt("num_results");
|
|
||||||
final String status = root.optString("status");
|
|
||||||
final JSONArray itemsJson = root.optJSONArray("items");
|
final JSONArray itemsJson = root.optJSONArray("items");
|
||||||
final List<FeedModel> items = parseItems(itemsJson);
|
final List<FeedModel> items = parseItems(itemsJson);
|
||||||
return new PostsFetchResponse(
|
return new PostsFetchResponse(
|
||||||
|
@ -228,8 +228,6 @@ public class ProfileService extends BaseService {
|
|||||||
final JSONObject root = new JSONObject(body);
|
final JSONObject root = new JSONObject(body);
|
||||||
final boolean moreAvailable = root.optBoolean("more_available");
|
final boolean moreAvailable = root.optBoolean("more_available");
|
||||||
final String nextMaxId = root.optString("next_max_id");
|
final String nextMaxId = root.optString("next_max_id");
|
||||||
final int numResults = root.optInt("num_results");
|
|
||||||
final String status = root.optString("status");
|
|
||||||
final JSONArray itemsJson = root.optJSONArray("items");
|
final JSONArray itemsJson = root.optJSONArray("items");
|
||||||
final List<FeedModel> items = parseItems(itemsJson, isInMedia);
|
final List<FeedModel> items = parseItems(itemsJson, isInMedia);
|
||||||
return new PostsFetchResponse(
|
return new PostsFetchResponse(
|
||||||
|
@ -158,8 +158,6 @@ public class TagsService extends BaseService {
|
|||||||
final JSONObject root = new JSONObject(body);
|
final JSONObject root = new JSONObject(body);
|
||||||
final boolean moreAvailable = root.optBoolean("more_available");
|
final boolean moreAvailable = root.optBoolean("more_available");
|
||||||
final String nextMaxId = root.optString("next_max_id");
|
final String nextMaxId = root.optString("next_max_id");
|
||||||
final int numResults = root.optInt("num_results");
|
|
||||||
final String status = root.optString("status");
|
|
||||||
final JSONArray itemsJson = root.optJSONArray("items");
|
final JSONArray itemsJson = root.optJSONArray("items");
|
||||||
final List<FeedModel> items = parseItems(itemsJson);
|
final List<FeedModel> items = parseItems(itemsJson);
|
||||||
return new PostsFetchResponse(
|
return new PostsFetchResponse(
|
||||||
|
Loading…
Reference in New Issue
Block a user