mirror of
https://github.com/KokaKiwi/BarInsta
synced 2026-03-05 12:01:35 +00:00
close #259, release prep, awaiting crowdin
This commit is contained in:
parent
d9c510d9cb
commit
f908e7b643
13 changed files with 197 additions and 377 deletions
|
|
@ -14,7 +14,8 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService {
|
|||
private final TagsService tagsService;
|
||||
private final HashtagModel hashtagModel;
|
||||
private String nextMaxId;
|
||||
private boolean moreAvailable, isLoggedIn;
|
||||
private boolean moreAvailable;
|
||||
private final boolean isLoggedIn;
|
||||
|
||||
public HashtagPostFetchService(final HashtagModel hashtagModel, final boolean isLoggedIn) {
|
||||
this.hashtagModel = hashtagModel;
|
||||
|
|
@ -24,7 +25,7 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService {
|
|||
|
||||
@Override
|
||||
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||
if (isLoggedIn) tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, new ServiceCallback<TagPostsFetchResponse>() {
|
||||
final ServiceCallback cb = new ServiceCallback<TagPostsFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final TagPostsFetchResponse result) {
|
||||
if (result == null) return;
|
||||
|
|
@ -42,26 +43,9 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService {
|
|||
fetchListener.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
else tagsService.fetchGraphQLPosts(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);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
if (isLoggedIn) tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb);
|
||||
else tagsService.fetchGraphQLPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -15,15 +15,17 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService {
|
|||
private final LocationModel locationModel;
|
||||
private String nextMaxId;
|
||||
private boolean moreAvailable;
|
||||
private final boolean isLoggedIn;
|
||||
|
||||
public LocationPostFetchService(final LocationModel locationModel) {
|
||||
public LocationPostFetchService(final LocationModel locationModel, final boolean isLoggedIn) {
|
||||
this.locationModel = locationModel;
|
||||
this.isLoggedIn = isLoggedIn;
|
||||
locationService = LocationService.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch(final FetchListener<List<FeedModel>> fetchListener) {
|
||||
locationService.fetchPosts(locationModel.getId(), nextMaxId, new ServiceCallback<LocationPostsFetchResponse>() {
|
||||
final ServiceCallback cb = new ServiceCallback<LocationPostsFetchResponse>() {
|
||||
@Override
|
||||
public void onSuccess(final LocationPostsFetchResponse result) {
|
||||
if (result == null) return;
|
||||
|
|
@ -41,7 +43,9 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService {
|
|||
fetchListener.onFailure(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
if (isLoggedIn) locationService.fetchPosts(locationModel.getId(), nextMaxId, cb);
|
||||
else locationService.fetchGraphQLPosts(locationModel.getId(), nextMaxId, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import awais.instagrabber.activities.MainActivity;
|
|||
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||
import awais.instagrabber.asyncs.HashtagFetcher;
|
||||
import awais.instagrabber.asyncs.HashtagPostFetchService;
|
||||
import awais.instagrabber.asyncs.PostFetcher;
|
||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
||||
import awais.instagrabber.databinding.FragmentHashtagBinding;
|
||||
|
|
@ -81,6 +82,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||
private NestedCoordinatorLayout root;
|
||||
private boolean shouldRefresh = true;
|
||||
private boolean hasStories = false;
|
||||
private boolean opening = false;
|
||||
private String hashtag;
|
||||
private HashtagModel hashtagModel;
|
||||
private ActionMode actionMode;
|
||||
|
|
@ -199,16 +201,37 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
|||
final View profilePicView,
|
||||
final View mainPostImage,
|
||||
final int position) {
|
||||
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||
.builder(feedModel);
|
||||
if (position >= 0) {
|
||||
builder.setPosition(position);
|
||||
if (opening) return;
|
||||
else if (TextUtils.isEmpty(feedModel.getProfileModel().getUsername())) {
|
||||
opening = true;
|
||||
new PostFetcher(feedModel.getShortCode(), newFeedModel -> {
|
||||
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||
.builder(newFeedModel);
|
||||
if (position >= 0) {
|
||||
builder.setPosition(position);
|
||||
}
|
||||
final PostViewV2Fragment fragment = builder
|
||||
.setSharedProfilePicElement(profilePicView)
|
||||
.setSharedMainPostElement(mainPostImage)
|
||||
.build();
|
||||
fragment.show(getChildFragmentManager(), "post_view");
|
||||
opening = false;
|
||||
}).execute();
|
||||
}
|
||||
else {
|
||||
opening = true;
|
||||
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||
.builder(feedModel);
|
||||
if (position >= 0) {
|
||||
builder.setPosition(position);
|
||||
}
|
||||
final PostViewV2Fragment fragment = builder
|
||||
.setSharedProfilePicElement(profilePicView)
|
||||
.setSharedMainPostElement(mainPostImage)
|
||||
.build();
|
||||
fragment.show(getChildFragmentManager(), "post_view");
|
||||
opening = false;
|
||||
}
|
||||
final PostViewV2Fragment fragment = builder
|
||||
.setSharedProfilePicElement(profilePicView)
|
||||
.setSharedMainPostElement(mainPostImage)
|
||||
.build();
|
||||
fragment.show(getChildFragmentManager(), "post_view");
|
||||
}
|
||||
};
|
||||
private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ import awais.instagrabber.activities.MainActivity;
|
|||
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||
import awais.instagrabber.asyncs.LocationFetcher;
|
||||
import awais.instagrabber.asyncs.LocationPostFetchService;
|
||||
import awais.instagrabber.asyncs.PostFetcher;
|
||||
import awais.instagrabber.customviews.PrimaryActionModeCallback;
|
||||
import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout;
|
||||
import awais.instagrabber.databinding.FragmentLocationBinding;
|
||||
|
|
@ -81,6 +82,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||
private NestedCoordinatorLayout root;
|
||||
private boolean shouldRefresh = true;
|
||||
private boolean hasStories = false;
|
||||
private boolean opening = false;
|
||||
private String locationId;
|
||||
private LocationModel locationModel;
|
||||
private ActionMode actionMode;
|
||||
|
|
@ -197,16 +199,37 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||
final View profilePicView,
|
||||
final View mainPostImage,
|
||||
final int position) {
|
||||
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||
.builder(feedModel);
|
||||
if (position >= 0) {
|
||||
builder.setPosition(position);
|
||||
if (opening) return;
|
||||
else if (TextUtils.isEmpty(feedModel.getProfileModel().getUsername())) {
|
||||
opening = true;
|
||||
new PostFetcher(feedModel.getShortCode(), newFeedModel -> {
|
||||
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||
.builder(newFeedModel);
|
||||
if (position >= 0) {
|
||||
builder.setPosition(position);
|
||||
}
|
||||
final PostViewV2Fragment fragment = builder
|
||||
.setSharedProfilePicElement(profilePicView)
|
||||
.setSharedMainPostElement(mainPostImage)
|
||||
.build();
|
||||
fragment.show(getChildFragmentManager(), "post_view");
|
||||
opening = false;
|
||||
}).execute();
|
||||
}
|
||||
else {
|
||||
opening = true;
|
||||
final PostViewV2Fragment.Builder builder = PostViewV2Fragment
|
||||
.builder(feedModel);
|
||||
if (position >= 0) {
|
||||
builder.setPosition(position);
|
||||
}
|
||||
final PostViewV2Fragment fragment = builder
|
||||
.setSharedProfilePicElement(profilePicView)
|
||||
.setSharedMainPostElement(mainPostImage)
|
||||
.build();
|
||||
fragment.show(getChildFragmentManager(), "post_view");
|
||||
opening = false;
|
||||
}
|
||||
final PostViewV2Fragment fragment = builder
|
||||
.setSharedProfilePicElement(profilePicView)
|
||||
.setSharedMainPostElement(mainPostImage)
|
||||
.build();
|
||||
fragment.show(getChildFragmentManager(), "post_view");
|
||||
}
|
||||
};
|
||||
private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() {
|
||||
|
|
@ -335,7 +358,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||
private void setupPosts() {
|
||||
binding.posts.setViewModelStoreOwner(this)
|
||||
.setLifeCycleOwner(this)
|
||||
.setPostFetchService(new LocationPostFetchService(locationModel))
|
||||
.setPostFetchService(new LocationPostFetchService(locationModel, isLoggedIn))
|
||||
.setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_LOCATION_POSTS_LAYOUT)))
|
||||
.addFetchStatusChangeListener(fetching -> updateSwipeRefreshState())
|
||||
.setFeedItemCallback(feedItemCallback)
|
||||
|
|
|
|||
|
|
@ -12,4 +12,7 @@ public interface LocationRepository {
|
|||
@GET("/api/v1/feed/location/{location}/")
|
||||
Call<String> fetchPosts(@Path("location") final String locationId,
|
||||
@QueryMap Map<String, String> queryParams);
|
||||
|
||||
@GET("/graphql/query/")
|
||||
Call<String> fetchGraphQLPosts(@QueryMap(encoded = true) Map<String, String> queryParams);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,9 +58,9 @@ public final class Constants {
|
|||
// spoof
|
||||
public static final String USER_AGENT = "Mozilla/5.0 (Linux; Android 8.1.0; motorola one Build/OPKS28.63-18-3; wv) " +
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 " +
|
||||
"Instagram 165.1.0.29.119 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 253447818)";
|
||||
"Instagram 166.1.0.42.245 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 256099205)";
|
||||
public static final String I_USER_AGENT =
|
||||
"Instagram 165.1.0.29.119 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 253447818)";
|
||||
"Instagram 166.1.0.42.245 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 256099205)";
|
||||
public static final String A_USER_AGENT = "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me";
|
||||
// see https://github.com/dilame/instagram-private-api/blob/master/src/core/constants.ts
|
||||
public static final String SUPPORTED_CAPABILITIES = "[ { \"name\": \"SUPPORTED_SDK_VERSIONS\", \"value\":" +
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public final class LocaleUtils {
|
|||
if (appLanguageIndex == 1) return "en";
|
||||
if (appLanguageIndex == 2) return "fr";
|
||||
if (appLanguageIndex == 3) return "es";
|
||||
if (appLanguageIndex == 4) return "zh";
|
||||
if (appLanguageIndex == 4) return "zh-rCN";
|
||||
if (appLanguageIndex == 5) return "in";
|
||||
if (appLanguageIndex == 6) return "it";
|
||||
if (appLanguageIndex == 7) return "de";
|
||||
|
|
@ -72,7 +72,9 @@ public final class LocaleUtils {
|
|||
if (appLanguageIndex == 11) return "fa";
|
||||
if (appLanguageIndex == 12) return "mk";
|
||||
if (appLanguageIndex == 13) return "vi";
|
||||
if (appLanguageIndex == 14) return "hi";
|
||||
if (appLanguageIndex == 14) return "zh-rTW";
|
||||
if (appLanguageIndex == 15) return "hi";
|
||||
if (appLanguageIndex == 16) return "cs";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -671,8 +671,6 @@ public final class ResponseBodyUtils {
|
|||
}
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -12,7 +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;
|
||||
|
|
@ -27,7 +29,7 @@ import retrofit2.Retrofit;
|
|||
public class LocationService extends BaseService {
|
||||
private static final String TAG = "LocationService";
|
||||
|
||||
private final LocationRepository repository;
|
||||
private final LocationRepository repository, webRepository;
|
||||
|
||||
private static LocationService instance;
|
||||
|
||||
|
|
@ -36,6 +38,10 @@ 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() {
|
||||
|
|
@ -48,7 +54,7 @@ public class LocationService extends BaseService {
|
|||
public void fetchPosts(@NonNull final String locationId,
|
||||
final String maxId,
|
||||
final ServiceCallback<LocationPostsFetchResponse> callback) {
|
||||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.<String, String>builder();
|
||||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
if (!TextUtils.isEmpty(maxId)) {
|
||||
builder.put("max_id", maxId);
|
||||
}
|
||||
|
|
@ -117,6 +123,82 @@ public class LocationService extends BaseService {
|
|||
return feedModels;
|
||||
}
|
||||
|
||||
public void fetchGraphQLPosts(@NonNull final String locationId,
|
||||
final String maxId,
|
||||
final ServiceCallback<LocationPostsFetchResponse> callback) {
|
||||
final Map<String, String> queryMap = new HashMap<>();
|
||||
queryMap.put("query_hash", "36bd0f2bf5911908de389b8ceaa3be6d");
|
||||
queryMap.put("variables", "{" +
|
||||
"\"id\":\"" + locationId + "\"," +
|
||||
"\"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 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<String> 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<FeedModel> items = parseGraphQLItems(itemsJson);
|
||||
return new LocationPostsFetchResponse(
|
||||
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 LocationPostsFetchResponse {
|
||||
private boolean moreAvailable;
|
||||
private String nextMaxId;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue