Browse Source

version up, close #266, address half of #259

instagram is not returning most data in graphql anymore so mitigation has been implemented, but again they need to stop trampling on the rights of anonymous users
renovate/org.robolectric-robolectric-4.x
Austin Huang 4 years ago
parent
commit
f67d3a023c
No known key found for this signature in database GPG Key ID: 84C23AA04587A91F
  1. 4
      app/build.gradle
  2. 4
      app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java
  3. 26
      app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java
  4. 2
      app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
  5. 6
      app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java
  6. 18
      app/src/main/java/awais/instagrabber/fragments/settings/AboutFragment.java
  7. 3
      app/src/main/java/awais/instagrabber/repositories/TagsRepository.java
  8. 156
      app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java
  9. 150
      app/src/main/java/awais/instagrabber/webservices/FeedService.java
  10. 78
      app/src/main/java/awais/instagrabber/webservices/TagsService.java

4
app/build.gradle

@ -10,8 +10,8 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 29
versionCode 52
versionName '19.0.0'
versionCode 53
versionName '19.0.1'
multiDexEnabled true multiDexEnabled true

4
app/src/main/java/awais/instagrabber/adapters/viewholder/FeedGridItemViewHolder.java

@ -64,7 +64,7 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
binding.postImage.setAspectRatio(1); binding.postImage.setAspectRatio(1);
} }
if (layoutPreferences.isAvatarVisible()) { if (layoutPreferences.isAvatarVisible()) {
binding.profilePic.setVisibility(View.VISIBLE);
binding.profilePic.setVisibility(TextUtils.isEmpty(feedModel.getProfileModel().getSdProfilePic()) ? View.GONE : View.VISIBLE);
binding.profilePic.setImageURI(feedModel.getProfileModel().getSdProfilePic()); binding.profilePic.setImageURI(feedModel.getProfileModel().getSdProfilePic());
final ViewGroup.LayoutParams layoutParams = binding.profilePic.getLayoutParams(); final ViewGroup.LayoutParams layoutParams = binding.profilePic.getLayoutParams();
@DimenRes final int dimenRes; @DimenRes final int dimenRes;
@ -88,7 +88,7 @@ public class FeedGridItemViewHolder extends RecyclerView.ViewHolder {
binding.profilePic.setVisibility(View.GONE); binding.profilePic.setVisibility(View.GONE);
} }
if (layoutPreferences.isNameVisible()) { if (layoutPreferences.isNameVisible()) {
binding.name.setVisibility(View.VISIBLE);
binding.name.setVisibility(TextUtils.isEmpty(feedModel.getProfileModel().getUsername()) ? View.GONE : View.VISIBLE);
binding.name.setText(feedModel.getProfileModel().getUsername()); binding.name.setText(feedModel.getProfileModel().getUsername());
} else { } else {
binding.name.setVisibility(View.GONE); binding.name.setVisibility(View.GONE);

26
app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java

@ -14,16 +14,36 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService {
private final TagsService tagsService; private final TagsService tagsService;
private final HashtagModel hashtagModel; private final HashtagModel hashtagModel;
private String nextMaxId; private String nextMaxId;
private boolean moreAvailable;
private boolean moreAvailable, isLoggedIn;
public HashtagPostFetchService(final HashtagModel hashtagModel) {
public HashtagPostFetchService(final HashtagModel hashtagModel, final boolean isLoggedIn) {
this.hashtagModel = hashtagModel; this.hashtagModel = hashtagModel;
this.isLoggedIn = isLoggedIn;
tagsService = TagsService.getInstance(); tagsService = TagsService.getInstance();
} }
@Override @Override
public void fetch(final FetchListener<List<FeedModel>> fetchListener) { public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, new ServiceCallback<TagPostsFetchResponse>() {
if (isLoggedIn) tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, new ServiceCallback<TagPostsFetchResponse>() {
@Override
public void onSuccess(final TagPostsFetchResponse result) {
if (result == null) return;
nextMaxId = result.getNextMaxId();
moreAvailable = result.isMoreAvailable();
if (fetchListener != null) {
fetchListener.onResult(result.getItems());
}
}
@Override
public void onFailure(final Throwable t) {
// Log.e(TAG, "onFailure: ", t);
if (fetchListener != null) {
fetchListener.onFailure(t);
}
}
});
else tagsService.fetchGraphQLPosts(hashtagModel.getName().toLowerCase(), nextMaxId, new ServiceCallback<TagPostsFetchResponse>() {
@Override @Override
public void onSuccess(final TagPostsFetchResponse result) { public void onSuccess(final TagPostsFetchResponse result) {
if (result == null) return; if (result == null) return;

2
app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java

@ -352,7 +352,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
private void setupPosts() { private void setupPosts() {
binding.posts.setViewModelStoreOwner(this) binding.posts.setViewModelStoreOwner(this)
.setLifeCycleOwner(this) .setLifeCycleOwner(this)
.setPostFetchService(new HashtagPostFetchService(hashtagModel))
.setPostFetchService(new HashtagPostFetchService(hashtagModel, isLoggedIn))
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_HASHTAG_POSTS_LAYOUT))) .setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_HASHTAG_POSTS_LAYOUT)))
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState()) .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
.setFeedItemCallback(feedItemCallback) .setFeedItemCallback(feedItemCallback)

6
app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java

@ -112,7 +112,7 @@ public class StoryViewerFragment extends Fragment {
private StoryModel currentStory; private StoryModel currentStory;
private int slidePos; private int slidePos;
private int lastSlidePos; private int lastSlidePos;
private String url, username;
private String url;
private PollModel poll; private PollModel poll;
private QuestionModel question; private QuestionModel question;
private String[] mentions; private String[] mentions;
@ -498,7 +498,7 @@ public class StoryViewerFragment extends Fragment {
} }
} else if (!TextUtils.isEmpty(fragmentArgs.getProfileId()) && !TextUtils.isEmpty(fragmentArgs.getUsername())) { } else if (!TextUtils.isEmpty(fragmentArgs.getProfileId()) && !TextUtils.isEmpty(fragmentArgs.getUsername())) {
currentStoryMediaId = fragmentArgs.getProfileId(); currentStoryMediaId = fragmentArgs.getProfileId();
username = fragmentArgs.getUsername();
currentStoryUsername = fragmentArgs.getUsername();
} }
isHashtag = fragmentArgs.getIsHashtag(); isHashtag = fragmentArgs.getIsHashtag();
isLoc = fragmentArgs.getIsLoc(); isLoc = fragmentArgs.getIsLoc();
@ -534,7 +534,7 @@ public class StoryViewerFragment extends Fragment {
} }
}; };
storiesService.getUserStory(currentStoryMediaId, storiesService.getUserStory(currentStoryMediaId,
username,
currentStoryUsername,
isLoc, isLoc,
isHashtag, isHashtag,
isHighlight, isHighlight,

18
app/src/main/java/awais/instagrabber/fragments/settings/AboutFragment.java

@ -39,10 +39,10 @@ public class AboutFragment extends BasePreferencesFragment {
//thirdPartyCategory.setSummary(R.string.about_category_3pt_summary); //thirdPartyCategory.setSummary(R.string.about_category_3pt_summary);
thirdPartyCategory.setIconSpaceReserved(false); thirdPartyCategory.setIconSpaceReserved(false);
// alphabetical order!!! // alphabetical order!!!
thirdPartyCategory.addPreference(getACIPreference());
thirdPartyCategory.addPreference(getAutolinkPreference()); thirdPartyCategory.addPreference(getAutolinkPreference());
thirdPartyCategory.addPreference(getExoPlayerPreference()); thirdPartyCategory.addPreference(getExoPlayerPreference());
thirdPartyCategory.addPreference(getFrescoPreference()); thirdPartyCategory.addPreference(getFrescoPreference());
thirdPartyCategory.addPreference(getIcafePreference());
thirdPartyCategory.addPreference(getJsoupPreference()); thirdPartyCategory.addPreference(getJsoupPreference());
thirdPartyCategory.addPreference(getMDIPreference()); thirdPartyCategory.addPreference(getMDIPreference());
thirdPartyCategory.addPreference(getRetrofitPreference()); thirdPartyCategory.addPreference(getRetrofitPreference());
@ -101,7 +101,7 @@ public class AboutFragment extends BasePreferencesFragment {
if (context == null) return null; if (context == null) return null;
final Preference preference = new Preference(context); final Preference preference = new Preference(context);
preference.setTitle("Retrofit"); preference.setTitle("Retrofit");
preference.setSummary("Copyright 2013 Square, Inc. Apache Version 2.0.");
preference.setSummary("Copyright 2013 Square, Inc. Apache 2.0.");
preference.setIconSpaceReserved(false); preference.setIconSpaceReserved(false);
preference.setOnPreferenceClickListener(p -> { preference.setOnPreferenceClickListener(p -> {
final Intent intent = new Intent(Intent.ACTION_VIEW); final Intent intent = new Intent(Intent.ACTION_VIEW);
@ -149,7 +149,7 @@ public class AboutFragment extends BasePreferencesFragment {
if (context == null) return null; if (context == null) return null;
final Preference preference = new Preference(context); final Preference preference = new Preference(context);
preference.setTitle("ExoPlayer"); preference.setTitle("ExoPlayer");
preference.setSummary("Copyright (C) 2016 The Android Open Source Project. Apache Version 2.0.");
preference.setSummary("Copyright (C) 2016 The Android Open Source Project. Apache 2.0.");
preference.setIconSpaceReserved(false); preference.setIconSpaceReserved(false);
preference.setOnPreferenceClickListener(p -> { preference.setOnPreferenceClickListener(p -> {
final Intent intent = new Intent(Intent.ACTION_VIEW); final Intent intent = new Intent(Intent.ACTION_VIEW);
@ -165,7 +165,7 @@ public class AboutFragment extends BasePreferencesFragment {
if (context == null) return null; if (context == null) return null;
final Preference preference = new Preference(context); final Preference preference = new Preference(context);
preference.setTitle("Material Design Icons"); preference.setTitle("Material Design Icons");
preference.setSummary("Copyright (C) 2014 Austin Andrews & Google LLC. Apache Version 2.0.");
preference.setSummary("Copyright (C) 2014 Austin Andrews & Google LLC. Apache 2.0.");
preference.setIconSpaceReserved(false); preference.setIconSpaceReserved(false);
preference.setOnPreferenceClickListener(p -> { preference.setOnPreferenceClickListener(p -> {
final Intent intent = new Intent(Intent.ACTION_VIEW); final Intent intent = new Intent(Intent.ACTION_VIEW);
@ -181,7 +181,7 @@ public class AboutFragment extends BasePreferencesFragment {
if (context == null) return null; if (context == null) return null;
final Preference preference = new Preference(context); final Preference preference = new Preference(context);
preference.setTitle("AutoLinkTextViewV2"); preference.setTitle("AutoLinkTextViewV2");
preference.setSummary("Copyright (C) 2019 Arman Chatikyan. Apache Version 2.0.");
preference.setSummary("Copyright (C) 2019 Arman Chatikyan. Apache 2.0.");
preference.setIconSpaceReserved(false); preference.setIconSpaceReserved(false);
preference.setOnPreferenceClickListener(p -> { preference.setOnPreferenceClickListener(p -> {
final Intent intent = new Intent(Intent.ACTION_VIEW); final Intent intent = new Intent(Intent.ACTION_VIEW);
@ -192,16 +192,16 @@ public class AboutFragment extends BasePreferencesFragment {
return preference; return preference;
} }
private Preference getIcafePreference() {
private Preference getACIPreference() {
final Context context = getContext(); final Context context = getContext();
if (context == null) return null; if (context == null) return null;
final Preference preference = new Preference(context); final Preference preference = new Preference(context);
preference.setTitle("ICAFE");
preference.setSummary("Copyright (C) 2014-2019 Wen Yu. Eclipse Version 2.0.");
preference.setTitle("Apache Commons Imaging");
preference.setSummary("Copyright 2007-2020 The Apache Software Foundation. Apache 2.0.");
preference.setIconSpaceReserved(false); preference.setIconSpaceReserved(false);
preference.setOnPreferenceClickListener(p -> { preference.setOnPreferenceClickListener(p -> {
final Intent intent = new Intent(Intent.ACTION_VIEW); final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://github.com/dragon66/icafe"));
intent.setData(Uri.parse("https://commons.apache.org/proper/commons-imaging/"));
startActivity(intent); startActivity(intent);
return true; return true;
}); });

3
app/src/main/java/awais/instagrabber/repositories/TagsRepository.java

@ -24,4 +24,7 @@ public interface TagsRepository {
@GET("/api/v1/feed/tag/{tag}/") @GET("/api/v1/feed/tag/{tag}/")
Call<String> fetchPosts(@Path("tag") final String tag, Call<String> fetchPosts(@Path("tag") final String tag,
@QueryMap Map<String, String> queryParams); @QueryMap Map<String, String> queryParams);
@GET("/graphql/query/")
Call<String> fetchGraphQLPosts(@QueryMap(encoded = true) Map<String, String> queryParams);
} }

156
app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java

@ -665,6 +665,124 @@ public final class ResponseBodyUtils {
return feedModelBuilder.build(); return feedModelBuilder.build();
} }
public static FeedModel parseGraphQLItem(final JSONObject itemJson) throws JSONException {
if (itemJson == null) {
return null;
}
final JSONObject feedItem = itemJson.getJSONObject("node");
final String mediaType = feedItem.optString("__typename");
if (mediaType.isEmpty() || "GraphSuggestedUserFeedUnit".equals(mediaType))
return null;
final boolean isVideo = feedItem.optBoolean("is_video");
final long videoViews = feedItem.optLong("video_view_count", 0);
final String displayUrl = feedItem.optString("display_url");
if (TextUtils.isEmpty(displayUrl)) return null;
final String resourceUrl;
if (isVideo && feedItem.has("video_url")) {
resourceUrl = feedItem.getString("video_url");
} else {
resourceUrl = feedItem.has("display_resources") ? ResponseBodyUtils.getHighQualityImage(feedItem) : displayUrl;
}
ProfileModel profileModel = null;
if (feedItem.has("owner")) {
final JSONObject owner = feedItem.getJSONObject("owner");
profileModel = new ProfileModel(
owner.optBoolean("is_private"),
false, // if you can see it then you def follow
owner.optBoolean("is_verified"),
owner.getString(Constants.EXTRAS_ID),
owner.optString(Constants.EXTRAS_USERNAME),
owner.optString("full_name"),
null,
null,
owner.optString("profile_pic_url"),
null,
0,
0,
0,
false,
false,
false,
false);
}
JSONObject tempJsonObject = feedItem.optJSONObject("edge_media_preview_comment");
final long commentsCount = tempJsonObject != null ? tempJsonObject.optLong("count") : 0;
tempJsonObject = feedItem.optJSONObject("edge_media_preview_like");
final long likesCount = tempJsonObject != null ? tempJsonObject.optLong("count") : 0;
tempJsonObject = feedItem.optJSONObject("edge_media_to_caption");
final JSONArray captions = tempJsonObject != null ? tempJsonObject.getJSONArray("edges") : null;
String captionText = null;
if (captions != null && captions.length() > 0) {
if ((tempJsonObject = captions.optJSONObject(0)) != null &&
(tempJsonObject = tempJsonObject.optJSONObject("node")) != null) {
captionText = tempJsonObject.getString("text");
}
}
final JSONObject location = feedItem.optJSONObject("location");
// Log.d(TAG, "location: " + (location == null ? null : location.toString()));
String locationId = null;
String locationName = null;
if (location != null) {
locationName = location.optString("name");
if (location.has("id")) {
locationId = location.getString("id");
} else if (location.has("pk")) {
locationId = location.getString("pk");
}
// Log.d(TAG, "locationId: " + locationId);
}
int height = 0;
int width = 0;
final JSONObject dimensions = feedItem.optJSONObject("dimensions");
if (dimensions != null) {
height = dimensions.optInt("height");
width = dimensions.optInt("width");
}
String thumbnailUrl = null;
try {
thumbnailUrl = feedItem.getJSONArray("display_resources")
.getJSONObject(0)
.getString("src");
} catch (JSONException ignored) {}
final FeedModel.Builder feedModelBuilder = new FeedModel.Builder()
.setProfileModel(profileModel)
.setItemType(isVideo ? MediaItemType.MEDIA_TYPE_VIDEO
: MediaItemType.MEDIA_TYPE_IMAGE)
.setViewCount(videoViews)
.setPostId(feedItem.getString(Constants.EXTRAS_ID))
.setDisplayUrl(resourceUrl)
.setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl : displayUrl)
.setShortCode(feedItem.getString(Constants.EXTRAS_SHORTCODE))
.setPostCaption(captionText)
.setCommentsCount(commentsCount)
.setTimestamp(feedItem.optLong("taken_at_timestamp", -1))
.setLiked(feedItem.optBoolean("viewer_has_liked"))
.setBookmarked(feedItem.optBoolean("viewer_has_saved"))
.setLikesCount(likesCount)
.setLocationName(locationName)
.setLocationId(locationId)
.setImageHeight(height)
.setImageWidth(width);
final boolean isSlider = "GraphSidecar".equals(mediaType) && feedItem.has("edge_sidecar_to_children");
if (isSlider) {
feedModelBuilder.setItemType(MediaItemType.MEDIA_TYPE_SLIDER);
final JSONObject sidecar = feedItem.optJSONObject("edge_sidecar_to_children");
if (sidecar != null) {
final JSONArray children = sidecar.optJSONArray("edges");
if (children != null) {
final List<PostChild> sliderItems = getSliderItems(children);
feedModelBuilder.setSliderItems(sliderItems);
}
}
}
return feedModelBuilder.build();
}
private static List<PostChild> getChildPosts(final JSONObject mediaJson) throws JSONException { private static List<PostChild> getChildPosts(final JSONObject mediaJson) throws JSONException {
if (mediaJson == null) { if (mediaJson == null) {
return Collections.emptyList(); return Collections.emptyList();
@ -708,4 +826,42 @@ public final class ResponseBodyUtils {
.setWidth(childJson.optInt("original_width")) .setWidth(childJson.optInt("original_width"))
.build(); .build();
} }
// this is for graphql
@NonNull
private static List<PostChild> getSliderItems(final JSONArray children) throws JSONException {
final List<PostChild> sliderItems = new ArrayList<>();
for (int j = 0; j < children.length(); ++j) {
final JSONObject childNode = children.optJSONObject(j).getJSONObject("node");
final boolean isChildVideo = childNode.optBoolean("is_video");
int height = 0;
int width = 0;
final JSONObject dimensions = childNode.optJSONObject("dimensions");
if (dimensions != null) {
height = dimensions.optInt("height");
width = dimensions.optInt("width");
}
String thumbnailUrl = null;
try {
thumbnailUrl = childNode.getJSONArray("display_resources")
.getJSONObject(0)
.getString("src");
} catch (JSONException ignored) {}
final PostChild sliderItem = new PostChild.Builder()
.setItemType(isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO
: MediaItemType.MEDIA_TYPE_IMAGE)
.setPostId(childNode.getString(Constants.EXTRAS_ID))
.setDisplayUrl(isChildVideo ? childNode.getString("video_url")
: childNode.getString("display_url"))
.setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl
: childNode.getString("display_url"))
.setVideoViews(childNode.optLong("video_view_count", 0))
.setHeight(height)
.setWidth(width)
.build();
// Log.d(TAG, "getSliderItems: sliderItem: " + sliderItem);
sliderItems.add(sliderItem);
}
return sliderItems;
}
} }

150
app/src/main/java/awais/instagrabber/webservices/FeedService.java

@ -150,157 +150,13 @@ public class FeedService extends BaseService {
final JSONArray feedItems = timelineFeed.getJSONArray("edges"); final JSONArray feedItems = timelineFeed.getJSONArray("edges");
for (int i = 0; i < feedItems.length(); ++i) { for (int i = 0; i < feedItems.length(); ++i) {
final JSONObject feedItem = feedItems.getJSONObject(i).getJSONObject("node");
final String mediaType = feedItem.optString("__typename");
if (mediaType.isEmpty() || "GraphSuggestedUserFeedUnit".equals(mediaType))
final JSONObject itemJson = feedItems.optJSONObject(i);
if (itemJson == null) {
continue; continue;
final boolean isVideo = feedItem.optBoolean("is_video");
final long videoViews = feedItem.optLong("video_view_count", 0);
final String displayUrl = feedItem.optString("display_url");
if (TextUtils.isEmpty(displayUrl)) continue;
final String resourceUrl;
if (isVideo) {
resourceUrl = feedItem.getString("video_url");
} else {
resourceUrl = feedItem.has("display_resources") ? ResponseBodyUtils.getHighQualityImage(feedItem) : displayUrl;
}
ProfileModel profileModel = null;
if (feedItem.has("owner")) {
final JSONObject owner = feedItem.getJSONObject("owner");
profileModel = new ProfileModel(
owner.optBoolean("is_private"),
false, // if you can see it then you def follow
owner.optBoolean("is_verified"),
owner.getString(Constants.EXTRAS_ID),
owner.getString(Constants.EXTRAS_USERNAME),
owner.optString("full_name"),
null,
null,
owner.getString("profile_pic_url"),
null,
0,
0,
0,
false,
false,
false,
false);
}
JSONObject tempJsonObject = feedItem.optJSONObject("edge_media_preview_comment");
final long commentsCount = tempJsonObject != null ? tempJsonObject.optLong("count") : 0;
tempJsonObject = feedItem.optJSONObject("edge_media_preview_like");
final long likesCount = tempJsonObject != null ? tempJsonObject.optLong("count") : 0;
tempJsonObject = feedItem.optJSONObject("edge_media_to_caption");
final JSONArray captions = tempJsonObject != null ? tempJsonObject.getJSONArray("edges") : null;
String captionText = null;
if (captions != null && captions.length() > 0) {
if ((tempJsonObject = captions.optJSONObject(0)) != null &&
(tempJsonObject = tempJsonObject.optJSONObject("node")) != null) {
captionText = tempJsonObject.getString("text");
}
}
final JSONObject location = feedItem.optJSONObject("location");
// Log.d(TAG, "location: " + (location == null ? null : location.toString()));
String locationId = null;
String locationName = null;
if (location != null) {
locationName = location.optString("name");
if (location.has("id")) {
locationId = location.getString("id");
} else if (location.has("pk")) {
locationId = location.getString("pk");
}
// Log.d(TAG, "locationId: " + locationId);
} }
int height = 0;
int width = 0;
final JSONObject dimensions = feedItem.optJSONObject("dimensions");
if (dimensions != null) {
height = dimensions.optInt("height");
width = dimensions.optInt("width");
}
String thumbnailUrl = null;
try {
thumbnailUrl = feedItem.getJSONArray("display_resources")
.getJSONObject(0)
.getString("src");
} catch (JSONException ignored) {}
final FeedModel.Builder feedModelBuilder = new FeedModel.Builder()
.setProfileModel(profileModel)
.setItemType(isVideo ? MediaItemType.MEDIA_TYPE_VIDEO
: MediaItemType.MEDIA_TYPE_IMAGE)
.setViewCount(videoViews)
.setPostId(feedItem.getString(Constants.EXTRAS_ID))
.setDisplayUrl(resourceUrl)
.setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl : displayUrl)
.setShortCode(feedItem.getString(Constants.EXTRAS_SHORTCODE))
.setPostCaption(captionText)
.setCommentsCount(commentsCount)
.setTimestamp(feedItem.optLong("taken_at_timestamp", -1))
.setLiked(feedItem.getBoolean("viewer_has_liked"))
.setBookmarked(feedItem.getBoolean("viewer_has_saved"))
.setLikesCount(likesCount)
.setLocationName(locationName)
.setLocationId(locationId)
.setImageHeight(height)
.setImageWidth(width);
final boolean isSlider = "GraphSidecar".equals(mediaType) && feedItem.has("edge_sidecar_to_children");
if (isSlider) {
feedModelBuilder.setItemType(MediaItemType.MEDIA_TYPE_SLIDER);
final JSONObject sidecar = feedItem.optJSONObject("edge_sidecar_to_children");
if (sidecar != null) {
final JSONArray children = sidecar.optJSONArray("edges");
if (children != null) {
final List<PostChild> sliderItems = getSliderItems(children);
feedModelBuilder.setSliderItems(sliderItems);
}
}
}
final FeedModel feedModel = feedModelBuilder.build();
final FeedModel feedModel = ResponseBodyUtils.parseItem(itemJson);
feedModels.add(feedModel); feedModels.add(feedModel);
} }
return new PostsFetchResponse(feedModels, hasNextPage, endCursor); return new PostsFetchResponse(feedModels, hasNextPage, endCursor);
} }
@NonNull
private List<PostChild> getSliderItems(final JSONArray children) throws JSONException {
final List<PostChild> sliderItems = new ArrayList<>();
for (int j = 0; j < children.length(); ++j) {
final JSONObject childNode = children.optJSONObject(j).getJSONObject("node");
final boolean isChildVideo = childNode.optBoolean("is_video");
int height = 0;
int width = 0;
final JSONObject dimensions = childNode.optJSONObject("dimensions");
if (dimensions != null) {
height = dimensions.optInt("height");
width = dimensions.optInt("width");
}
String thumbnailUrl = null;
try {
thumbnailUrl = childNode.getJSONArray("display_resources")
.getJSONObject(0)
.getString("src");
} catch (JSONException ignored) {}
final PostChild sliderItem = new PostChild.Builder()
.setItemType(isChildVideo ? MediaItemType.MEDIA_TYPE_VIDEO
: MediaItemType.MEDIA_TYPE_IMAGE)
.setPostId(childNode.getString(Constants.EXTRAS_ID))
.setDisplayUrl(isChildVideo ? childNode.getString("video_url")
: childNode.getString("display_url"))
.setThumbnailUrl(thumbnailUrl != null ? thumbnailUrl
: childNode.getString("display_url"))
.setVideoViews(childNode.optLong("video_view_count", 0))
.setHeight(height)
.setWidth(width)
.build();
// Log.d(TAG, "getSliderItems: sliderItem: " + sliderItem);
sliderItems.add(sliderItem);
}
return sliderItems;
}
} }

78
app/src/main/java/awais/instagrabber/webservices/TagsService.java

@ -12,7 +12,9 @@ import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.FeedModel;
@ -186,6 +188,82 @@ public class TagsService extends BaseService {
return feedModels; return feedModels;
} }
public void fetchGraphQLPosts(@NonNull final String tag,
final String maxId,
final ServiceCallback<TagPostsFetchResponse> callback) {
final Map<String, String> queryMap = new HashMap<>();
queryMap.put("query_hash", "9b498c08113f1e09617a1703c22b2f32");
queryMap.put("variables", "{" +
"\"tag_name\":\"" + tag + "\"," +
"\"first\":25," +
"\"after\":\"" + (maxId == null ? "" : maxId) + "\"" +
"}");
final Call<String> request = webRepository.fetchGraphQLPosts(queryMap);
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> 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<String> 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<FeedModel> items = parseGraphQLItems(itemsJson);
return new TagPostsFetchResponse(
moreAvailable,
nextMaxId,
numResults,
status,
items
);
}
private List<FeedModel> parseGraphQLItems(final JSONArray items) throws JSONException {
if (items == null) {
return Collections.emptyList();
}
final List<FeedModel> 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 { public static class TagPostsFetchResponse {
private boolean moreAvailable; private boolean moreAvailable;
private String nextMaxId; private String nextMaxId;

Loading…
Cancel
Save