@@ -104,16 +104,12 @@ public Matcher isSingleton() {
104104 @ Override
105105 public JSONObject getFirstPage () throws IOException {
106106 try {
107- if (authToken == null || authToken .isBlank ()) {
108- fetchAuthToken ();
109- }
110-
111107 if (isSingleton ().matches ()) {
112108 maxPages = 1 ;
113109 String gifDetailsURL = String .format (GIFS_DETAIL_ENDPOINT , getGID (url ));
114- return Http . url (gifDetailsURL ). header ( "Authorization" , "Bearer " + authToken ). getJSON ( );
110+ return getAuthenticatedJSON (gifDetailsURL );
115111 } else if (isSearch ().matches () || isTags ().matches ()) {
116- var json = Http . url (getSearchOrTagsURL ()). header ( "Authorization" , "Bearer " + authToken ). getJSON ( );
112+ var json = getAuthenticatedJSON (getSearchOrTagsURL ());
117113 maxPages = json .getInt ("pages" );
118114 return json ;
119115 } else {
@@ -122,7 +118,7 @@ public JSONObject getFirstPage() throws IOException {
122118 uri .addParameter ("order" , "new" );
123119 uri .addParameter ("count" , Integer .toString (count ));
124120 uri .addParameter ("page" , Integer .toString (currentPage ));
125- var json = Http . url (uri .build ().toURL ()). header ( "Authorization" , "Bearer " + authToken ). getJSON ( );
121+ var json = getAuthenticatedJSON (uri .build ().toURL ());
126122 maxPages = json .getInt ("pages" );
127123 return json ;
128124 }
@@ -194,7 +190,7 @@ public JSONObject getNextPage(JSONObject doc) throws IOException, URISyntaxExcep
194190 }
195191 currentPage ++;
196192 if (isSearch ().matches () || isTags ().matches ()) {
197- var json = Http . url (getSearchOrTagsURL ()). header ( "Authorization" , "Bearer " + authToken ). getJSON ( );
193+ var json = getAuthenticatedJSON (getSearchOrTagsURL ());
198194 // Handle rare maxPages change during a rip
199195 maxPages = json .getInt ("pages" );
200196 return json ;
@@ -203,7 +199,7 @@ public JSONObject getNextPage(JSONObject doc) throws IOException, URISyntaxExcep
203199 uri .addParameter ("order" , "new" );
204200 uri .addParameter ("count" , Integer .toString (count ));
205201 uri .addParameter ("page" , Integer .toString (currentPage ));
206- var json = Http . url (uri .build ().toURL ()). header ( "Authorization" , "Bearer " + authToken ). getJSON ( );
202+ var json = getAuthenticatedJSON (uri .build ().toURL ());
207203 // Handle rare maxPages change during a rip
208204 maxPages = json .getInt ("pages" );
209205 return json ;
@@ -254,8 +250,7 @@ private static List<String> getURLsForGallery(String galleryID, String gifID) {
254250 return list ;
255251 }
256252 try {
257- var json = Http .url (String .format (GALLERY_ENDPOINT , galleryID ))
258- .header ("Authorization" , "Bearer " + authToken ).getJSON ();
253+ var json = getAuthenticatedJSON (String .format (GALLERY_ENDPOINT , galleryID ));
259254 for (var gif : json .getJSONArray ("gifs" )) {
260255 var hdURL = ((JSONObject ) gif ).getJSONObject ("urls" ).getString ("hd" );
261256 list .add (hdURL );
@@ -280,12 +275,9 @@ public static String getVideoURL(URL url) throws IOException, URISyntaxException
280275 if (!m .matches ()) {
281276 throw new IOException (String .format ("Cannot fetch redgif url %s" , url .toExternalForm ()));
282277 }
283- if (authToken == null || authToken .isBlank ()) {
284- fetchAuthToken ();
285- }
286278 var gid = m .group (1 ).split ("-" )[0 ];
287279 var gifDetailsURL = String .format (GIFS_DETAIL_ENDPOINT , gid );
288- var json = Http . url (gifDetailsURL ). header ( "Authorization" , "Bearer " + authToken ). getJSON ( );
280+ var json = getAuthenticatedJSON (gifDetailsURL );
289281 var gif = json .getJSONObject ("gif" );
290282 if (!gif .isNull ("gallery" )) {
291283 // TODO check how to handle a image gallery
@@ -303,7 +295,31 @@ private static void fetchAuthToken() throws IOException {
303295 var json = Http .url (TEMPORARY_AUTH_ENDPOINT ).getJSON ();
304296 var token = json .getString ("token" );
305297 authToken = token ;
306- logger .info ("Incase of redgif 401 errors, please restart the app to refresh the auth token" );
298+ }
299+
300+ /**
301+ * Performs an authenticated GET against the RedGIFs API. The auth token is a
302+ * temporary, expiring credential (see fetchAuthToken()); on a 401 (expired
303+ * token) this fetches a fresh one and retries once before giving up.
304+ */
305+ private static JSONObject getAuthenticatedJSON (String requestUrl ) throws IOException {
306+ if (authToken == null || authToken .isBlank ()) {
307+ fetchAuthToken ();
308+ }
309+ try {
310+ return Http .url (requestUrl ).header ("Authorization" , "Bearer " + authToken ).getJSON ();
311+ } catch (org .jsoup .HttpStatusException e ) {
312+ if (e .getStatusCode () != 401 ) {
313+ throw e ;
314+ }
315+ logger .warn ("RedGIFs auth token expired, fetching a new one and retrying: " + requestUrl );
316+ fetchAuthToken ();
317+ return Http .url (requestUrl ).header ("Authorization" , "Bearer " + authToken ).getJSON ();
318+ }
319+ }
320+
321+ private static JSONObject getAuthenticatedJSON (URL requestUrl ) throws IOException {
322+ return getAuthenticatedJSON (requestUrl .toExternalForm ());
307323 }
308324
309325 /**
0 commit comments