From 49f41f46548b75766e166698f4c829b98d29ac0c Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Sun, 20 Dec 2020 13:35:16 -0500 Subject: [PATCH] api refactor --- .../asyncs/FeedPostFetchService.java | 8 +- .../asyncs/HashtagPostFetchService.java | 19 +- .../asyncs/HighlightsFetcher.java | 73 ----- .../asyncs/LocationPostFetchService.java | 19 +- .../asyncs/ProfilePostFetchService.java | 27 +- .../asyncs/SavedPostFetchService.java | 24 +- .../fragments/SavedViewerFragment.java | 12 +- .../fragments/main/ProfileFragment.java | 33 ++- ...Repository.java => GraphQLRepository.java} | 2 +- .../repositories/LocationRepository.java | 3 - .../repositories/ProfileRepository.java | 9 +- .../repositories/StoriesRepository.java | 7 +- .../repositories/TagsRepository.java | 3 - .../{FeedService.java => GraphQLService.java} | 158 ++++++---- .../webservices/LocationService.java | 191 +------------ .../webservices/ProfileService.java | 270 ++++-------------- .../webservices/StoriesService.java | 50 +++- .../instagrabber/webservices/TagsService.java | 187 +----------- 18 files changed, 320 insertions(+), 775 deletions(-) delete mode 100755 app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java rename app/src/main/java/awais/instagrabber/repositories/{FeedRepository.java => GraphQLRepository.java} (87%) rename app/src/main/java/awais/instagrabber/webservices/{FeedService.java => GraphQLService.java} (55%) diff --git a/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java index d2bba0ce..a3792921 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/FeedPostFetchService.java @@ -6,22 +6,22 @@ import awais.instagrabber.customviews.helpers.PostFetcher; import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.FeedModel; import awais.instagrabber.repositories.responses.PostsFetchResponse; -import awais.instagrabber.webservices.FeedService; +import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.ServiceCallback; public class FeedPostFetchService implements PostFetcher.PostFetchService { private static final String TAG = "FeedPostFetchService"; - private final FeedService feedService; + private final GraphQLService graphQLService; private String nextCursor; private boolean hasNextPage; public FeedPostFetchService() { - feedService = FeedService.getInstance(); + graphQLService = GraphQLService.getInstance(); } @Override public void fetch(final FetchListener> fetchListener) { - feedService.fetch(25, nextCursor, new ServiceCallback() { + graphQLService.fetchFeed(25, nextCursor, new ServiceCallback() { @Override public void onSuccess(final PostsFetchResponse result) { if (result == null) return; diff --git a/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java index 4c12f16e..55f1bc40 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java @@ -6,12 +6,14 @@ import awais.instagrabber.customviews.helpers.PostFetcher; import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.HashtagModel; +import awais.instagrabber.repositories.responses.PostsFetchResponse; +import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.TagsService; -import awais.instagrabber.webservices.TagsService.TagPostsFetchResponse; public class HashtagPostFetchService implements PostFetcher.PostFetchService { private final TagsService tagsService; + private final GraphQLService graphQLService; private final HashtagModel hashtagModel; private String nextMaxId; private boolean moreAvailable; @@ -20,19 +22,20 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService { public HashtagPostFetchService(final HashtagModel hashtagModel, final boolean isLoggedIn) { this.hashtagModel = hashtagModel; this.isLoggedIn = isLoggedIn; - tagsService = TagsService.getInstance(); + tagsService = isLoggedIn ? TagsService.getInstance() : null; + graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); } @Override public void fetch(final FetchListener> fetchListener) { - final ServiceCallback cb = new ServiceCallback() { + final ServiceCallback cb = new ServiceCallback() { @Override - public void onSuccess(final TagPostsFetchResponse result) { + public void onSuccess(final PostsFetchResponse result) { if (result == null) return; - nextMaxId = result.getNextMaxId(); - moreAvailable = result.isMoreAvailable(); + nextMaxId = result.getNextCursor(); + moreAvailable = result.hasNextPage(); if (fetchListener != null) { - fetchListener.onResult(result.getItems()); + fetchListener.onResult(result.getFeedModels()); } } @@ -45,7 +48,7 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService { } }; if (isLoggedIn) tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb); - else tagsService.fetchGraphQLPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb); + else graphQLService.fetchHashtagPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb); } @Override diff --git a/app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java deleted file mode 100755 index acb7b7c1..00000000 --- a/app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java +++ /dev/null @@ -1,73 +0,0 @@ -package awais.instagrabber.asyncs; - -import android.os.AsyncTask; -import android.util.Log; - -import org.json.JSONArray; -import org.json.JSONObject; - -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -import awais.instagrabber.BuildConfig; -import awais.instagrabber.interfaces.FetchListener; -import awais.instagrabber.models.HighlightModel; -import awais.instagrabber.utils.Constants; -import awais.instagrabber.utils.NetworkUtils; - -public final class HighlightsFetcher extends AsyncTask> { - private final String id; - private final FetchListener> fetchListener; - - public HighlightsFetcher(final String id, final FetchListener> fetchListener) { - this.id = id; - this.fetchListener = fetchListener; - } - - @Override - protected List doInBackground(final Void... voids) { - List result = null; - String url = "https://i.instagram.com/api/v1/highlights/" + id + "/highlights_tray/"; - - try { - HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); - conn.setInstanceFollowRedirects(false); - conn.setUseCaches(false); - conn.setRequestProperty("User-Agent", Constants.I_USER_AGENT); - conn.connect(); - - if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - final JSONArray highlightsReel = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONArray("tray"); - - final int length = highlightsReel.length(); - final List highlightModels = new ArrayList<>(); - // final String[] highlightIds = new String[length]; - for (int i = 0; i < length; ++i) { - final JSONObject highlightNode = highlightsReel.getJSONObject(i); - highlightModels.add(new HighlightModel( - highlightNode.getString("title"), - highlightNode.getString(Constants.EXTRAS_ID), - highlightNode.getJSONObject("cover_media") - .getJSONObject("cropped_image_version") - .getString("url") - )); - } - conn.disconnect(); - result = highlightModels; - } - - conn.disconnect(); - } catch (Exception e) { - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); - } - - return result; - } - - @Override - protected void onPostExecute(final List result) { - if (fetchListener != null) fetchListener.onResult(result); - } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java index 2b504c50..7c7a12ce 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java @@ -6,12 +6,14 @@ import awais.instagrabber.customviews.helpers.PostFetcher; import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.LocationModel; +import awais.instagrabber.repositories.responses.PostsFetchResponse; +import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.LocationService; -import awais.instagrabber.webservices.LocationService.LocationPostsFetchResponse; import awais.instagrabber.webservices.ServiceCallback; public class LocationPostFetchService implements PostFetcher.PostFetchService { private final LocationService locationService; + private final GraphQLService graphQLService; private final LocationModel locationModel; private String nextMaxId; private boolean moreAvailable; @@ -20,19 +22,20 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService { public LocationPostFetchService(final LocationModel locationModel, final boolean isLoggedIn) { this.locationModel = locationModel; this.isLoggedIn = isLoggedIn; - locationService = LocationService.getInstance(); + locationService = isLoggedIn ? LocationService.getInstance() : null; + graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); } @Override public void fetch(final FetchListener> fetchListener) { - final ServiceCallback cb = new ServiceCallback() { + final ServiceCallback cb = new ServiceCallback() { @Override - public void onSuccess(final LocationPostsFetchResponse result) { + public void onSuccess(final PostsFetchResponse result) { if (result == null) return; - nextMaxId = result.getNextMaxId(); - moreAvailable = result.isMoreAvailable(); + nextMaxId = result.getNextCursor(); + moreAvailable = result.hasNextPage(); if (fetchListener != null) { - fetchListener.onResult(result.getItems()); + fetchListener.onResult(result.getFeedModels()); } } @@ -45,7 +48,7 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService { } }; if (isLoggedIn) locationService.fetchPosts(locationModel.getId(), nextMaxId, cb); - else locationService.fetchGraphQLPosts(locationModel.getId(), nextMaxId, cb); + else graphQLService.fetchLocationPosts(locationModel.getId(), nextMaxId, cb); } @Override diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java index 3617cbd2..649d28d8 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java @@ -7,29 +7,34 @@ import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.ProfileModel; import awais.instagrabber.repositories.responses.PostsFetchResponse; +import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.ProfileService; import awais.instagrabber.webservices.ServiceCallback; public class ProfilePostFetchService implements PostFetcher.PostFetchService { private static final String TAG = "ProfilePostFetchService"; private final ProfileService profileService; + private final GraphQLService graphQLService; private final ProfileModel profileModel; - private String nextCursor; - private boolean hasNextPage; + private final boolean isLoggedIn; + private String nextMaxId; + private boolean moreAvailable; - public ProfilePostFetchService(final ProfileModel profileModel) { + public ProfilePostFetchService(final ProfileModel profileModel, final boolean isLoggedIn) { this.profileModel = profileModel; - profileService = ProfileService.getInstance(); + this.isLoggedIn = isLoggedIn; + graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); + profileService = isLoggedIn ? ProfileService.getInstance() : null; } @Override public void fetch(final FetchListener> fetchListener) { - profileService.fetchPosts(profileModel, 30, nextCursor, new ServiceCallback() { + final ServiceCallback cb = new ServiceCallback() { @Override public void onSuccess(final PostsFetchResponse result) { if (result == null) return; - nextCursor = result.getNextCursor(); - hasNextPage = result.hasNextPage(); + nextMaxId = result.getNextCursor(); + moreAvailable = result.hasNextPage(); if (fetchListener != null) { fetchListener.onResult(result.getFeedModels()); } @@ -42,16 +47,18 @@ public class ProfilePostFetchService implements PostFetcher.PostFetchService { fetchListener.onFailure(t); } } - }); + }; + if (isLoggedIn) profileService.fetchPosts(profileModel.getId(), nextMaxId, cb); + else graphQLService.fetchProfilePosts(profileModel.getId(), 30, nextMaxId, cb); } @Override public void reset() { - nextCursor = null; + nextMaxId = null; } @Override public boolean hasNextPage() { - return hasNextPage; + return moreAvailable; } } diff --git a/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java index fcae9b65..b13d842d 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java @@ -6,34 +6,39 @@ import awais.instagrabber.customviews.helpers.PostFetcher; import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.enums.PostItemType; +import awais.instagrabber.repositories.responses.PostsFetchResponse; +import awais.instagrabber.webservices.GraphQLService; import awais.instagrabber.webservices.ProfileService; -import awais.instagrabber.webservices.ProfileService.SavedPostsFetchResponse; import awais.instagrabber.webservices.ServiceCallback; public class SavedPostFetchService implements PostFetcher.PostFetchService { private final ProfileService profileService; + private final GraphQLService graphQLService; private final String profileId; private final PostItemType type; + private final boolean isLoggedIn; private String nextMaxId; private boolean moreAvailable; - public SavedPostFetchService(final String profileId, final PostItemType type) { + public SavedPostFetchService(final String profileId, final PostItemType type, final boolean isLoggedIn) { this.profileId = profileId; this.type = type; - profileService = ProfileService.getInstance(); + this.isLoggedIn = isLoggedIn; + graphQLService = isLoggedIn ? null : GraphQLService.getInstance(); + profileService = isLoggedIn ? ProfileService.getInstance() : null; } @Override public void fetch(final FetchListener> fetchListener) { - final ServiceCallback callback = new ServiceCallback() { + final ServiceCallback callback = new ServiceCallback() { @Override - public void onSuccess(final SavedPostsFetchResponse result) { + public void onSuccess(final PostsFetchResponse result) { if (result == null) return; - nextMaxId = result.getNextMaxId(); - moreAvailable = result.isMoreAvailable(); + nextMaxId = result.getNextCursor(); + moreAvailable = result.hasNextPage(); if (fetchListener != null) { - fetchListener.onResult(result.getItems()); + fetchListener.onResult(result.getFeedModels()); } } @@ -50,7 +55,8 @@ public class SavedPostFetchService implements PostFetcher.PostFetchService { profileService.fetchLiked(nextMaxId, callback); break; case TAGGED: - profileService.fetchTagged(profileId, 30, nextMaxId, callback); + if (isLoggedIn) profileService.fetchTagged(profileId, nextMaxId, callback); + else graphQLService.fetchTaggedPosts(profileId, 30, nextMaxId, callback); break; case SAVED: default: diff --git a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java index fbb9f8f7..ed16f43c 100644 --- a/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java @@ -40,24 +40,26 @@ import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.enums.PostItemType; import awais.instagrabber.utils.Constants; +import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.DownloadUtils; +import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; import static androidx.core.content.PermissionChecker.checkSelfPermission; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; +import static awais.instagrabber.utils.Utils.settingsHelper; public final class SavedViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { private static final int STORAGE_PERM_REQUEST_CODE = 8020; private static final int STORAGE_PERM_REQUEST_CODE_FOR_SELECTION = 8030; private FragmentSavedBinding binding; - private String username; + private String username, cookie, profileId; private ActionMode actionMode; private SwipeRefreshLayout root; private AppCompatActivity fragmentActivity; - private boolean shouldRefresh = true; + private boolean isLoggedIn, shouldRefresh = true; private PostItemType type; - private String profileId; private Set selectedFeedModels; private FeedModel downloadFeedModel; private int downloadChildPosition = -1; @@ -225,6 +227,8 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL @Override public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { + cookie = settingsHelper.getString(Constants.COOKIE); + isLoggedIn = !TextUtils.isEmpty(cookie) && CookieUtils.getUserIdFromCookie(cookie) != null; if (root != null) { shouldRefresh = false; return root; @@ -281,7 +285,7 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL private void setupPosts() { binding.posts.setViewModelStoreOwner(this) .setLifeCycleOwner(this) - .setPostFetchService(new SavedPostFetchService(profileId, type)) + .setPostFetchService(new SavedPostFetchService(profileId, type, isLoggedIn)) .setLayoutPreferences(layoutPreferences) .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState()) .setFeedItemCallback(feedItemCallback) diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index 180f6e25..f9e85d3e 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -54,7 +54,6 @@ import awais.instagrabber.R; import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.HighlightsAdapter; -import awais.instagrabber.asyncs.HighlightsFetcher; import awais.instagrabber.asyncs.ProfileFetcher; import awais.instagrabber.asyncs.ProfilePostFetchService; import awais.instagrabber.asyncs.UsernameFetcher; @@ -75,6 +74,7 @@ import awais.instagrabber.dialogs.ProfilePicDialogFragment; import awais.instagrabber.fragments.PostViewV2Fragment; import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.models.FeedModel; +import awais.instagrabber.models.HighlightModel; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.ProfileModel; import awais.instagrabber.models.StoryModel; @@ -729,6 +729,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe } private void setupButtons(final String profileId, final String myId) { + profileDetailsBinding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE); if (isLoggedIn) { if (profileId.equals(myId)) { profileDetailsBinding.btnTagged.setVisibility(View.VISIBLE); @@ -738,7 +739,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe profileDetailsBinding.btnSaved.setText(R.string.saved); return; } - profileDetailsBinding.btnTagged.setVisibility(View.GONE); profileDetailsBinding.btnSaved.setVisibility(View.GONE); profileDetailsBinding.btnLiked.setVisibility(View.GONE); profileDetailsBinding.btnDM.setVisibility(View.VISIBLE); // maybe there is a judgment mechanism? @@ -776,7 +776,6 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe restrictMenuItem.setTitle(R.string.restrict); } } - profileDetailsBinding.btnTagged.setVisibility(profileModel.isReallyPrivate() ? View.GONE : View.VISIBLE); if (blockMenuItem != null) { blockMenuItem.setVisible(true); if (profileModel.isBlocked()) { @@ -817,14 +816,24 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe Log.e(TAG, "Error", t); } }); - new HighlightsFetcher(profileId, - result -> { - highlightsFetching = false; - if (result != null) { - profileDetailsBinding.highlightsList.setVisibility(View.VISIBLE); - highlightsViewModel.getList().postValue(result); - } else profileDetailsBinding.highlightsList.setVisibility(View.GONE); - }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + storiesService.fetchHighlights(profileId, + new ServiceCallback>() { + @Override + public void onSuccess(final List result) { + highlightsFetching = false; + if (result != null) { + profileDetailsBinding.highlightsList.setVisibility(View.VISIBLE); + highlightsViewModel.getList().postValue(result); + } + else profileDetailsBinding.highlightsList.setVisibility(View.GONE); + } + + @Override + public void onFailure(final Throwable t) { + profileDetailsBinding.highlightsList.setVisibility(View.GONE); + Log.e(TAG, "Error", t); + } + }); } private void setupCommonListeners() { @@ -979,7 +988,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe private void setupPosts() { binding.postsRecyclerView.setViewModelStoreOwner(this) .setLifeCycleOwner(this) - .setPostFetchService(new ProfilePostFetchService(profileModel)) + .setPostFetchService(new ProfilePostFetchService(profileModel, isLoggedIn)) .setLayoutPreferences(layoutPreferences) .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState()) .setFeedItemCallback(feedItemCallback) diff --git a/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java similarity index 87% rename from app/src/main/java/awais/instagrabber/repositories/FeedRepository.java rename to app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java index 3672fb90..23020035 100644 --- a/app/src/main/java/awais/instagrabber/repositories/FeedRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/GraphQLRepository.java @@ -6,7 +6,7 @@ import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.QueryMap; -public interface FeedRepository { +public interface GraphQLRepository { @GET("/graphql/query/") Call fetch(@QueryMap(encoded = true) Map queryParams); } diff --git a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java index 2237254d..7cb5ca41 100644 --- a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java @@ -12,7 +12,4 @@ public interface LocationRepository { @GET("/api/v1/feed/location/{location}/") Call fetchPosts(@Path("location") final String locationId, @QueryMap Map queryParams); - - @GET("/graphql/query/") - Call fetchGraphQLPosts(@QueryMap(encoded = true) Map queryParams); } diff --git a/app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java b/app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java index ec6a0d6e..916dd094 100644 --- a/app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java @@ -9,15 +9,18 @@ import retrofit2.http.QueryMap; public interface ProfileRepository { - @GET("api/v1/users/{uid}/info/") + @GET("/api/v1/users/{uid}/info/") Call getUserInfo(@Path("uid") final String uid); - @GET("/graphql/query/") - Call fetch(@QueryMap Map queryMap); + @GET("/api/v1/feed/user/{uid}/") + Call fetch(@Path("uid") final String uid, @QueryMap Map queryParams); @GET("/api/v1/feed/saved/") Call fetchSaved(@QueryMap Map queryParams); @GET("/api/v1/feed/liked/") Call fetchLiked(@QueryMap Map queryParams); + + @GET("/api/v1/usertags/{profileId}/feed/") + Call fetchTagged(@Path("profileId") final String profileId, @QueryMap Map queryParams); } diff --git a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java index 8ed0da74..c66368de 100644 --- a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java @@ -17,8 +17,11 @@ public interface StoriesRepository { @FormUrlEncoded @POST("/api/v1/feed/reels_tray/") - Call getStories(@Header("User-Agent") String userAgent, - @FieldMap Map form); + Call getFeedStories(@Header("User-Agent") String userAgent, + @FieldMap Map form); + + @GET("/api/v1/highlights/{uid}/highlights_tray/") + Call fetchHighlights(@Path("uid") final String uid); @GET Call getUserStory(@Header("User-Agent") String userAgent, @Url String url); diff --git a/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java b/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java index b646a8a3..4db3efbd 100644 --- a/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/TagsRepository.java @@ -24,7 +24,4 @@ public interface TagsRepository { @GET("/api/v1/feed/tag/{tag}/") Call fetchPosts(@Path("tag") final String tag, @QueryMap Map queryParams); - - @GET("/graphql/query/") - Call fetchGraphQLPosts(@QueryMap(encoded = true) Map queryParams); } diff --git a/app/src/main/java/awais/instagrabber/webservices/FeedService.java b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java similarity index 55% rename from app/src/main/java/awais/instagrabber/webservices/FeedService.java rename to app/src/main/java/awais/instagrabber/webservices/GraphQLService.java index d4e84e35..94d9f38f 100644 --- a/app/src/main/java/awais/instagrabber/webservices/FeedService.java +++ b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java @@ -21,10 +21,7 @@ import java.util.List; import java.util.Map; 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.GraphQLRepository; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.ResponseBodyUtils; @@ -34,69 +31,43 @@ import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; -public class FeedService extends BaseService { - private static final String TAG = "FeedService"; +public class GraphQLService extends BaseService { + private static final String TAG = "GraphQLService"; private static final boolean loadFromMock = false; - private final FeedRepository repository; + private final GraphQLRepository repository; - private static FeedService instance; + private static GraphQLService instance; - private FeedService() { + private GraphQLService() { final Retrofit retrofit = getRetrofitBuilder() .baseUrl("https://www.instagram.com") .build(); - repository = retrofit.create(FeedRepository.class); + repository = retrofit.create(GraphQLRepository.class); } - public static FeedService getInstance() { + public static GraphQLService getInstance() { if (instance == null) { - instance = new FeedService(); + instance = new GraphQLService(); } return instance; } - public void fetch(final int maxItemsToLoad, - final String cursor, - final ServiceCallback 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())); - } catch (IOException | JSONException e) { - Log.e(TAG, "fetch: ", e); - } - }, 1000); - return; - } + private void fetch(final String queryHash, + final String variables, + final String arg1, + final String arg2, + final ServiceCallback callback) { final Map queryMap = new HashMap<>(); - queryMap.put("query_hash", "c699b185975935ae2a457f24075de8c7"); - queryMap.put("variables", "{" + - "\"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) + "\"" + - "}"); + queryMap.put("query_hash", queryHash); + queryMap.put("variables", variables); final Call request = repository.fetch(queryMap); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { try { // Log.d(TAG, "onResponse: body: " + response.body()); - final PostsFetchResponse postsFetchResponse = parseResponse(response); + final PostsFetchResponse postsFetchResponse = parsePostResponse(response, arg1, arg2); if (callback != null) { callback.onSuccess(postsFetchResponse); } @@ -115,26 +86,111 @@ public class FeedService extends BaseService { } } }); + } + public void fetchFeed(final int maxItemsToLoad, + final String cursor, + final ServiceCallback 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, + final String maxId, + final ServiceCallback callback) { + fetch("36bd0f2bf5911908de389b8ceaa3be6d", + "{\"id\":\"" + locationId + "\"," + + "\"first\":25," + + "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", + Constants.EXTRAS_LOCATION, + "edge_location_to_media", + callback); + } + + public void fetchHashtagPosts(@NonNull final String tag, + final String maxId, + final ServiceCallback callback) { + fetch("9b498c08113f1e09617a1703c22b2f32", + "{\"tag_name\":\"" + tag + "\"," + + "\"first\":25," + + "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", + Constants.EXTRAS_HASHTAG, + "edge_hashtag_to_media", + callback); + } + + public void fetchProfilePosts(@NonNull final String profileId, + final int postsPerPage, + final String maxId, + final ServiceCallback callback) { + fetch("18a7b935ab438c4514b1f742d8fa07a7", + "{\"id\":\"" + profileId + "\"," + + "\"first\":" + postsPerPage + "," + + "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", + Constants.EXTRAS_USER, + "edge_owner_to_timeline_media", + callback); + } + + public void fetchTaggedPosts(@NonNull final String profileId, + final int postsPerPage, + final String maxId, + final ServiceCallback callback) { + fetch("31fe64d9463cbbe58319dced405c6206", + "{\"id\":\"" + profileId + "\"," + + "\"first\":" + postsPerPage + "," + + "\"after\":\"" + (maxId == null ? "" : maxId) + "\"}", + Constants.EXTRAS_USER, + "edge_user_to_photos_of_you", + callback); } @NonNull - private PostsFetchResponse parseResponse(@NonNull final Response response) throws JSONException { + private PostsFetchResponse parsePostResponse(@NonNull final Response response, @NonNull final String arg1, @NonNull final String arg2) 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()); + return parseResponseBody(response.body(), arg1, arg2); } @NonNull - private PostsFetchResponse parseResponseBody(@NonNull final String body) + private PostsFetchResponse parseResponseBody(@NonNull final String body, @NonNull final String arg1, @NonNull final String arg2) throws JSONException { final List feedModels = new ArrayList<>(); final JSONObject timelineFeed = new JSONObject(body) .getJSONObject("data") - .getJSONObject(Constants.EXTRAS_USER) - .getJSONObject("edge_web_feed_timeline"); + .getJSONObject(arg1) + .getJSONObject(arg2); final String endCursor; final boolean hasNextPage; diff --git a/app/src/main/java/awais/instagrabber/webservices/LocationService.java b/app/src/main/java/awais/instagrabber/webservices/LocationService.java index 241453be..d0bbe7c2 100644 --- a/app/src/main/java/awais/instagrabber/webservices/LocationService.java +++ b/app/src/main/java/awais/instagrabber/webservices/LocationService.java @@ -19,6 +19,7 @@ import java.util.Objects; import awais.instagrabber.models.FeedModel; import awais.instagrabber.repositories.LocationRepository; +import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.utils.ResponseBodyUtils; import awais.instagrabber.utils.TextUtils; import retrofit2.Call; @@ -29,7 +30,7 @@ import retrofit2.Retrofit; public class LocationService extends BaseService { private static final String TAG = "LocationService"; - private final LocationRepository repository, webRepository; + private final LocationRepository repository; private static LocationService instance; @@ -38,10 +39,6 @@ public class LocationService extends BaseService { .baseUrl("https://i.instagram.com") .build(); repository = retrofit.create(LocationRepository.class); - final Retrofit webRetrofit = getRetrofitBuilder() - .baseUrl("https://www.instagram.com") - .build(); - webRepository = webRetrofit.create(LocationRepository.class); } public static LocationService getInstance() { @@ -53,7 +50,7 @@ public class LocationService extends BaseService { public void fetchPosts(@NonNull final String locationId, final String maxId, - final ServiceCallback callback) { + final ServiceCallback callback) { final ImmutableMap.Builder builder = ImmutableMap.builder(); if (!TextUtils.isEmpty(maxId)) { builder.put("max_id", maxId); @@ -71,7 +68,7 @@ public class LocationService extends BaseService { callback.onSuccess(null); return; } - final LocationPostsFetchResponse tagPostsFetchResponse = parseResponse(body); + final PostsFetchResponse tagPostsFetchResponse = parseResponse(body); callback.onSuccess(tagPostsFetchResponse); } catch (JSONException e) { Log.e(TAG, "onResponse", e); @@ -88,7 +85,7 @@ public class LocationService extends BaseService { }); } - private LocationPostsFetchResponse parseResponse(@NonNull final String body) throws JSONException { + private PostsFetchResponse parseResponse(@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"); @@ -96,12 +93,10 @@ public class LocationService extends BaseService { final String status = root.optString("status"); final JSONArray itemsJson = root.optJSONArray("items"); final List items = parseItems(itemsJson); - return new LocationPostsFetchResponse( + return new PostsFetchResponse( + items, moreAvailable, - nextMaxId, - numResults, - status, - items + nextMaxId ); } @@ -122,174 +117,4 @@ public class LocationService extends BaseService { } return feedModels; } - - public void fetchGraphQLPosts(@NonNull final String locationId, - final String maxId, - final ServiceCallback callback) { - final Map queryMap = new HashMap<>(); - queryMap.put("query_hash", "36bd0f2bf5911908de389b8ceaa3be6d"); - queryMap.put("variables", "{" + - "\"id\":\"" + locationId + "\"," + - "\"first\":25," + - "\"after\":\"" + (maxId == null ? "" : maxId) + "\"" + - "}"); - final Call request = webRepository.fetchGraphQLPosts(queryMap); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - try { - if (callback == null) { - return; - } - final String body = response.body(); - if (TextUtils.isEmpty(body)) { - callback.onSuccess(null); - return; - } - final LocationPostsFetchResponse tagPostsFetchResponse = parseGraphQLResponse(body); - callback.onSuccess(tagPostsFetchResponse); - } catch (JSONException e) { - Log.e(TAG, "onResponse", e); - callback.onFailure(e); - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }); - } - - private LocationPostsFetchResponse parseGraphQLResponse(@NonNull final String body) throws JSONException { - final JSONObject rootroot = new JSONObject(body); - final JSONObject root = rootroot.getJSONObject("data").getJSONObject("location").getJSONObject("edge_location_to_media"); - final boolean moreAvailable = root.getJSONObject("page_info").optBoolean("has_next_page"); - final String nextMaxId = root.getJSONObject("page_info").optString("end_cursor"); - final int numResults = root.optInt("count"); - final String status = rootroot.optString("status"); - final JSONArray itemsJson = root.optJSONArray("edges"); - final List items = parseGraphQLItems(itemsJson); - return new LocationPostsFetchResponse( - moreAvailable, - nextMaxId, - numResults, - status, - items - ); - } - - private List parseGraphQLItems(final JSONArray items) throws JSONException { - if (items == null) { - return Collections.emptyList(); - } - final List feedModels = new ArrayList<>(); - for (int i = 0; i < items.length(); i++) { - final JSONObject itemJson = items.optJSONObject(i); - if (itemJson == null) { - continue; - } - final FeedModel feedModel = ResponseBodyUtils.parseGraphQLItem(itemJson); - if (feedModel != null) { - feedModels.add(feedModel); - } - } - return feedModels; - } - - public static class LocationPostsFetchResponse { - private boolean moreAvailable; - private String nextMaxId; - private int numResults; - private String status; - private List items; - - public LocationPostsFetchResponse(final boolean moreAvailable, - final String nextMaxId, - final int numResults, - final String status, - final List items) { - this.moreAvailable = moreAvailable; - this.nextMaxId = nextMaxId; - this.numResults = numResults; - this.status = status; - this.items = items; - } - - public boolean isMoreAvailable() { - return moreAvailable; - } - - public LocationPostsFetchResponse setMoreAvailable(final boolean moreAvailable) { - this.moreAvailable = moreAvailable; - return this; - } - - public String getNextMaxId() { - return nextMaxId; - } - - public LocationPostsFetchResponse setNextMaxId(final String nextMaxId) { - this.nextMaxId = nextMaxId; - return this; - } - - public int getNumResults() { - return numResults; - } - - public LocationPostsFetchResponse setNumResults(final int numResults) { - this.numResults = numResults; - return this; - } - - public String getStatus() { - return status; - } - - public LocationPostsFetchResponse setStatus(final String status) { - this.status = status; - return this; - } - - public List getItems() { - return items; - } - - public LocationPostsFetchResponse setItems(final List items) { - this.items = items; - return this; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final LocationPostsFetchResponse that = (LocationPostsFetchResponse) o; - return moreAvailable == that.moreAvailable && - numResults == that.numResults && - Objects.equals(nextMaxId, that.nextMaxId) && - Objects.equals(status, that.status) && - Objects.equals(items, that.items); - } - - @Override - public int hashCode() { - return Objects.hash(moreAvailable, nextMaxId, numResults, status, items); - } - - @NonNull - @Override - public String toString() { - return "LocationPostsFetchResponse{" + - "moreAvailable=" + moreAvailable + - ", nextMaxId='" + nextMaxId + '\'' + - ", numResults=" + numResults + - ", status='" + status + '\'' + - ", items=" + items + - '}'; - } - } } diff --git a/app/src/main/java/awais/instagrabber/webservices/ProfileService.java b/app/src/main/java/awais/instagrabber/webservices/ProfileService.java index 9ba5a620..8e3c4c6a 100644 --- a/app/src/main/java/awais/instagrabber/webservices/ProfileService.java +++ b/app/src/main/java/awais/instagrabber/webservices/ProfileService.java @@ -12,15 +12,9 @@ import org.json.JSONObject; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Objects; 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.ProfileRepository; import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.repositories.responses.UserInfo; @@ -36,7 +30,6 @@ public class ProfileService extends BaseService { private static final String TAG = "ProfileService"; private final ProfileRepository repository; - private final ProfileRepository wwwRepository; private static ProfileService instance; @@ -44,11 +37,7 @@ public class ProfileService extends BaseService { final Retrofit retrofit = getRetrofitBuilder() .baseUrl("https://i.instagram.com") .build(); - final Retrofit wwwRetrofit = getRetrofitBuilder() - .baseUrl("https://www.instagram.com") - .build(); repository = retrofit.create(ProfileRepository.class); - wwwRepository = wwwRetrofit.create(ProfileRepository.class); } public static ProfileService getInstance() { @@ -89,32 +78,31 @@ public class ProfileService extends BaseService { }); } - public void fetchPosts(final ProfileModel profileModel, - final int postsPerPage, - final String cursor, + public void fetchPosts(final String userId, + final String maxId, final ServiceCallback callback) { - final Map queryMap = new HashMap<>(); - queryMap.put("query_hash", "18a7b935ab438c4514b1f742d8fa07a7"); - queryMap.put("variables", "{" + - "\"id\":\"" + profileModel.getId() + "\"," + - "\"first\":" + postsPerPage + "," + - "\"after\":\"" + (cursor == null ? "" : cursor) + "\"" + - "}"); - final Call request = wwwRepository.fetch(queryMap); + final ImmutableMap.Builder builder = ImmutableMap.builder(); + if (!TextUtils.isEmpty(maxId)) { + builder.put("max_id", maxId); + } + final Call request = repository.fetch(userId, builder.build()); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { try { - // Log.d(TAG, "onResponse: body: " + response.body()); - final PostsFetchResponse postsFetchResponse = parseResponse(profileModel, response); - if (callback != null) { - callback.onSuccess(postsFetchResponse); + if (callback == null) { + return; } + final String body = response.body(); + if (TextUtils.isEmpty(body)) { + callback.onSuccess(null); + return; + } + final PostsFetchResponse postsFetchResponse = parseProfilePostsResponse(body, false); + callback.onSuccess(postsFetchResponse); } catch (JSONException e) { Log.e(TAG, "onResponse", e); - if (callback != null) { - callback.onFailure(e); - } + callback.onFailure(e); } } @@ -127,48 +115,8 @@ public class ProfileService extends BaseService { }); } - private PostsFetchResponse parseResponse(final ProfileModel profileModel, final Response 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(profileModel, response.body()); - } - - private PostsFetchResponse parseResponseBody(final ProfileModel profileModel, final String body) throws JSONException { - // Log.d(TAG, "parseResponseBody: body: " + body); - final List feedModels = new ArrayList<>(); - // return new FeedFetchResponse(feedModels, false, null); - final JSONObject mediaPosts = new JSONObject(body) - .getJSONObject("data") - .getJSONObject(Constants.EXTRAS_USER) - .getJSONObject("edge_owner_to_timeline_media"); - final String endCursor; - final boolean hasNextPage; - final JSONObject pageInfo = mediaPosts.getJSONObject("page_info"); - if (pageInfo.has("has_next_page")) { - hasNextPage = pageInfo.getBoolean("has_next_page"); - endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null; - } else { - hasNextPage = false; - endCursor = null; - } - final JSONArray edges = mediaPosts.getJSONArray("edges"); - for (int i = 0; i < edges.length(); ++i) { - final JSONObject itemJson = edges.optJSONObject(i); - if (itemJson == null) { - continue; - } - final FeedModel feedModel = ResponseBodyUtils.parseGraphQLItem(itemJson); - if (feedModel != null) { - feedModels.add(feedModel); - } - } - return new PostsFetchResponse(feedModels, hasNextPage, endCursor); - } - public void fetchSaved(final String maxId, - final ServiceCallback callback) { + final ServiceCallback callback) { final ImmutableMap.Builder builder = ImmutableMap.builder(); if (!TextUtils.isEmpty(maxId)) { builder.put("max_id", maxId); @@ -186,8 +134,8 @@ public class ProfileService extends BaseService { callback.onSuccess(null); return; } - final SavedPostsFetchResponse savedPostsFetchResponse = parseSavedPostsResponse(body, true); - callback.onSuccess(savedPostsFetchResponse); + final PostsFetchResponse PostsFetchResponse = parseSavedPostsResponse(body, true); + callback.onSuccess(PostsFetchResponse); } catch (JSONException e) { Log.e(TAG, "onResponse", e); callback.onFailure(e); @@ -204,7 +152,7 @@ public class ProfileService extends BaseService { } public void fetchLiked(final String maxId, - final ServiceCallback callback) { + final ServiceCallback callback) { final ImmutableMap.Builder builder = ImmutableMap.builder(); if (!TextUtils.isEmpty(maxId)) { builder.put("max_id", maxId); @@ -222,8 +170,8 @@ public class ProfileService extends BaseService { callback.onSuccess(null); return; } - final SavedPostsFetchResponse savedPostsFetchResponse = parseSavedPostsResponse(body, false); - callback.onSuccess(savedPostsFetchResponse); + final PostsFetchResponse PostsFetchResponse = parseSavedPostsResponse(body, false); + callback.onSuccess(PostsFetchResponse); } catch (JSONException e) { Log.e(TAG, "onResponse", e); callback.onFailure(e); @@ -240,17 +188,13 @@ public class ProfileService extends BaseService { } public void fetchTagged(final String profileId, - final int postsPerPage, - final String cursor, - final ServiceCallback callback) { - final Map queryMap = new HashMap<>(); - queryMap.put("query_hash", "31fe64d9463cbbe58319dced405c6206"); - queryMap.put("variables", "{" + - "\"id\":\"" + profileId + "\"," + - "\"first\":" + postsPerPage + "," + - "\"after\":\"" + (cursor == null ? "" : cursor) + "\"" + - "}"); - final Call request = wwwRepository.fetch(queryMap); + final String maxId, + final ServiceCallback callback) { + final ImmutableMap.Builder builder = ImmutableMap.builder(); + if (!TextUtils.isEmpty(maxId)) { + builder.put("max_id", maxId); + } + final Call request = repository.fetchTagged(profileId, builder.build()); request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -263,8 +207,8 @@ public class ProfileService extends BaseService { callback.onSuccess(null); return; } - final SavedPostsFetchResponse savedPostsFetchResponse = parseTaggedPostsResponse(body); - callback.onSuccess(savedPostsFetchResponse); + final PostsFetchResponse PostsFetchResponse = parseSavedPostsResponse(body, false); + callback.onSuccess(PostsFetchResponse); } catch (JSONException e) { Log.e(TAG, "onResponse", e); callback.onFailure(e); @@ -280,7 +224,7 @@ public class ProfileService extends BaseService { }); } - private SavedPostsFetchResponse parseSavedPostsResponse(final String body, final boolean isInMedia) throws JSONException { + private PostsFetchResponse parseProfilePostsResponse(final String body, final boolean isInMedia) throws JSONException { final JSONObject root = new JSONObject(body); final boolean moreAvailable = root.optBoolean("more_available"); final String nextMaxId = root.optString("next_max_id"); @@ -288,48 +232,26 @@ public class ProfileService extends BaseService { final String status = root.optString("status"); final JSONArray itemsJson = root.optJSONArray("items"); final List items = parseItems(itemsJson, isInMedia); - return new SavedPostsFetchResponse( + return new PostsFetchResponse( + items, moreAvailable, - nextMaxId, - numResults, - status, - items + nextMaxId ); } - @NonNull - private SavedPostsFetchResponse parseTaggedPostsResponse(@NonNull final String body) - throws JSONException { - final List feedModels = new ArrayList<>(); - final JSONObject timelineFeed = new JSONObject(body) - .getJSONObject("data") - .getJSONObject(Constants.EXTRAS_USER) - .getJSONObject("edge_user_to_photos_of_you"); - final String endCursor; - final boolean hasNextPage; - - final JSONObject pageInfo = timelineFeed.getJSONObject("page_info"); - if (pageInfo.has("has_next_page")) { - hasNextPage = pageInfo.getBoolean("has_next_page"); - endCursor = hasNextPage ? pageInfo.getString("end_cursor") : null; - } else { - hasNextPage = false; - endCursor = null; - } - - final JSONArray feedItems = timelineFeed.getJSONArray("edges"); - - for (int i = 0; i < feedItems.length(); ++i) { - final JSONObject itemJson = feedItems.optJSONObject(i); - if (itemJson == null) { - continue; - } - final FeedModel feedModel = ResponseBodyUtils.parseGraphQLItem(itemJson); - if (feedModel != null) { - feedModels.add(feedModel); - } - } - return new SavedPostsFetchResponse(hasNextPage, endCursor, timelineFeed.getInt("count"), "ok", feedModels); + private PostsFetchResponse parseSavedPostsResponse(final String body, final boolean isInMedia) throws JSONException { + final JSONObject root = new JSONObject(body); + final boolean moreAvailable = root.optBoolean("more_available"); + 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 List items = parseItems(itemsJson, isInMedia); + return new PostsFetchResponse( + items, + moreAvailable, + nextMaxId + ); } private List parseItems(final JSONArray items, final boolean isInMedia) throws JSONException { @@ -349,98 +271,4 @@ public class ProfileService extends BaseService { } return feedModels; } - - public static class SavedPostsFetchResponse { - private boolean moreAvailable; - private String nextMaxId; - private int numResults; - private String status; - private List items; - - public SavedPostsFetchResponse(final boolean moreAvailable, - final String nextMaxId, - final int numResults, - final String status, - final List items) { - this.moreAvailable = moreAvailable; - this.nextMaxId = nextMaxId; - this.numResults = numResults; - this.status = status; - this.items = items; - } - - public boolean isMoreAvailable() { - return moreAvailable; - } - - public SavedPostsFetchResponse setMoreAvailable(final boolean moreAvailable) { - this.moreAvailable = moreAvailable; - return this; - } - - public String getNextMaxId() { - return nextMaxId; - } - - public SavedPostsFetchResponse setNextMaxId(final String nextMaxId) { - this.nextMaxId = nextMaxId; - return this; - } - - public int getNumResults() { - return numResults; - } - - public SavedPostsFetchResponse setNumResults(final int numResults) { - this.numResults = numResults; - return this; - } - - public String getStatus() { - return status; - } - - public SavedPostsFetchResponse setStatus(final String status) { - this.status = status; - return this; - } - - public List getItems() { - return items; - } - - public SavedPostsFetchResponse setItems(final List items) { - this.items = items; - return this; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final SavedPostsFetchResponse that = (SavedPostsFetchResponse) o; - return moreAvailable == that.moreAvailable && - numResults == that.numResults && - Objects.equals(nextMaxId, that.nextMaxId) && - Objects.equals(status, that.status) && - Objects.equals(items, that.items); - } - - @Override - public int hashCode() { - return Objects.hash(moreAvailable, nextMaxId, numResults, status, items); - } - - @NonNull - @Override - public String toString() { - return "SavedPostsFetchResponse{" + - "moreAvailable=" + moreAvailable + - ", nextMaxId='" + nextMaxId + '\'' + - ", numResults=" + numResults + - ", status='" + status + '\'' + - ", items=" + items + - '}'; - } - } } diff --git a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java index d5a0c723..a0d93d3c 100644 --- a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java +++ b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.UUID; import awais.instagrabber.models.FeedStoryModel; +import awais.instagrabber.models.HighlightModel; import awais.instagrabber.models.ProfileModel; import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.MediaItemType; @@ -33,6 +34,7 @@ import awais.instagrabber.repositories.StoriesRepository; import awais.instagrabber.repositories.responses.StoryStickerResponse; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.ResponseBodyUtils; +import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; import retrofit2.Call; import retrofit2.Callback; @@ -92,7 +94,7 @@ public class StoriesService extends BaseService { form.put("_uuid", UUID.randomUUID().toString()); form.put("supported_capabilities_new", Constants.SUPPORTED_CAPABILITIES); final Map signedForm = Utils.sign(form); - final Call response = repository.getStories(Constants.I_USER_AGENT, signedForm); + final Call response = repository.getFeedStories(Constants.I_USER_AGENT, signedForm); response.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -134,6 +136,52 @@ public class StoriesService extends BaseService { } } + public void fetchHighlights(final String profileId, + final ServiceCallback> callback) { + final Call request = repository.fetchHighlights(profileId); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + try { + if (callback == null) { + return; + } + final String body = response.body(); + if (TextUtils.isEmpty(body)) { + callback.onSuccess(null); + return; + } + final JSONArray highlightsReel = new JSONObject(body).getJSONArray("tray"); + + final int length = highlightsReel.length(); + final List highlightModels = new ArrayList<>(); + // final String[] highlightIds = new String[length]; + for (int i = 0; i < length; ++i) { + final JSONObject highlightNode = highlightsReel.getJSONObject(i); + highlightModels.add(new HighlightModel( + highlightNode.getString("title"), + highlightNode.getString(Constants.EXTRAS_ID), + highlightNode.getJSONObject("cover_media") + .getJSONObject("cropped_image_version") + .getString("url") + )); + } + callback.onSuccess(highlightModels); + } catch (JSONException e) { + Log.e(TAG, "onResponse", e); + callback.onFailure(e); + } + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + if (callback != null) { + callback.onFailure(t); + } + } + }); + } + public void getUserStory(final String id, final String username, final boolean isLoc, diff --git a/app/src/main/java/awais/instagrabber/webservices/TagsService.java b/app/src/main/java/awais/instagrabber/webservices/TagsService.java index 5aabaee1..5786e82f 100644 --- a/app/src/main/java/awais/instagrabber/webservices/TagsService.java +++ b/app/src/main/java/awais/instagrabber/webservices/TagsService.java @@ -19,6 +19,7 @@ import java.util.Objects; import awais.instagrabber.models.FeedModel; import awais.instagrabber.repositories.TagsRepository; +import awais.instagrabber.repositories.responses.PostsFetchResponse; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.ResponseBodyUtils; import awais.instagrabber.utils.TextUtils; @@ -118,8 +119,8 @@ public class TagsService extends BaseService { public void fetchPosts(@NonNull final String tag, final String maxId, - final ServiceCallback callback) { - final ImmutableMap.Builder builder = ImmutableMap.builder(); + final ServiceCallback callback) { + final ImmutableMap.Builder builder = ImmutableMap.builder(); if (!TextUtils.isEmpty(maxId)) { builder.put("max_id", maxId); } @@ -136,7 +137,7 @@ public class TagsService extends BaseService { callback.onSuccess(null); return; } - final TagPostsFetchResponse tagPostsFetchResponse = parseResponse(body); + final PostsFetchResponse tagPostsFetchResponse = parseResponse(body); callback.onSuccess(tagPostsFetchResponse); } catch (JSONException e) { Log.e(TAG, "onResponse", e); @@ -153,7 +154,7 @@ public class TagsService extends BaseService { }); } - private TagPostsFetchResponse parseResponse(@NonNull final String body) throws JSONException { + private PostsFetchResponse parseResponse(@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"); @@ -161,12 +162,10 @@ public class TagsService extends BaseService { final String status = root.optString("status"); final JSONArray itemsJson = root.optJSONArray("items"); final List items = parseItems(itemsJson); - return new TagPostsFetchResponse( + return new PostsFetchResponse( + items, moreAvailable, - nextMaxId, - numResults, - status, - items + nextMaxId ); } @@ -187,174 +186,4 @@ public class TagsService extends BaseService { } return feedModels; } - - public void fetchGraphQLPosts(@NonNull final String tag, - final String maxId, - final ServiceCallback callback) { - final Map queryMap = new HashMap<>(); - queryMap.put("query_hash", "9b498c08113f1e09617a1703c22b2f32"); - queryMap.put("variables", "{" + - "\"tag_name\":\"" + tag + "\"," + - "\"first\":25," + - "\"after\":\"" + (maxId == null ? "" : maxId) + "\"" + - "}"); - final Call request = webRepository.fetchGraphQLPosts(queryMap); - request.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - try { - if (callback == null) { - return; - } - final String body = response.body(); - if (TextUtils.isEmpty(body)) { - callback.onSuccess(null); - return; - } - final TagPostsFetchResponse tagPostsFetchResponse = parseGraphQLResponse(body); - callback.onSuccess(tagPostsFetchResponse); - } catch (JSONException e) { - Log.e(TAG, "onResponse", e); - callback.onFailure(e); - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - if (callback != null) { - callback.onFailure(t); - } - } - }); - } - - private TagPostsFetchResponse parseGraphQLResponse(@NonNull final String body) throws JSONException { - final JSONObject rootroot = new JSONObject(body); - final JSONObject root = rootroot.getJSONObject("data").getJSONObject("hashtag").getJSONObject("edge_hashtag_to_media"); - final boolean moreAvailable = root.getJSONObject("page_info").optBoolean("has_next_page"); - final String nextMaxId = root.getJSONObject("page_info").optString("end_cursor"); - final int numResults = root.optInt("count"); - final String status = rootroot.optString("status"); - final JSONArray itemsJson = root.optJSONArray("edges"); - final List items = parseGraphQLItems(itemsJson); - return new TagPostsFetchResponse( - moreAvailable, - nextMaxId, - numResults, - status, - items - ); - } - - private List parseGraphQLItems(final JSONArray items) throws JSONException { - if (items == null) { - return Collections.emptyList(); - } - final List feedModels = new ArrayList<>(); - for (int i = 0; i < items.length(); i++) { - final JSONObject itemJson = items.optJSONObject(i); - if (itemJson == null) { - continue; - } - final FeedModel feedModel = ResponseBodyUtils.parseGraphQLItem(itemJson); - if (feedModel != null) { - feedModels.add(feedModel); - } - } - return feedModels; - } - - public static class TagPostsFetchResponse { - private boolean moreAvailable; - private String nextMaxId; - private int numResults; - private String status; - private List items; - - public TagPostsFetchResponse(final boolean moreAvailable, - final String nextMaxId, - final int numResults, - final String status, - final List items) { - this.moreAvailable = moreAvailable; - this.nextMaxId = nextMaxId; - this.numResults = numResults; - this.status = status; - this.items = items; - } - - public boolean isMoreAvailable() { - return moreAvailable; - } - - public TagPostsFetchResponse setMoreAvailable(final boolean moreAvailable) { - this.moreAvailable = moreAvailable; - return this; - } - - public String getNextMaxId() { - return nextMaxId; - } - - public TagPostsFetchResponse setNextMaxId(final String nextMaxId) { - this.nextMaxId = nextMaxId; - return this; - } - - public int getNumResults() { - return numResults; - } - - public TagPostsFetchResponse setNumResults(final int numResults) { - this.numResults = numResults; - return this; - } - - public String getStatus() { - return status; - } - - public TagPostsFetchResponse setStatus(final String status) { - this.status = status; - return this; - } - - public List getItems() { - return items; - } - - public TagPostsFetchResponse setItems(final List items) { - this.items = items; - return this; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - final TagPostsFetchResponse that = (TagPostsFetchResponse) o; - return moreAvailable == that.moreAvailable && - numResults == that.numResults && - Objects.equals(nextMaxId, that.nextMaxId) && - Objects.equals(status, that.status) && - Objects.equals(items, that.items); - } - - @Override - public int hashCode() { - return Objects.hash(moreAvailable, nextMaxId, numResults, status, items); - } - - @NonNull - @Override - public String toString() { - return "TagPostsFetchResponse{" + - "moreAvailable=" + moreAvailable + - ", nextMaxId='" + nextMaxId + '\'' + - ", numResults=" + numResults + - ", status='" + status + '\'' + - ", items=" + items + - '}'; - } - } }