Use the solution provided by @edgan to fix the redgifs issue.

This commit is contained in:
Docile-Alligator 2025-09-10 22:00:34 -04:00
parent ab20643fdb
commit bbc7c368e2
5 changed files with 273 additions and 37 deletions

View File

@ -215,7 +215,7 @@ abstract class NetworkModule {
return new RedgifsAccessTokenAuthenticator(currentAccountSharedPreferences);
}
/*@Provides
@Provides
@Named("redgifs")
@Singleton
static Retrofit provideRedgifsRetrofit(@Named("RedgifsAccessTokenAuthenticator") Interceptor accessTokenAuthenticator,
@ -236,16 +236,16 @@ abstract class NetworkModule {
.baseUrl(APIUtils.REDGIFS_API_BASE_URI)
.client(okHttpClientBuilder.build())
.build();
}*/
}
@Provides
/*@Provides
@Named("redgifs")
@Singleton
static Retrofit provideRedgifsRetrofit(@Named("base") Retrofit retrofit) {
return retrofit.newBuilder()
.baseUrl(APIUtils.OH_MY_DL_BASE_URI)
.build();
}
}*/
@Provides
@Named("imgur")

View File

@ -18,4 +18,7 @@ public interface RedgifsAPI {
@FormUrlEncoded
@POST("/v2/oauth/client")
Call<String> getRedgifsAccessToken(@FieldMap Map<String, String> params);
@GET("/v2/auth/temporary")
Call<String> getRedgifsTemporaryToken();
}

View File

