Austin Huang
4 years ago
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
8 changed files with 214 additions and 421 deletions
-
42app/src/main/java/awais/instagrabber/asyncs/ProfilePostFetchService.java
-
49app/src/main/java/awais/instagrabber/asyncs/SavedPostFetchService.java
-
42app/src/main/java/awais/instagrabber/fragments/SavedCollectionsFragment.java
-
40app/src/main/java/awais/instagrabber/repositories/ProfileRepository.java
-
40app/src/main/java/awais/instagrabber/repositories/ProfileService.kt
-
2app/src/main/java/awais/instagrabber/webservices/CollectionService.java
-
121app/src/main/java/awais/instagrabber/webservices/ProfileRepository.kt
-
299app/src/main/java/awais/instagrabber/webservices/ProfileService.java
@ -1,40 +0,0 @@ |
|||
package awais.instagrabber.repositories; |
|||
|
|||
import java.util.Map; |
|||
|
|||
import awais.instagrabber.repositories.responses.WrappedFeedResponse; |
|||
import awais.instagrabber.repositories.responses.saved.CollectionsListResponse; |
|||
import awais.instagrabber.repositories.responses.UserFeedResponse; |
|||
import retrofit2.Call; |
|||
import retrofit2.http.FieldMap; |
|||
import retrofit2.http.FormUrlEncoded; |
|||
import retrofit2.http.GET; |
|||
import retrofit2.http.Path; |
|||
import retrofit2.http.POST; |
|||
import retrofit2.http.QueryMap; |
|||
|
|||
public interface ProfileRepository { |
|||
|
|||
@GET("/api/v1/feed/user/{uid}/") |
|||
Call<UserFeedResponse> fetch(@Path("uid") final long uid, @QueryMap Map<String, String> queryParams); |
|||
|
|||
@GET("/api/v1/feed/saved/") |
|||
Call<WrappedFeedResponse> fetchSaved(@QueryMap Map<String, String> queryParams); |
|||
|
|||
@GET("/api/v1/feed/collection/{collectionId}/") |
|||
Call<WrappedFeedResponse> fetchSavedCollection(@Path("collectionId") final String collectionId, |
|||
@QueryMap Map<String, String> queryParams); |
|||
|
|||
@GET("/api/v1/feed/liked/") |
|||
Call<UserFeedResponse> fetchLiked(@QueryMap Map<String, String> queryParams); |
|||
|
|||
@GET("/api/v1/usertags/{profileId}/feed/") |
|||
Call<UserFeedResponse> fetchTagged(@Path("profileId") final long profileId, @QueryMap Map<String, String> queryParams); |
|||
|
|||
@GET("/api/v1/collections/list/") |
|||
Call<CollectionsListResponse> fetchCollections(@QueryMap Map<String, String> queryParams); |
|||
|
|||
@FormUrlEncoded |
|||
@POST("/api/v1/collections/create/") |
|||
Call<String> createCollection(@FieldMap Map<String, String> signedForm); |
|||
} |
@ -0,0 +1,40 @@ |
|||
package awais.instagrabber.repositories |
|||
|
|||
import awais.instagrabber.repositories.responses.UserFeedResponse |
|||
import awais.instagrabber.repositories.responses.WrappedFeedResponse |
|||
import awais.instagrabber.repositories.responses.saved.CollectionsListResponse |
|||
import retrofit2.Call |
|||
import retrofit2.http.* |
|||
|
|||
interface ProfileService { |
|||
@GET("/api/v1/feed/user/{uid}/") |
|||
suspend fun fetch( |
|||
@Path("uid") uid: Long, |
|||
@QueryMap queryParams: Map<String?, String?>? |
|||
): UserFeedResponse? |
|||
|
|||
@GET("/api/v1/feed/saved/") |
|||
suspend fun fetchSaved(@QueryMap queryParams: Map<String?, String?>?): WrappedFeedResponse? |
|||
|
|||
@GET("/api/v1/feed/collection/{collectionId}/") |
|||
suspend fun fetchSavedCollection( |
|||
@Path("collectionId") collectionId: String?, |
|||
@QueryMap queryParams: Map<String?, String?>? |
|||
): WrappedFeedResponse? |
|||
|
|||
@GET("/api/v1/feed/liked/") |
|||
suspend fun fetchLiked(@QueryMap queryParams: Map<String?, String?>?): UserFeedResponse? |
|||
|
|||
@GET("/api/v1/usertags/{profileId}/feed/") |
|||
suspend fun fetchTagged( |
|||
@Path("profileId") profileId: Long, |
|||
@QueryMap queryParams: Map<String?, String?>? |
|||
): UserFeedResponse? |
|||
|
|||
@GET("/api/v1/collections/list/") |
|||
suspend fun fetchCollections(@QueryMap queryParams: Map<String?, String?>?): CollectionsListResponse? |
|||
|
|||
@FormUrlEncoded |
|||
@POST("/api/v1/collections/create/") |
|||
suspend fun createCollection(@FieldMap signedForm: Map<String?, String?>?): String? |
|||
} |
@ -0,0 +1,121 @@ |
|||
package awais.instagrabber.webservices |
|||
|
|||
import awais.instagrabber.repositories.ProfileService |
|||
import awais.instagrabber.repositories.responses.Media |
|||
import awais.instagrabber.repositories.responses.PostsFetchResponse |
|||
import awais.instagrabber.repositories.responses.WrappedMedia |
|||
import awais.instagrabber.repositories.responses.saved.CollectionsListResponse |
|||
import awais.instagrabber.utils.Utils |
|||
import com.google.common.collect.ImmutableMap |
|||
import java.util.* |
|||
import java.util.stream.Collectors |
|||
|
|||
class ProfileRepository private constructor(private val repository: ProfileService) { |
|||
suspend fun fetchPosts( |
|||
userId: Long, |
|||
maxId: String? |
|||
): PostsFetchResponse? { |
|||
val builder = ImmutableMap.builder<String?, String?>() |
|||
if (!maxId.isNullOrEmpty()) { |
|||
builder.put("max_id", maxId) |
|||
} |
|||
val body = repository.fetch(userId, builder.build()) ?: return null |
|||
return PostsFetchResponse( |
|||
body.items, |
|||
body.moreAvailable, |
|||
body.nextMaxId |
|||
) |
|||
} |
|||
|
|||
suspend fun fetchSaved(maxId: String?, collectionId: String): PostsFetchResponse? { |
|||
val builder = ImmutableMap.builder<String?, String?>() |
|||
if (!maxId.isNullOrEmpty()) { |
|||
builder.put("max_id", maxId) |
|||
} |
|||
val userFeedResponse = if (collectionId.isNullOrEmpty() || collectionId == "ALL_MEDIA_AUTO_COLLECTION") |
|||
(repository.fetchSaved(builder.build()) ?: return null) |
|||
else repository.fetchSavedCollection(collectionId, builder.build()) ?: return null |
|||
val items = userFeedResponse.items |
|||
val posts: List<Media> = if (items == null) { |
|||
emptyList() |
|||
} else { |
|||
items.stream() |
|||
.map(WrappedMedia::media) |
|||
.filter { obj: Media? -> Objects.nonNull(obj) } |
|||
.collect(Collectors.toList()) |
|||
} |
|||
return PostsFetchResponse( |
|||
posts, |
|||
userFeedResponse.isMoreAvailable, |
|||
userFeedResponse.nextMaxId |
|||
) |
|||
} |
|||
|
|||
suspend fun fetchCollections(maxId: String?): CollectionsListResponse? { |
|||
val builder = ImmutableMap.builder<String?, String?>() |
|||
if (!maxId.isNullOrEmpty()) { |
|||
builder.put("max_id", maxId) |
|||
} |
|||
builder.put( |
|||
"collection_types", |
|||
"[\"ALL_MEDIA_AUTO_COLLECTION\",\"MEDIA\",\"PRODUCT_AUTO_COLLECTION\"]" |
|||
) |
|||
return repository.fetchCollections(builder.build()) |
|||
} |
|||
|
|||
suspend fun createCollection( |
|||
name: String, |
|||
deviceUuid: String, |
|||
userId: Long, |
|||
csrfToken: String |
|||
): String? { |
|||
val form: MutableMap<String, Any> = HashMap(6) |
|||
form["_csrftoken"] = csrfToken |
|||
form["_uuid"] = deviceUuid |
|||
form["_uid"] = userId |
|||
form["collection_visibility"] = |
|||
"0" // 1 for public, planned for future but currently inexistant |
|||
form["module_name"] = "collection_create" |
|||
form["name"] = name |
|||
val signedForm = Utils.sign(form) |
|||
return repository.createCollection(signedForm) |
|||
} |
|||
|
|||
suspend fun fetchLiked(maxId: String?): PostsFetchResponse? { |
|||
val builder = ImmutableMap.builder<String?, String?>() |
|||
if (!maxId.isNullOrEmpty()) { |
|||
builder.put("max_id", maxId) |
|||
} |
|||
val userFeedResponse = repository.fetchLiked(builder.build()) ?: return null |
|||
return PostsFetchResponse( |
|||
userFeedResponse.items, |
|||
userFeedResponse.moreAvailable, |
|||
userFeedResponse.nextMaxId |
|||
) |
|||
} |
|||
|
|||
suspend fun fetchTagged(profileId: Long, maxId: String?): PostsFetchResponse? { |
|||
val builder = ImmutableMap.builder<String?, String?>() |
|||
if (!maxId.isNullOrEmpty()) { |
|||
builder.put("max_id", maxId) |
|||
} |
|||
val userFeedResponse = repository.fetchTagged(profileId, builder.build()) ?: return null |
|||
return PostsFetchResponse( |
|||
userFeedResponse.items, |
|||
userFeedResponse.moreAvailable, |
|||
userFeedResponse.nextMaxId |
|||
) |
|||
} |
|||
|
|||
companion object { |
|||
@Volatile |
|||
private var INSTANCE: ProfileRepository? = null |
|||
|
|||
fun getInstance(): ProfileRepository { |
|||
return INSTANCE ?: synchronized(this) { |
|||
val service: ProfileService = RetrofitFactory.retrofit.create(ProfileService::class.java) |
|||
ProfileRepository(service).also { INSTANCE = it } |
|||
} |
|||
} |
|||
} |
|||
} |
@ -1,299 +0,0 @@ |
|||
package awais.instagrabber.webservices; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
|
|||
import com.google.common.collect.ImmutableMap; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Objects; |
|||
import java.util.stream.Collectors; |
|||
|
|||
import awais.instagrabber.repositories.ProfileRepository; |
|||
import awais.instagrabber.repositories.responses.Media; |
|||
import awais.instagrabber.repositories.responses.PostsFetchResponse; |
|||
import awais.instagrabber.repositories.responses.UserFeedResponse; |
|||
import awais.instagrabber.repositories.responses.WrappedFeedResponse; |
|||
import awais.instagrabber.repositories.responses.WrappedMedia; |
|||
import awais.instagrabber.repositories.responses.saved.CollectionsListResponse; |
|||
import awais.instagrabber.utils.TextUtils; |
|||
import awais.instagrabber.utils.Utils; |
|||
import retrofit2.Call; |
|||
import retrofit2.Callback; |
|||
import retrofit2.Response; |
|||
|
|||
public class ProfileService { |
|||
private static final String TAG = "ProfileService"; |
|||
|
|||
private final ProfileRepository repository; |
|||
|
|||
private static ProfileService instance; |
|||
|
|||
private ProfileService() { |
|||
repository = RetrofitFactory.INSTANCE |
|||
.getRetrofit() |
|||
.create(ProfileRepository.class); |
|||
} |
|||
|
|||
public static ProfileService getInstance() { |
|||
if (instance == null) { |
|||
instance = new ProfileService(); |
|||
} |
|||
return instance; |
|||
} |
|||
|
|||
public void fetchPosts(final long userId, |
|||
final String maxId, |
|||
final ServiceCallback<PostsFetchResponse> callback) { |
|||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); |
|||
if (!TextUtils.isEmpty(maxId)) { |
|||
builder.put("max_id", maxId); |
|||
} |
|||
final Call<UserFeedResponse> request = repository.fetch(userId, builder.build()); |
|||
request.enqueue(new Callback<UserFeedResponse>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<UserFeedResponse> call, @NonNull final Response<UserFeedResponse> response) { |
|||
if (callback == null) return; |
|||
final UserFeedResponse body = response.body(); |
|||
if (body == null) { |
|||
callback.onSuccess(null); |
|||
return; |
|||
} |
|||
callback.onSuccess(new PostsFetchResponse( |
|||
body.getItems(), |
|||
body.getMoreAvailable(), |
|||
body.getNextMaxId() |
|||
)); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<UserFeedResponse> call, @NonNull final Throwable t) { |
|||
if (callback != null) { |
|||
callback.onFailure(t); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public void fetchSaved(final String maxId, |
|||
final String collectionId, |
|||
final ServiceCallback<PostsFetchResponse> callback) { |
|||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); |
|||
Call<WrappedFeedResponse> request = null; |
|||
if (!TextUtils.isEmpty(maxId)) { |
|||
builder.put("max_id", maxId); |
|||
} |
|||
if (TextUtils.isEmpty(collectionId) || collectionId.equals("ALL_MEDIA_AUTO_COLLECTION")) request = repository.fetchSaved(builder.build()); |
|||
else request = repository.fetchSavedCollection(collectionId, builder.build()); |
|||
request.enqueue(new Callback<WrappedFeedResponse>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<WrappedFeedResponse> call, @NonNull final Response<WrappedFeedResponse> response) { |
|||
if (callback == null) return; |
|||
final WrappedFeedResponse userFeedResponse = response.body(); |
|||
if (userFeedResponse == null) { |
|||
callback.onSuccess(null); |
|||
return; |
|||
} |
|||
final List<WrappedMedia> items = userFeedResponse.getItems(); |
|||
final List<Media> posts; |
|||
if (items == null) { |
|||
posts = Collections.emptyList(); |
|||
} else { |
|||
posts = items.stream() |
|||
.map(WrappedMedia::getMedia) |
|||
.filter(Objects::nonNull) |
|||
.collect(Collectors.toList()); |
|||
} |
|||
callback.onSuccess(new PostsFetchResponse( |
|||
posts, |
|||
userFeedResponse.isMoreAvailable(), |
|||
userFeedResponse.getNextMaxId() |
|||
)); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<WrappedFeedResponse> call, @NonNull final Throwable t) { |
|||
if (callback != null) { |
|||
callback.onFailure(t); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public void fetchCollections(final String maxId, |
|||
final ServiceCallback<CollectionsListResponse> callback) { |
|||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); |
|||
if (!TextUtils.isEmpty(maxId)) { |
|||
builder.put("max_id", maxId); |
|||
} |
|||
builder.put("collection_types", "[\"ALL_MEDIA_AUTO_COLLECTION\",\"MEDIA\",\"PRODUCT_AUTO_COLLECTION\"]"); |
|||
final Call<CollectionsListResponse> request = repository.fetchCollections(builder.build()); |
|||
request.enqueue(new Callback<CollectionsListResponse>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<CollectionsListResponse> call, @NonNull final Response<CollectionsListResponse> response) { |
|||
if (callback == null) return; |
|||
final CollectionsListResponse collectionsListResponse = response.body(); |
|||
if (collectionsListResponse == null) { |
|||
callback.onSuccess(null); |
|||
return; |
|||
} |
|||
callback.onSuccess(collectionsListResponse); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<CollectionsListResponse> call, @NonNull final Throwable t) { |
|||
if (callback != null) { |
|||
callback.onFailure(t); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public void createCollection(final String name, |
|||
final String deviceUuid, |
|||
final long userId, |
|||
final String csrfToken, |
|||
final ServiceCallback<String> callback) { |
|||
final Map<String, Object> form = new HashMap<>(6); |
|||
form.put("_csrftoken", csrfToken); |
|||
form.put("_uuid", deviceUuid); |
|||
form.put("_uid", userId); |
|||
form.put("collection_visibility", "0"); // 1 for public, planned for future but currently inexistant |
|||
form.put("module_name", "collection_create"); |
|||
form.put("name", name); |
|||
final Map<String, String> signedForm = Utils.sign(form); |
|||
final Call<String> request = repository.createCollection(signedForm); |
|||
request.enqueue(new Callback<String>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) { |
|||
if (callback == null) return; |
|||
final String collectionsListResponse = response.body(); |
|||
if (collectionsListResponse == null) { |
|||
callback.onSuccess(null); |
|||
return; |
|||
} |
|||
callback.onSuccess(collectionsListResponse); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) { |
|||
if (callback != null) { |
|||
callback.onFailure(t); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public void fetchLiked(final String maxId, |
|||
final ServiceCallback<PostsFetchResponse> callback) { |
|||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); |
|||
if (!TextUtils.isEmpty(maxId)) { |
|||
builder.put("max_id", maxId); |
|||
} |
|||
final Call<UserFeedResponse> request = repository.fetchLiked(builder.build()); |
|||
request.enqueue(new Callback<UserFeedResponse>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<UserFeedResponse> call, @NonNull final Response<UserFeedResponse> response) { |
|||
if (callback == null) return; |
|||
final UserFeedResponse userFeedResponse = response.body(); |
|||
if (userFeedResponse == null) { |
|||
callback.onSuccess(null); |
|||
return; |
|||
} |
|||
callback.onSuccess(new PostsFetchResponse( |
|||
userFeedResponse.getItems(), |
|||
userFeedResponse.getMoreAvailable(), |
|||
userFeedResponse.getNextMaxId() |
|||
)); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<UserFeedResponse> call, @NonNull final Throwable t) { |
|||
if (callback != null) { |
|||
callback.onFailure(t); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public void fetchTagged(final long profileId, |
|||
final String maxId, |
|||
final ServiceCallback<PostsFetchResponse> callback) { |
|||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); |
|||
if (!TextUtils.isEmpty(maxId)) { |
|||
builder.put("max_id", maxId); |
|||
} |
|||
final Call<UserFeedResponse> request = repository.fetchTagged(profileId, builder.build()); |
|||
request.enqueue(new Callback<UserFeedResponse>() { |
|||
@Override |
|||
public void onResponse(@NonNull final Call<UserFeedResponse> call, @NonNull final Response<UserFeedResponse> response) { |
|||
if (callback == null) return; |
|||
final UserFeedResponse userFeedResponse = response.body(); |
|||
if (userFeedResponse == null) { |
|||
callback.onSuccess(null); |
|||
return; |
|||
} |
|||
callback.onSuccess(new PostsFetchResponse( |
|||
userFeedResponse.getItems(), |
|||
userFeedResponse.getMoreAvailable(), |
|||
userFeedResponse.getNextMaxId() |
|||
)); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(@NonNull final Call<UserFeedResponse> call, @NonNull final Throwable t) { |
|||
if (callback != null) { |
|||
callback.onFailure(t); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
// private PostsFetchResponse parseProfilePostsResponse(final String body) throws JSONException { |
|||
// final JSONObject root = new JSONObject(body); |
|||
// final boolean moreAvailable = root.optBoolean("more_available"); |
|||
// final String nextMaxId = root.optString("next_max_id"); |
|||
// final JSONArray itemsJson = root.optJSONArray("items"); |
|||
// final List<FeedModel> items = parseItems(itemsJson, false); |
|||
// return new PostsFetchResponse( |
|||
// items, |
|||
// moreAvailable, |
|||
// nextMaxId |
|||
// ); |
|||
// } |
|||
|
|||
// 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<FeedModel> items = parseItems(itemsJson, isInMedia); |
|||
// return new PostsFetchResponse( |
|||
// items, |
|||
// moreAvailable, |
|||
// nextMaxId |
|||
// ); |
|||
// } |
|||
|
|||
// private List<FeedModel> parseItems(final JSONArray items, final boolean isInMedia) 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.parseItem(isInMedia ? itemJson.optJSONObject("media") : itemJson); |
|||
// if (feedModel != null) { |
|||
// feedModels.add(feedModel); |
|||
// } |
|||
// } |
|||
// return feedModels; |
|||
// } |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue