// MUST NOT return null
try {
InputStream in = load(originalUrl, false, stable);
- if (Instance.isDebug()) {
- System.err.println("Cache " + (in != null ? "hit" : "miss")
- + ": " + url);
- }
+ Instance.trace("Cache " + (in != null ? "hit" : "miss") + ": "
+ + url);
if (in == null) {
* @param getParams
* the GET parameters (priority over POST)
* @param oauth
- * OAuth authorization (aka, "bearer XXXXXXX")
+ * OAuth authorisation (aka, "bearer XXXXXXX")
* @return the {@link InputStream} of the opened page
*
* @throws IOException
final URL originalUrl, Map<String, String> postParams,
Map<String, String> getParams, String oauth) throws IOException {
- if (Instance.isDebug()) {
- System.err.println("Open no cache: " + url);
- }
+ Instance.trace("Open no cache: " + url);
URLConnection conn = openConnectionWithCookies(url, support);
if (support != null) {
* @author niki
*/
public class Instance {
+ /**
+ * A handler when a recoverable exception was caught by the program.
+ *
+ * @author niki
+ */
+ public interface SyserrHandler {
+ /**
+ * An exception happened, log it.
+ *
+ * @param e
+ * the exception
+ * @param showDetails
+ * show more details (usually equivalent to the value of
+ * DEBUG)
+ */
+ public void notify(Exception e, boolean showDetails);
+ }
+
+ /**
+ * A handler when a trace message is sent.
+ *
+ * @author niki
+ */
+ public interface TraceHandler {
+ /**
+ * A trace happened, show it.
+ * <p>
+ * Will only be called if TRACE is true.
+ *
+ * @param message
+ * the trace message
+ */
+ public void trace(String message);
+ }
+
private static ConfigBundle config;
private static UiConfigBundle uiconfig;
private static StringIdBundle trans;
private static Cache cache;
private static LocalLibrary lib;
private static boolean debug;
+ private static boolean trace;
private static File coverDir;
private static File readerTmp;
private static File remoteDir;
private static String configDir;
+ private static SyserrHandler syserrHandler;
+
+ private static TraceHandler traceHandler;
+
static {
// Most of the rest is dependent upon this:
config = new ConfigBundle();
}
debug = Instance.getConfig().getBoolean(Config.DEBUG_ERR, false);
+ trace = Instance.getConfig().getBoolean(Config.DEBUG_TRACE, false);
coverDir = getFile(Config.DEFAULT_COVERS_DIR);
File tmp = getFile(Config.CACHE_DIR);
readerTmp = getFile(UiConfig.CACHE_DIR_LOCAL_READER);
}
}
+ /**
+ * Replace the global syserr handler.
+ *
+ * @param syserrHandler
+ * the new syserr handler
+ */
+ public static void setSyserrHandler(SyserrHandler syserrHandler) {
+ Instance.syserrHandler = syserrHandler;
+ }
+
+ /**
+ * Replace the global trace handler.
+ *
+ * @param traceHandler
+ * the new trace handler
+ */
+ public static void setTraceHandler(TraceHandler traceHandler) {
+ Instance.traceHandler = traceHandler;
+ }
+
/**
* Report an error to the user
*
* the {@link Exception} to report
*/
public static void syserr(Exception e) {
- if (debug) {
- e.printStackTrace();
+ if (syserrHandler != null) {
+ syserrHandler.notify(e, debug);
} else {
- System.err.println(e.getMessage());
+ if (debug) {
+ e.printStackTrace();
+ } else {
+ System.err.println(e.getMessage());
+ }
}
}
/**
- * The program is in DEBUG mode (more verbose).
+ * Notify of a debug message.
*
- * @return TRUE if it is
+ * @param message
+ * the message
*/
- public static boolean isDebug() {
- return debug;
+ public static void trace(String message) {
+ if (trace) {
+ if (traceHandler != null) {
+ traceHandler.trace(message);
+ } else {
+ System.out.println(message);
+ }
+ }
}
/**
LIBRARY_DIR, //
@Meta(description = "boolean", format = Format.BOOLEAN, info = "Show debug information on errors")
DEBUG_ERR, //
+ @Meta(description = "boolean", format = Format.BOOLEAN, info = "Show debug trace information")
+ DEBUG_TRACE, //
@Meta(description = "image format", format = Format.COMBO_LIST, list = {
"PNG", "JPG", "BMP" }, info = "Image format to use for cover images")
IMAGE_FORMAT_COVER, //
LOGIN_FIMFICTION_APIKEY_CLIENT_ID, //
@Meta(description = "An API key required to create a token from FimFiction", format = Format.PASSWORD)
LOGIN_FIMFICTION_APIKEY_CLIENT_SECRET, //
- @Meta(description = "Do not use the new API, even if we have a token, and force HTML scraping", format = Format.BOOLEAN)
+ @Meta(description = "Do not use the new API, even if we have a token, and force HTML scraping (default is false, use API if token or ID present)", format = Format.BOOLEAN)
LOGIN_FIMFICTION_APIKEY_FORCE_HTML, //
@Meta(description = "A token is required to use the beta APIv2 from FimFiction (see APIKEY_CLIENT_*)", format = Format.PASSWORD)
LOGIN_FIMFICTION_APIKEY_TOKEN, //
CACHE_MAX_TIME_CHANGING = 24
# delay in hours, or 0 for no cache, or -1 for infinite time (default)
# (FORMAT: INT) The delay after which a cached resource that is thought to change rarely is considered too old and triggers a refresh
-CACHE_MAX_TIME_STABLE =
+CACHE_MAX_TIME_STABLE = 720
# string (FORMAT: STRING) The user-agent to use to download files
USER_AGENT = Mozilla/5.0 (X11; Linux x86_64; rv:44.0) Gecko/20100101 Firefox/44.0 -- ELinks/0.9.3 (Linux 2.6.11 i686; 80x24)
# absolute path, $HOME variable supported, / is always accepted as dir separator
LIBRARY_DIR = $HOME/Books
# boolean (FORMAT: BOOLEAN) Show debug information on errors
DEBUG_ERR = false
+# boolean (FORMAT: BOOLEAN) Show debug trace information
+DEBUG_TRACE = false
# image format (FORMAT: COMBO_LIST) Image format to use for cover images
# ALLOWED VALUES: "PNG" "JPG" "BMP"
IMAGE_FORMAT_COVER = png
# If the last update check was done at least that many days, check for updates at startup (-1 for 'no checks' -- default is 1 day)
# (FORMAT: INT)
UPDATE_INTERVAL =
-# An API key required to use the beta APIv2 from FimFiction
+# An API key required to create a token from FimFiction
+# (FORMAT: STRING)
+LOGIN_FIMFICTION_APIKEY_CLIENT_ID =
+# An API key required to create a token from FimFiction
# (FORMAT: PASSWORD)
-LOGIN_FIMFICTION_APIKEY =
-# Do not use the new API, even if we have an API key, and force HTML scraping
+LOGIN_FIMFICTION_APIKEY_CLIENT_SECRET =
+# Do not use the new API, even if we have a token, and force HTML scraping (default is false, use API if token or ID present)
# (FORMAT: BOOLEAN)
-LOGIN_FIMFICTION_FORCE_HTML =
+LOGIN_FIMFICTION_APIKEY_FORCE_HTML =
+# A token is required to use the beta APIv2 from FimFiction (see APIKEY_CLIENT_*)
+# (FORMAT: PASSWORD)
+LOGIN_FIMFICTION_APIKEY_TOKEN =
import jexer.TApplication;
import jexer.TMessageBox;
import jexer.TWindow;
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.Instance.SyserrHandler;
+import be.nikiroo.fanfix.Instance.TraceHandler;
import be.nikiroo.fanfix.data.MetaData;
import be.nikiroo.fanfix.data.Story;
import be.nikiroo.fanfix.library.BasicLibrary;
private void init(Reader reader) {
this.reader = reader;
+ // Do not allow traces/debug to pollute the screen:
+ Instance.setSyserrHandler(new SyserrHandler() {
+ @Override
+ public void notify(Exception e, boolean showDetails) {
+ // TODO
+ }
+ });
+
+ Instance.setTraceHandler(new TraceHandler() {
+ @Override
+ public void trace(String message) {
+ // TODO
+ }
+ });
+ //
+
// Add the menus
addFileMenu();
addEditMenu();
@Override
protected String getDesc(URL source, InputStream in) {
String desc = getKeyJson(json, 0, "type", "story", "description");
-
- // TODO: if the description becomes available in html, use it
- desc = desc.replace("\\r\\n", "<br/>");
- desc = desc.replace("[i]", "_").replace("[/i]", "_")
- .replace("[b]", "*").replace("[/b]", "*");
- desc = desc.replaceAll("\\[[^\\]]*\\]", "");
-
- return desc;
+ return unbbcode(desc);
}
@Override
|| "www.fimfiction.net".equals(url.getHost());
}
+ /**
+ * Generate a new token from the client ID and secret.
+ * <p>
+ * Note that those tokens are long-lived, and it would be badly seen to
+ * create a lot of them without due cause.
+ * <p>
+ * So, please cache and re-use them.
+ *
+ * @param clientId
+ * the client ID offered on FimFiction
+ * @param clientSecret
+ * the client secret that goes with it
+ *
+ * @return a new generated token linked to that client ID
+ *
+ * @throws IOException
+ * in case of I/O errors
+ */
static private String generateOAuth(String clientId, String clientSecret)
throws IOException {
URL url = new URL("https://www.fimfiction.net/api/v2/token");
String jsonToken = IOUtils.readSmallStream(in);
// Extract token type and token from: {
- // token_type = "bearer",
+ // token_type = "Bearer",
// access_token = "xxxxxxxxxxxxxx"
// }
String token = getKeyText(jsonToken, "\"access_token\"", "\"", "\"");
String tokenType = getKeyText(jsonToken, "\"token_type\"", "\"", "\"");
- // TODO: remove this once the bug is fixed on the server side
- if ("bearer".equals(tokenType)) {
- tokenType = "Bearer";
- }
-
return tokenType + " " + token;
}
return pos;
}
+
+ // quick & dirty filter
+ static private String unbbcode(String bbcode) {
+ String text = bbcode.replace("\\r\\n", "<br/>") //
+ .replace("[i]", "_").replace("[/i]", "_") //
+ .replace("[b]", "*").replace("[/b]", "*") //
+ .replaceAll("\\[[^\\]]*\\]", "");
+ return text;
+ }
}