Skip to content

Commit b69779c

Browse files
committed
SD image requests - fixes for error handling
1 parent 8090fdc commit b69779c

File tree

5 files changed

+99
-54
lines changed

5 files changed

+99
-54
lines changed

java/sage/MetaImage.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@
2222
import java.awt.Shape;
2323
import java.awt.geom.RoundRectangle2D;
2424
import java.awt.image.BufferedImage;
25-
import java.io.ByteArrayInputStream;
2625
import java.io.ByteArrayOutputStream;
2726
import java.io.File;
28-
import java.io.FileInputStream;
2927
import java.io.FileOutputStream;
3028
import java.io.IOException;
3129
import java.io.InputStream;
@@ -52,8 +50,8 @@
5250
import java.util.Vector;
5351
import java.util.WeakHashMap;
5452
import sage.epg.sd.SDRipper;
55-
import sage.epg.sd.SDSageSession;
5653
import sage.epg.sd.SDSession;
54+
import sage.epg.sd.SDUtils;
5755

5856
/*
5957
* NOTE: DON'T DO THUMBNAIL GENERATION IN HERE. WE WANT TO STORE THEM IN SEPARATE FILES
@@ -3218,6 +3216,7 @@ private boolean loadCacheFile()
32183216
HttpURLConnection.setFollowRedirects(true);
32193217
URL myURL = (URL) src;
32203218
URLConnection myURLConn;
3219+
boolean isSDURL = false;
32213220
try
32223221
{
32233222
while (true)
@@ -3227,6 +3226,7 @@ private boolean loadCacheFile()
32273226
myURLConn.setConnectTimeout(30000);
32283227
myURLConn.setReadTimeout(30000);
32293228
if(src.toString().startsWith(SDSession.URL_VERSIONED)){
3229+
isSDURL = true;
32303230
//this is an SD supplied image so a token is required to retreive it
32313231
if (Sage.DBG) System.out.println("MetaImage.loadCacheFile: Found SD image url. src = '" + src + "'");
32323232

@@ -3245,8 +3245,10 @@ private boolean loadCacheFile()
32453245
if (Sage.DBG) System.out.println("MetaImage.loadCacheFile: No token so skipping src = '" + src + "'");
32463246
break;
32473247
}else{
3248+
//TODO: remove the following after testing
32483249
if (Sage.DBG) System.out.println("MetaImage.loadCacheFile: Adding token to Request. token = '" + sdToken + "'");
32493250
myURLConn.addRequestProperty("token", sdToken);
3251+
myURLConn.setRequestProperty("User-Agent", SDSession.USER_AGENT);
32503252
//secret SD debug mode that will send requests to their debug server. Only enable when working with SD Support
32513253
if(Sage.getBoolean("debug_sd_support", false)) {
32523254
myURLConn.addRequestProperty("RouteTo", "debug");
@@ -3258,7 +3260,34 @@ private boolean loadCacheFile()
32583260
is = myURLConn.getInputStream();
32593261
if (myURLConn instanceof HttpURLConnection)
32603262
{
3263+
//TODO: SD testing of response code
32613264
HttpURLConnection httpConn = (HttpURLConnection) myURLConn;
3265+
if (isSDURL) {
3266+
if (Sage.DBG) System.out.println("MetaImage.loadCacheFile: HttpURLConnection response found for SD image - type:" + httpConn.getContentType() + " response code:" + httpConn.getResponseCode());
3267+
if ("application/json".equals(httpConn.getContentType())){
3268+
//SD returned an error which is within the returned json
3269+
//TODO: remove after testing
3270+
int imageErrorCode = SDUtils.handleSDJsonErrorFromHttpResponse(httpConn);
3271+
if(imageErrorCode==1004){ //no or invalid token
3272+
is.close();
3273+
is = null;
3274+
break;
3275+
}else if(imageErrorCode==5000){ //image does not exist - do not ask again
3276+
//create a blank image to store in cache so next request returns the blank rather than asking for an image that does not exist in SD
3277+
if (Sage.DBG) System.out.println("MetaImage.loadCacheFile: error 5000 - SD image does not exist - creating a blank image for the cache to avoid re-requesting");
3278+
is = SDUtils.createBlankImageInputStream(270,360,"jpg");
3279+
break;
3280+
}else if(imageErrorCode==5002 || imageErrorCode==5003){ //5002-max downloads, 5003-max downloads trial
3281+
is.close();
3282+
is = null;
3283+
break;
3284+
}else{
3285+
is.close();
3286+
is = null;
3287+
break;
3288+
}
3289+
}
3290+
}
32623291
if (httpConn.getResponseCode() / 100 == 3)
32633292
{
32643293
if (Sage.DBG) System.out.println("Internally processing HTTP redirect...");

java/sage/SageConstants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ private SageConstants()
2222
// Non-instantiable
2323
}
2424

25-
public static final int BUILD_VERSION = 1037;
25+
public static final int BUILD_VERSION = 1046;
2626
}

java/sage/epg/sd/SDSageSession.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.io.InputStreamReader;
2323
import java.io.OutputStream;
2424
import java.net.URL;
25-
import java.util.Random;
2625

2726
public class SDSageSession extends SDSession
2827
{

java/sage/epg/sd/SDSession.java

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@
5858
import java.time.LocalDateTime;
5959
import java.time.format.DateTimeFormatter;
6060
import java.util.Collection;
61-
import java.util.logging.Level;
62-
import java.util.logging.Logger;
6361

6462
public abstract class SDSession
6563
{
@@ -85,7 +83,7 @@ public abstract class SDSession
8583
// The character set to be used for outgoing communications.
8684
protected static final Charset OUT_CHARSET = StandardCharsets.UTF_8;
8785
// The expected character set to be used for incoming communications.
88-
protected static final Charset IN_CHARSET = StandardCharsets.ISO_8859_1;
86+
public static final Charset IN_CHARSET = StandardCharsets.ISO_8859_1;
8987

9088
// These are set in the static constructor because they can throw format exceptions.
9189
// Returns a token if the credentials are valid.
@@ -384,9 +382,6 @@ private static String debugDateTime(){
384382
*/
385383
public synchronized void authenticate() throws IOException, SDException
386384
{
387-
388-
//if(Sage.DBG) System.out.println("SDSession/authenticate: checking existing token:" + token + " with expiry:" + tokenExpiration + " against System:" + (System.currentTimeMillis()/1000));
389-
390385
// The token is still valid.
391386
if (System.currentTimeMillis()/1000 < tokenExpiration && token != null)
392387
{
@@ -952,7 +947,9 @@ public SDProgram[] getPrograms(Collection<String> programs) throws IOException,
952947
JsonArray submit = new JsonArray();
953948
for (String program : programs)
954949
{
955-
submit.add(SDUtils.fromSageTVtoProgram(program));
950+
if(SDUtils.isValidProgramID(program)){
951+
submit.add(SDUtils.fromSageTVtoProgram(program));
952+
}
956953
}
957954

958955
SDProgram[] returnValues = postAuthJson(GET_PROGRAMS, SDProgram[].class, submit);
@@ -1104,16 +1101,12 @@ public SDProgramImages[] getProgramImages(String[] programs) throws IOException,
11041101
JsonArray submit = new JsonArray();
11051102
for (String program : programs)
11061103
{
1107-
//08-12-2025 jusjoken - convert program to 14 chars needed by SD
1108-
program = SDUtils.fromSageTVtoProgram(program);
1109-
//08-12-2025 jusjoken - the below should no longer be required - remove after testing
11101104
//03-01-2025 jusjoken: added validation for program ids
11111105
//first check if its already formated correctly
1112-
if(SDUtils.isValidShortProgramID(program)){
1106+
if(SDUtils.isValidProgramID(program)){
1107+
//08-12-2025 jusjoken - convert program to 14 chars needed by SD
1108+
program = SDUtils.fromSageTVtoProgram(program);
11131109
submit.add(program);
1114-
}else if(SDUtils.isValidProgramID(program)){ //valid BUT is not shortend to 10
1115-
submit.add(program); //submit as is as SD now handles the 14 character program ids as well
1116-
//submit.add(program.substring(0, 10));
11171110
}else{
11181111
if (Sage.DBG) System.out.println("getProgramImages: INVALID program ID - SKIPPING:" + program);
11191112
}
@@ -1264,7 +1257,7 @@ public SDInProgressSport getInProgressSport(String programId) throws IOException
12641257
if (programId == null || programId.length() == 0)
12651258
return null;
12661259

1267-
if (programId.length() == 12)
1260+
if (SDUtils.isValidProgramID(programId))
12681261
programId = SDUtils.fromSageTVtoProgram(programId);
12691262

12701263
// A token is now required to perform this lookup.

java/sage/epg/sd/SDUtils.java

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,16 @@
5353
import java.io.InputStream;
5454
import java.io.InputStreamReader;
5555
import java.io.OutputStreamWriter;
56+
import java.net.HttpURLConnection;
5657
import java.text.ParseException;
5758
import java.text.SimpleDateFormat;
5859
import java.util.ArrayList;
5960
import java.util.Collection;
6061
import java.util.List;
6162
import java.util.TimeZone;
6263
import java.util.zip.GZIPInputStream;
64+
import java.awt.image.BufferedImage;
65+
import javax.imageio.ImageIO;
6366

6467
public class SDUtils
6568
{
@@ -81,6 +84,43 @@ public class SDUtils
8184
gsonBuilder.registerTypeAdapter(SDProgramSchedule.class, new SDProgramScheduleDeserializer());
8285
GSON = gsonBuilder.create();
8386
}
87+
88+
public static int handleSDJsonErrorFromHttpResponse(HttpURLConnection httpConn) throws IOException
89+
{
90+
InputStream inputStream = new BufferedInputStream(httpConn.getInputStream());
91+
InputStreamReader reader = new InputStreamReader(inputStream, SDSession.IN_CHARSET);
92+
JsonElement errorElement = GSON.fromJson(reader, JsonElement.class);
93+
if (Sage.DBG) System.out.println("SDUtils.handleSDJsonErrorFromHttpResponse: Start of handler");
94+
95+
if (errorElement instanceof JsonObject)
96+
{
97+
JsonElement codeElement = ((JsonObject) errorElement).get("code");
98+
int code = codeElement != null ? codeElement.getAsInt() : -1;
99+
if (Sage.DBG) System.out.println("SDUtils.handleSDJsonErrorFromHttpResponse: Error:" + code + " : " + SDErrors.getErrorForCode(code));
100+
return code;
101+
}else{
102+
if (Sage.DBG) System.out.println("SDUtils.handleSDJsonErrorFromHttpResponse: Unknown Error");
103+
return -9999;
104+
}
105+
}
106+
107+
public static ByteArrayInputStream createBlankImageInputStream(int width, int height, String format) {
108+
// Create a blank BufferedImage (white background)
109+
BufferedImage blankImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
110+
// Fill the image with white color
111+
blankImage.createGraphics().fillRect(0, 0, width, height);
112+
113+
// Convert BufferedImage to byte array using ImageIO
114+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
115+
try {
116+
ImageIO.write(blankImage, format, baos);
117+
} catch (IOException e) {
118+
throw new RuntimeException("Failed to write blank image to stream", e);
119+
}
120+
121+
// Convert byte array to ByteArrayInputStream
122+
return new ByteArrayInputStream(baos.toByteArray());
123+
}
84124

85125
/**
86126
* Determine what kind of stream is returned and wrap it with an appropriate processing layer.
@@ -443,29 +483,31 @@ public static String removeLeadingZeros(String channelNumber)
443483
return new String(channel, writeFrom, channel.length - writeFrom);
444484
}
445485

486+
//enfore sending 14 character programids
446487
public static String fromSageTVtoProgram(String program)
447488
{
448-
if (program.length() == 12)
489+
if (program.length() == 14) return program;
490+
491+
if (program.startsWith("EP") && program.length()==12) //add zeros after EP
449492
{
450-
char returnValue[] = new char[14];
451-
program.getChars(0, 2, returnValue, 0);
452-
returnValue[2] = '0';
453-
returnValue[3] = '0';
454-
program.getChars(2, 12, returnValue, 4);
455-
return new String(returnValue);
493+
String programNumber = program.substring(2);
494+
program = "EP00" + programNumber;
495+
if (Sage.DBG) System.out.println("SDUtils.fromSageTVtoProgram: program = '" + program + "'");
496+
return program;
456497
}
457-
if (program.length() == 10 && program.startsWith("EP"))
498+
else if (program.startsWith("EP")) //add zeros to end
458499
{
459-
program = program + "0000";
500+
program = String.format("%-14s", program).replace(' ', '0');
460501
program.replace("EP", "SH");
502+
if (Sage.DBG) System.out.println("SDUtils.fromSageTVtoProgram: EP program converted to = '" + program + "'");
461503
return program;
462-
}else if(program.length() == 10){
463-
program = program + "0000";
504+
}else{ //add zerors to the start AFTER the 2 char type
505+
String programType = program.substring(0, 2);
506+
String programNumber = program.substring(2);
507+
program = programType + String.format("%-12s", programNumber).replace(' ', '0');
508+
if (Sage.DBG) System.out.println("SDUtils.fromSageTVtoProgram: program = '" + program + "'");
464509
return program;
465510
}
466-
if (Sage.DBG && program.length()!=14) System.out.println("SDUtils.fromSageTVtoProgram: After conversion program is NOT 14 characters that SD requires. program = '" + program + "'");
467-
468-
return program;
469511
}
470512

471513
public static String fromProgramToSageTV(String program)
@@ -488,27 +530,9 @@ public static String fromProgramToSageTV(String program)
488530
*/
489531
public static boolean isValidProgramID(String programId)
490532
{
491-
if (programId == null || programId.length() == 0 ||
492-
(programId.length() != 12 && programId.length() != 14) ||
493-
(!programId.startsWith("EP") && !programId.startsWith("SH") &&
494-
!programId.startsWith("MV") && !programId.startsWith("SP") && !programId.startsWith("EV")))
495-
return false;
496-
return true;
497-
}
498-
499-
/**
500-
* Check if a given program ID is valid to send to SD for metadata and is already shortened to 10
501-
*
502-
* @param programId The program ID to check.
503-
* @return <code>true</code> if a given program ID is valid to send to SD.
504-
*/
505-
public static boolean isValidShortProgramID(String programId)
506-
{
507-
if (programId == null || programId.length() == 0 || programId.length() != 10 ||
508-
(!programId.startsWith("EP") && !programId.startsWith("SH") &&
509-
!programId.startsWith("MV") && !programId.startsWith("SP") && !programId.startsWith("EV")))
533+
if(programId == null || programId.length() == 0) return false;
534+
if (programId.startsWith("EP") || programId.startsWith("SH") || programId.startsWith("MV") || programId.startsWith("SP") || programId.startsWith("EV")) return true;
510535
return false;
511-
return true;
512536
}
513537

514538
/**

0 commit comments

Comments
 (0)