@ -1,27 +1,6 @@
package ml.docilealligator.infinityforreddit.network;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import ml.docilealligator.infinityforreddit.apis.RedgifsAPI;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.Response;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public class RedgifsAccessTokenAuthenticator implements Interceptor {
/*public class RedgifsAccessTokenAuthenticator implements Interceptor {
private final SharedPreferences mCurrentAccountSharedPreferences;
public RedgifsAccessTokenAuthenticator(SharedPreferences currentAccountSharedPreferences) {
@ -57,6 +36,98 @@ public class RedgifsAccessTokenAuthenticator implements Interceptor {
return "";
}
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if (response.code() == 401 || response.code() == 400) {
String accessTokenHeader = response.request().header(APIUtils.AUTHORIZATION_KEY);
if (accessTokenHeader == null) {
return response;
}
String accessToken = accessTokenHeader.substring(APIUtils.AUTHORIZATION_BASE.length() - 1).trim();
synchronized (this) {
String accessTokenFromSharedPreferences = mCurrentAccountSharedPreferences.getString(SharedPreferencesUtils.REDGIFS_ACCESS_TOKEN, "");
if (accessToken.equals(accessTokenFromSharedPreferences)) {
String newAccessToken = refreshAccessToken();
if (!newAccessToken.equals("")) {
response.close();
return chain.proceed(response.request().newBuilder().headers(Headers.of(APIUtils.getRedgifsOAuthHeader(newAccessToken))).build());
} else {
return response;
}
} else {
response.close();
return chain.proceed(response.request().newBuilder().headers(Headers.of(APIUtils.getRedgifsOAuthHeader(accessTokenFromSharedPreferences))).build());
}
}
}
return response;
}
}*/
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import ml.docilealligator.infinityforreddit.apis.RedgifsAPI;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.Response;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public class RedgifsAccessTokenAuthenticator implements Interceptor {
private final SharedPreferences mCurrentAccountSharedPreferences;
public RedgifsAccessTokenAuthenticator(SharedPreferences currentAccountSharedPreferences) {
this.mCurrentAccountSharedPreferences = currentAccountSharedPreferences;
}
private String refreshAccessToken() {
// Check if existing token is valid
APIUtils.RedgifsAuthToken currentToken = APIUtils.REDGIFS_TOKEN.get();
if (currentToken.isValid()) {
return currentToken.token;
}
// Get new token from API
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(APIUtils.REDGIFS_API_BASE_URI)
.addConverterFactory(ScalarsConverterFactory.create())
.build();
RedgifsAPI api = retrofit.create(RedgifsAPI.class);
Call<String> accessTokenCall = api.getRedgifsTemporaryToken();
try {
retrofit2.Response<String> response = accessTokenCall.execute();
if (response.isSuccessful() && response.body() != null) {
String newAccessToken = new JSONObject(response.body()).getString("token");
// Update both the atomic reference and shared preferences
APIUtils.RedgifsAuthToken newToken = APIUtils.RedgifsAuthToken.expireIn1day(newAccessToken);
APIUtils.REDGIFS_TOKEN.set(newToken);
mCurrentAccountSharedPreferences.edit().putString(SharedPreferencesUtils.REDGIFS_ACCESS_TOKEN, newAccessToken).apply();
return newAccessToken;
}
return "";
} catch (IOException | JSONException e) {
e.printStackTrace();
}
return "";
}
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {

View File

@ -10,12 +10,9 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import ml.docilealligator.infinityforreddit.FetchVideoLinkListener;
import ml.docilealligator.infinityforreddit.apis.OhMyDlAPI;
import ml.docilealligator.infinityforreddit.apis.RedgifsAPI;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.JSONUtils;
@ -31,6 +28,41 @@ public class FetchRedgifsVideoLinks {
FetchVideoLinkListener fetchVideoLinkListener) {
executor.execute(() -> {
try {
// Get valid token
String accessToken = getValidAccessToken(redgifsRetrofit, currentAccountSharedPreferences);
if (accessToken.isEmpty()) {
handler.post(() -> fetchVideoLinkListener.failed(null));
return;
}
Response<String> response = redgifsRetrofit
.create(RedgifsAPI.class)
.getRedgifsData(APIUtils.getRedgifsOAuthHeader(accessToken),
redgifsId, APIUtils.USER_AGENT)
.execute();
if (response.isSuccessful()) {
parseRedgifsVideoLinks(handler, response.body(), fetchVideoLinkListener);
} else if (response.code() == 401) {
// Token expired, try once more with new token
accessToken = refreshAccessToken(redgifsRetrofit, currentAccountSharedPreferences);
if (!accessToken.isEmpty()) {
response = redgifsRetrofit
.create(RedgifsAPI.class)
.getRedgifsData(
APIUtils.getRedgifsOAuthHeader(accessToken),
redgifsId, APIUtils.USER_AGENT)
.execute();
if (response.isSuccessful()) {
parseRedgifsVideoLinks(handler, response.body(), fetchVideoLinkListener);
} else {
handler.post(() -> fetchVideoLinkListener.failed(null));
}
} else {
handler.post(() -> fetchVideoLinkListener.failed(null));
}
} else {
handler.post(() -> fetchVideoLinkListener.failed(null));
}
/*Response<String> response = redgifsRetrofit
.create(RedgifsAPI.class)
.getRedgifsData(
@ -43,7 +75,7 @@ public class FetchRedgifsVideoLinks {
handler.post(() -> fetchVideoLinkListener.failed(null));
}*/
Map<String, String> params = new HashMap<>();
/*Map<String, String> params = new HashMap<>();
params.put(APIUtils.PLATFORM_KEY, "redgifs");
params.put(APIUtils.URL_KEY, "https://www.redgifs.com/watch/" + redgifsId);
@ -55,7 +87,7 @@ public class FetchRedgifsVideoLinks {
parseRedgifsVideoLinks(handler, response.body(), fetchVideoLinkListener);
} else {
handler.post(() -> fetchVideoLinkListener.failed(null));
}
}*/
} catch (IOException e) {
e.printStackTrace();
handler.post(() -> fetchVideoLinkListener.failed(null));
@ -69,7 +101,39 @@ public class FetchRedgifsVideoLinks {
SharedPreferences currentAccountSharedPreferences,
String redgifsId) {
try {
// Get valid token
String accessToken = getValidAccessToken(redgifsRetrofit, currentAccountSharedPreferences);
if (accessToken.isEmpty()) {
return null;
}
Response<String> response = redgifsRetrofit
.create(RedgifsAPI.class)
.getRedgifsData(APIUtils.getRedgifsOAuthHeader(accessToken),
redgifsId, APIUtils.USER_AGENT)
.execute();
if (response.isSuccessful()) {
return parseRedgifsVideoLinks(response.body());
} else if (response.code() == 401) {
// Token expired, try once more with new token
accessToken = refreshAccessToken(redgifsRetrofit, currentAccountSharedPreferences);
if (!accessToken.isEmpty()) {
response = redgifsRetrofit
.create(RedgifsAPI.class)
.getRedgifsData(
APIUtils.getRedgifsOAuthHeader(accessToken),
redgifsId, APIUtils.USER_AGENT)
.execute();
if (response.isSuccessful()) {
return parseRedgifsVideoLinks(response.body());
}
}
return null;
} else {
return null;
}
/*Response<String> response = redgifsRetrofit
.create(RedgifsAPI.class)
.getRedgifsData(
APIUtils.getRedgifsOAuthHeader(currentAccountSharedPreferences.getString(SharedPreferencesUtils.REDGIFS_ACCESS_TOKEN, "")),
@ -79,7 +143,7 @@ public class FetchRedgifsVideoLinks {
return parseRedgifsVideoLinks(response.body());
} else {
return null;
}
}*/
} catch (IOException e) {
e.printStackTrace();
return null;
@ -106,31 +170,101 @@ public class FetchRedgifsVideoLinks {
private static void parseRedgifsVideoLinks(Handler handler, String response,
FetchVideoLinkListener fetchVideoLinkListener) {
try {
/*String mp4 = new JSONObject(response).getJSONObject(JSONUtils.GIF_KEY).getJSONObject(JSONUtils.URLS_KEY)
/*try {
*//*String mp4 = new JSONObject(response).getJSONObject(JSONUtils.GIF_KEY).getJSONObject(JSONUtils.URLS_KEY)
.getString(JSONUtils.HD_KEY);
if (mp4.contains("-silent")) {
mp4 = mp4.substring(0, mp4.indexOf("-silent")) + ".mp4";
}
final String mp4Name = mp4;
handler.post(() -> fetchVideoLinkListener.onFetchRedgifsVideoLinkSuccess(mp4Name, mp4Name));*/
handler.post(() -> fetchVideoLinkListener.onFetchRedgifsVideoLinkSuccess(mp4Name, mp4Name));*//*
String mp4 = new JSONObject(response).getString(JSONUtils.VIDEO_DOWNLOAD_URL);
handler.post(() -> fetchVideoLinkListener.onFetchRedgifsVideoLinkSuccess(mp4, mp4));
} catch (JSONException e) {
e.printStackTrace();
handler.post(() -> fetchVideoLinkListener.failed(null));
}*/
try {
JSONObject jsonResponse = new JSONObject(response);
JSONObject gif = jsonResponse.getJSONObject(JSONUtils.GIF_KEY);
JSONObject urls = gif.getJSONObject(JSONUtils.URLS_KEY);
// Try HD first, fall back to SD if not available
String mp4;
if (urls.has(JSONUtils.HD_KEY)) {
mp4 = urls.getString(JSONUtils.HD_KEY);
} else if (urls.has("sd")) {
mp4 = urls.getString("sd");
} else {
handler.post(() -> fetchVideoLinkListener.failed(null));
return;
}
if (mp4.contains("-silent")) {
mp4 = mp4.substring(0, mp4.indexOf("-silent")) + ".mp4";
}
final String mp4Name = mp4;
handler.post(() -> fetchVideoLinkListener.onFetchRedgifsVideoLinkSuccess(mp4Name, mp4Name));
} catch (JSONException e) {
e.printStackTrace();
handler.post(() -> fetchVideoLinkListener.failed(null));
}
}
@Nullable
private static String parseRedgifsVideoLinks(String response) {
try {
return new JSONObject(response).getJSONObject(JSONUtils.GIF_KEY).getJSONObject(JSONUtils.URLS_KEY)
.getString(JSONUtils.HD_KEY);
/*return new JSONObject(response).getJSONObject(JSONUtils.GIF_KEY).getJSONObject(JSONUtils.URLS_KEY)
.getString(JSONUtils.HD_KEY);*/
JSONObject jsonResponse = new JSONObject(response);
JSONObject gif = jsonResponse.getJSONObject(JSONUtils.GIF_KEY);
JSONObject urls = gif.getJSONObject(JSONUtils.URLS_KEY);
// Try HD first, fall back to SD if not available
if (urls.has(JSONUtils.HD_KEY)) {
return urls.getString(JSONUtils.HD_KEY);
} else if (urls.has("sd")) {
return urls.getString("sd");
} else {
return null;
}
} catch (JSONException e) {
e.printStackTrace();
return null;
}
}
private static String getValidAccessToken(Retrofit redgifsRetrofit, SharedPreferences currentAccountSharedPreferences) {
// Check if existing token is valid
APIUtils.RedgifsAuthToken currentToken = APIUtils.REDGIFS_TOKEN.get();
if (currentToken.isValid()) {
return currentToken.token;
}
// Get new token if current one is invalid
return refreshAccessToken(redgifsRetrofit, currentAccountSharedPreferences);
}
private static String refreshAccessToken(Retrofit redgifsRetrofit, SharedPreferences currentAccountSharedPreferences) {
try {
RedgifsAPI api = redgifsRetrofit.create(RedgifsAPI.class);
retrofit2.Response<String> response = api.getRedgifsTemporaryToken().execute();
if (response.isSuccessful() && response.body() != null) {
String newAccessToken = new JSONObject(response.body()).getString("token");
// Update both the atomic reference and shared preferences
APIUtils.RedgifsAuthToken newToken = APIUtils.RedgifsAuthToken.expireIn1day(newAccessToken);
APIUtils.REDGIFS_TOKEN.set(newToken);
currentAccountSharedPreferences.edit().putString(SharedPreferencesUtils.REDGIFS_ACCESS_TOKEN, newAccessToken).apply();
return newAccessToken;
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}

View File

@ -1,9 +1,13 @@
package ml.docilealligator.infinityforreddit.utils;
import android.os.SystemClock;
import android.util.Base64;
import androidx.annotation.NonNull;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import ml.docilealligator.infinityforreddit.BuildConfig;
import ml.docilealligator.infinityforreddit.account.Account;
@ -166,4 +170,28 @@ public class APIUtils {
params.put(APIUtils.USER_AGENT_KEY, APIUtils.USER_AGENT);
return params;
}
// RedGifs token management
public static final AtomicReference<RedgifsAuthToken> REDGIFS_TOKEN = new AtomicReference<>(new RedgifsAuthToken("", 0));
public static class RedgifsAuthToken {
@NonNull
public final String token;
private final long expireAt;
private RedgifsAuthToken(@NonNull String token, final long expireAt) {
this.token = token;
this.expireAt = expireAt;
}
public static RedgifsAuthToken expireIn1day(@NonNull String token) {
// 23 not 24 to give an hour leeway
long expireTime = 1000 * 60 * 60 * 23;
return new RedgifsAuthToken(token, SystemClock.uptimeMillis() + expireTime);
}
public boolean isValid() {
return !token.isEmpty() && expireAt > SystemClock.uptimeMillis();
}
}
}