From: Niki Roo Date: Fri, 10 Apr 2020 17:12:41 +0000 (+0200) Subject: Merge branch 'master' into subtree X-Git-Url: http://git.nikiroo.be/?p=fanfix.git;a=commitdiff_plain;h=2e1300b9580ae1b6dded5a734c617a66a116c16d;hp=-c Merge branch 'master' into subtree --- 2e1300b9580ae1b6dded5a734c617a66a116c16d diff --combined DataLoader.java index 3e0e770,901e8da..901e8da --- a/DataLoader.java +++ b/DataLoader.java @@@ -314,11 -314,10 +314,10 @@@ public class DataLoader throws IOException { String format; if (cover) { - format = Instance.getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER) - .toLowerCase(); + format = Instance.getInstance().getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER).toLowerCase(); } else { - format = Instance.getConfig() - .getString(Config.FILE_FORMAT_IMAGE_FORMAT_CONTENT).toLowerCase(); + format = Instance.getInstance().getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_CONTENT) + .toLowerCase(); } saveAsImage(img, new File(target.toString() + "." + format), format); } diff --combined Instance.java index 561e2f6,f48d05b..f48d05b --- a/Instance.java +++ b/Instance.java @@@ -29,55 -29,83 +29,83 @@@ import be.nikiroo.utils.resources.Bundl * @author niki */ public class Instance { - private static ConfigBundle config; - private static UiConfigBundle uiconfig; - private static StringIdBundle trans; - private static DataLoader cache; - private static StringIdGuiBundle transGui; - private static BasicLibrary lib; - private static File coverDir; - private static File readerTmp; - private static File remoteDir; - private static String configDir; - private static TraceHandler tracer; - private static TempFiles tempFiles; - - private static boolean init; + static private Instance instance; + static private Object instancelock = new Object(); + + private ConfigBundle config; + private UiConfigBundle uiconfig; + private StringIdBundle trans; + private DataLoader cache; + private StringIdGuiBundle transGui; + private BasicLibrary lib; + private File coverDir; + private File readerTmp; + private File remoteDir; + private String configDir; + private TraceHandler tracer; + private TempFiles tempFiles; /** * Initialise the instance -- if already initialised, nothing will happen. *

- * Before calling this method, you may call - * {@link Bundles#setDirectory(String)} if wanted. + * Before calling this method, you may call {@link Bundles#setDirectory(String)} + * if wanted. */ static public void init() { init(false); } /** - * Initialise the instance -- if already initialised, nothing will happen - * unless you pass TRUE to force. + * Initialise the instance -- if already initialised, nothing will happen unless + * you pass TRUE to force. *

- * Before calling this method, you may call - * {@link Bundles#setDirectory(String)} if wanted. + * Before calling this method, you may call {@link Bundles#setDirectory(String)} + * if wanted. *

- * Note: forcing the initialisation can be dangerous, so make sure to only - * make it under controlled circumstances -- for instance, at the start of - * the program, you could call {@link Instance#init()}, change some settings - * because you want to force those settings (it will also forbid users to - * change them!) and then call {@link Instance#init(boolean)} with - * force set to TRUE. + * Note: forcing the initialisation can be dangerous, so make sure to only make + * it under controlled circumstances -- for instance, at the start of the + * program, you could call {@link Instance#init()}, change some settings because + * you want to force those settings (it will also forbid users to change them!) + * and then call {@link Instance#init(boolean)} with force set to TRUE. * - * @param force - * force the initialisation even if already initialised + * @param force force the initialisation even if already initialised */ static public void init(boolean force) { - if (init && !force) { - return; + synchronized (instancelock) { + if (instance == null || force) { + instance = new Instance(); + } } - init = true; + } + + /** + * Force-initialise the {@link Instance} to a known value. + *

+ * Usually for DEBUG/Test purposes. + * + * @param instance the actual Instance to use + */ + static public void init(Instance instance) { + Instance.instance = instance; + } + + /** + * The (mostly unique) instance of this {@link Instance}. + * + * @return the (mostly unique) instance + */ + public static Instance getInstance() { + return instance; + } + /** + * Actually initialise the instance. + *

+ * Before calling this method, you may call {@link Bundles#setDirectory(String)} + * if wanted. + */ + protected Instance() { // Before we can configure it: Boolean debug = checkEnv("DEBUG"); boolean trace = debug != null && debug; @@@ -93,12 -121,12 +121,12 @@@ createConfigs(configDir, false); // Proxy support - Proxy.use(Instance.getConfig().getString(Config.NETWORK_PROXY)); + Proxy.use(config.getString(Config.NETWORK_PROXY)); // update tracer: if (debug == null) { - debug = Instance.getConfig().getBoolean(Config.DEBUG_ERR, false); - trace = Instance.getConfig().getBoolean(Config.DEBUG_TRACE, false); + debug = config.getBoolean(Config.DEBUG_ERR, false); + trace = config.getBoolean(Config.DEBUG_TRACE, false); } tracer = new TraceHandler(true, debug, trace); @@@ -120,19 -148,16 +148,16 @@@ int hoursLarge = config.getInteger(Config.CACHE_MAX_TIME_STABLE, 0); cache = new DataLoader(tmp, ua, hours, hoursLarge); } catch (IOException e) { - tracer.error(new IOException( - "Cannot create cache (will continue without cache)", e)); + tracer.error(new IOException("Cannot create cache (will continue without cache)", e)); cache = new DataLoader(ua); } cache.setTraceHandler(tracer); // readerTmp / coverDir - readerTmp = getFile(UiConfig.CACHE_DIR_LOCAL_READER, new File( - configDir, "tmp-reader")); + readerTmp = getFile(UiConfig.CACHE_DIR_LOCAL_READER, new File(configDir, "tmp-reader")); - coverDir = getFile(Config.DEFAULT_COVERS_DIR, new File(configDir, - "covers")); + coverDir = getFile(Config.DEFAULT_COVERS_DIR, new File(configDir, "covers")); coverDir.mkdirs(); try { @@@ -149,22 -174,21 +174,21 @@@ * * @return the traces handler (never NULL) */ - public static TraceHandler getTraceHandler() { + public TraceHandler getTraceHandler() { return tracer; } /** * The traces handler for this {@link Cache}. * - * @param tracer - * the new traces handler or NULL + * @param tracer the new traces handler or NULL */ - public static void setTraceHandler(TraceHandler tracer) { + public void setTraceHandler(TraceHandler tracer) { if (tracer == null) { tracer = new TraceHandler(false, false, false); } - Instance.tracer = tracer; + this.tracer = tracer; cache.setTraceHandler(tracer); } @@@ -173,7 -197,7 +197,7 @@@ * * @return the configuration service */ - public static ConfigBundle getConfig() { + public ConfigBundle getConfig() { return config; } @@@ -182,17 -206,16 +206,16 @@@ * * @return the configuration service */ - public static UiConfigBundle getUiConfig() { + public UiConfigBundle getUiConfig() { return uiconfig; } /** * Reset the configuration. * - * @param resetTrans - * also reset the translation files + * @param resetTrans also reset the translation files */ - public static void resetConfig(boolean resetTrans) { + public void resetConfig(boolean resetTrans) { String dir = Bundles.getDirectory(); Bundles.setDirectory(null); try { @@@ -227,7 -250,7 +250,7 @@@ * * @return the {@link DataLoader} */ - public static DataLoader getCache() { + public DataLoader getCache() { return cache; } @@@ -238,7 -261,7 +261,7 @@@ * * @return the {link StringIdBundle} */ - public static StringIdBundle getTrans() { + public StringIdBundle getTrans() { return trans; } @@@ -249,7 -272,7 +272,7 @@@ * * @return the {link StringIdGuiBundle} */ - public static StringIdGuiBundle getTransGui() { + public StringIdGuiBundle getTransGui() { return transGui; } @@@ -258,7 -281,7 +281,7 @@@ * * @return the {@link LocalLibrary} */ - public static BasicLibrary getLibrary() { + public BasicLibrary getLibrary() { if (lib == null) { throw new NullPointerException("We don't have a library to return"); } @@@ -271,7 -294,7 +294,7 @@@ * * @return the default covers directory */ - public static File getCoverDir() { + public File getCoverDir() { return coverDir; } @@@ -280,7 -303,7 +303,7 @@@ * * @return the directory */ - public static File getReaderDir() { + public File getReaderDir() { return readerTmp; } @@@ -288,12 -311,11 +311,11 @@@ * Return the directory where to store temporary files for the remote * {@link LocalLibrary}. * - * @param host - * the remote for this host + * @param host the remote for this host * * @return the directory */ - public static File getRemoteDir(String host) { + public File getRemoteDir(String host) { return getRemoteDir(remoteDir, host); } @@@ -301,14 -323,12 +323,12 @@@ * Return the directory where to store temporary files for the remote * {@link LocalLibrary}. * - * @param remoteDir - * the base remote directory - * @param host - * the remote for this host + * @param remoteDir the base remote directory + * @param host the remote for this host * * @return the directory */ - private static File getRemoteDir(File remoteDir, String host) { + private File getRemoteDir(File remoteDir, String host) { remoteDir.mkdirs(); if (host != null) { @@@ -323,15 -343,12 +343,12 @@@ * * @return TRUE if we need to */ - public static boolean isVersionCheckNeeded() { + public boolean isVersionCheckNeeded() { try { - long wait = config.getInteger(Config.NETWORK_UPDATE_INTERVAL, 0) - * 24 * 60 * 60 * 1000; + long wait = config.getInteger(Config.NETWORK_UPDATE_INTERVAL, 0) * 24 * 60 * 60 * 1000; if (wait >= 0) { - String lastUpString = IOUtils.readSmallFile(new File(configDir, - "LAST_UPDATE")); - long delay = new Date().getTime() - - Long.parseLong(lastUpString); + String lastUpString = IOUtils.readSmallFile(new File(configDir, "LAST_UPDATE")); + long delay = new Date().getTime() - Long.parseLong(lastUpString); if (delay > wait) { return true; } @@@ -349,10 -366,9 +366,9 @@@ /** * Notify that we checked for a new version of Fanfix. */ - public static void setVersionChecked() { + public void setVersionChecked() { try { - IOUtils.writeSmallFile(new File(configDir), "LAST_UPDATE", - Long.toString(new Date().getTime())); + IOUtils.writeSmallFile(new File(configDir), "LAST_UPDATE", Long.toString(new Date().getTime())); } catch (IOException e) { tracer.error(e); } @@@ -365,18 -381,18 +381,18 @@@ * * @return the facility */ - public static TempFiles getTempFiles() { + public TempFiles getTempFiles() { return tempFiles; } /** - * The configuration directory (will check, in order of preference, the - * system properties, the environment and then defaults to + * The configuration directory (will check, in order of preference, the system + * properties, the environment and then defaults to * {@link Instance#getHome()}/.fanfix). * * @return the config directory */ - private static String getConfigDir() { + private String getConfigDir() { String configDir = System.getProperty("CONFIG_DIR"); if (configDir == null) { @@@ -395,13 -411,11 +411,11 @@@ * {@link Instance#uiconfig}, {@link Instance#trans} and * {@link Instance#transGui}). * - * @param configDir - * the directory where to find the configuration files - * @param refresh - * TRUE to reset the configuration files from the default - * included ones + * @param configDir the directory where to find the configuration files + * @param refresh TRUE to reset the configuration files from the default + * included ones */ - private static void createConfigs(String configDir, boolean refresh) { + private void createConfigs(String configDir, boolean refresh) { if (!refresh) { Bundles.setDirectory(configDir); } @@@ -443,16 -457,14 +457,14 @@@ /** * Create the default library as specified by the config. * - * @param remoteDir - * the base remote directory if needed + * @param remoteDir the base remote directory if needed * * @return the default {@link BasicLibrary} */ - private static BasicLibrary createDefaultLibrary(File remoteDir) { + private BasicLibrary createDefaultLibrary(File remoteDir) { BasicLibrary lib = null; - boolean useRemote = config.getBoolean(Config.REMOTE_LIBRARY_ENABLED, - false); + boolean useRemote = config.getBoolean(Config.REMOTE_LIBRARY_ENABLED, false); if (useRemote) { String host = null; @@@ -464,11 -476,9 +476,9 @@@ tracer.trace("Selecting remote library " + host + ":" + port); lib = new RemoteLibrary(key, host, port); - lib = new CacheLibrary(getRemoteDir(remoteDir, host), lib); + lib = new CacheLibrary(getRemoteDir(remoteDir, host), lib, uiconfig); } catch (Exception e) { - tracer.error(new IOException( - "Cannot create remote library for: " + host + ":" - + port, e)); + tracer.error(new IOException("Cannot create remote library for: " + host + ":" + port, e)); } } else { String libDir = System.getenv("BOOKS_DIR"); @@@ -479,11 -489,9 +489,9 @@@ } } try { - lib = new LocalLibrary(getFile(libDir)); + lib = new LocalLibrary(getFile(libDir), config); } catch (Exception e) { - tracer.error(new IOException( - "Cannot create library for directory: " - + getFile(libDir), e)); + tracer.error(new IOException("Cannot create library for directory: " + getFile(libDir), e)); } } @@@ -493,9 -501,11 +501,11 @@@ /** * Return a path, but support the special $HOME variable. * - * @return the path + * @param id the key for the path, which may contain "$HOME" + * @param def the default value if none + * @return the path, with expanded "$HOME" if needed */ - private static File getFile(Config id, File def) { + protected File getFile(Config id, File def) { String path = config.getString(id, def.getPath()); return getFile(path); } @@@ -503,9 -513,11 +513,11 @@@ /** * Return a path, but support the special $HOME variable. * - * @return the path + * @param id the key for the path, which may contain "$HOME" + * @param def the default value if none + * @return the path, with expanded "$HOME" if needed */ - private static File getFile(UiConfig id, File def) { + protected File getFile(UiConfig id, File def) { String path = uiconfig.getString(id, def.getPath()); return getFile(path); } @@@ -513,9 -525,10 +525,10 @@@ /** * Return a path, but support the special $HOME variable. * - * @return the path + * @param path the path, which may contain "$HOME" + * @return the path, with expanded "$HOME" if needed */ - private static File getFile(String path) { + protected File getFile(String path) { File file = null; if (path != null && !path.isEmpty()) { path = path.replace('/', File.separatorChar); @@@ -534,12 -547,12 +547,12 @@@ * properties. *

* The environment variable is tested first. Then, the custom property - * "fanfix.home" is tried, followed by the usual "user.home" then - * "java.io.tmp" if nothing else is found. + * "fanfix.home" is tried, followed by the usual "user.home" then "java.io.tmp" + * if nothing else is found. * * @return the home */ - private static String getHome() { + protected String getHome() { String home = System.getenv("FANFIX_DIR"); if (home != null && new File(home).isFile()) { home = null; @@@ -578,12 -591,11 +591,11 @@@ * * @return the language */ - private static String getLang() { + protected String getLang() { String lang = config.getString(Config.LANG); if (lang == null || lang.isEmpty()) { - if (System.getenv("LANG") != null - && !System.getenv("LANG").isEmpty()) { + if (System.getenv("LANG") != null && !System.getenv("LANG").isEmpty()) { lang = System.getenv("LANG"); } } @@@ -598,17 -610,15 +610,15 @@@ /** * Check that the given environment variable is "enabled". * - * @param key - * the variable to check + * @param key the variable to check * * @return TRUE if it is */ - private static Boolean checkEnv(String key) { + protected Boolean checkEnv(String key) { String value = System.getenv(key); if (value != null) { value = value.trim().toLowerCase(); - if ("yes".equals(value) || "true".equals(value) - || "on".equals(value) || "1".equals(value) + if ("yes".equals(value) || "true".equals(value) || "on".equals(value) || "1".equals(value) || "y".equals(value)) { return true; } diff --combined Main.java index 2ed276b,961816a..961816a --- a/Main.java +++ b/Main.java @@@ -10,9 -10,7 +10,7 @@@ import java.util.List import javax.net.ssl.SSLException; import be.nikiroo.fanfix.bundles.Config; - import be.nikiroo.fanfix.bundles.ConfigBundle; import be.nikiroo.fanfix.bundles.StringId; - import be.nikiroo.fanfix.bundles.StringIdBundle; import be.nikiroo.fanfix.data.Chapter; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Story; @@@ -31,8 -29,6 +29,6 @@@ import be.nikiroo.fanfix.supported.Basi import be.nikiroo.fanfix.supported.SupportType; import be.nikiroo.utils.Progress; import be.nikiroo.utils.Version; - import be.nikiroo.utils.resources.Bundles; - import be.nikiroo.utils.resources.TransBundle; import be.nikiroo.utils.serial.server.ServerObject; /** @@@ -126,9 -122,8 +122,8 @@@ public class Main action = MainAction.valueOf(args[i].substring(2) .toUpperCase().replace("-", "_")); } catch (Exception e) { - Instance.getTraceHandler().error( - new IllegalArgumentException("Unknown action: " - + args[i], e)); + Instance.getInstance().getTraceHandler() + .error(new IllegalArgumentException("Unknown action: " + args[i], e)); exitCode = 255; } } @@@ -229,15 -224,13 +224,13 @@@ searchOn = SupportType.valueOfAllOkUC(args[i]); if (searchOn == null) { - Instance.getTraceHandler().error( - "Website not known: <" + args[i] + ">"); + Instance.getInstance().getTraceHandler().error("Website not known: <" + args[i] + ">"); exitCode = 41; break; } if (BasicSearchable.getSearchable(searchOn) == null) { - Instance.getTraceHandler().error( - "Website not supported: " + searchOn); + Instance.getInstance().getTraceHandler().error("Website not supported: " + searchOn); exitCode = 42; break; } @@@ -272,14 -265,12 +265,12 @@@ searchOn = SupportType.valueOfAllOkUC(args[i]); if (searchOn == null) { - Instance.getTraceHandler().error( - "Website not known: <" + args[i] + ">"); + Instance.getInstance().getTraceHandler().error("Website not known: <" + args[i] + ">"); exitCode = 255; } if (BasicSearchable.getSearchable(searchOn) == null) { - Instance.getTraceHandler().error( - "Website not supported: " + searchOn); + Instance.getInstance().getTraceHandler().error("Website not supported: " + searchOn); exitCode = 255; } } else if (page == null && item == null) { @@@ -292,8 -283,7 +283,7 @@@ int index = Integer.parseInt(args[i]); tags.add(index); } catch (NumberFormatException e) { - Instance.getTraceHandler().error( - "Invalid tag index: " + args[i]); + Instance.getInstance().getTraceHandler().error("Invalid tag index: " + args[i]); exitCode = 255; } } @@@ -349,7 -339,8 +339,8 @@@ port = Integer.parseInt(args[i]); BasicLibrary lib = new RemoteLibrary(key, host, port); - lib = new CacheLibrary(Instance.getRemoteDir(host), lib); + lib = new CacheLibrary(Instance.getInstance().getRemoteDir(host), lib, + Instance.getInstance().getUiConfig()); BasicReader.setDefaultLibrary(lib); @@@ -423,9 -414,7 +414,7 @@@ break; case LIST: if (BasicReader.getReader() == null) { - Instance.getTraceHandler() - .error(new Exception( - "No reader type has been configured")); + Instance.getInstance().getTraceHandler().error(new Exception("No reader type has been configured")); exitCode = 10; break; } @@@ -433,33 -422,31 +422,31 @@@ break; case SET_SOURCE: try { - Instance.getLibrary().changeSource(luid, sourceString, pg); + Instance.getInstance().getLibrary().changeSource(luid, sourceString, pg); } catch (IOException e1) { - Instance.getTraceHandler().error(e1); + Instance.getInstance().getTraceHandler().error(e1); exitCode = 21; } break; case SET_TITLE: try { - Instance.getLibrary().changeTitle(luid, titleString, pg); + Instance.getInstance().getLibrary().changeTitle(luid, titleString, pg); } catch (IOException e1) { - Instance.getTraceHandler().error(e1); + Instance.getInstance().getTraceHandler().error(e1); exitCode = 22; } break; case SET_AUTHOR: try { - Instance.getLibrary().changeAuthor(luid, authorString, pg); + Instance.getInstance().getLibrary().changeAuthor(luid, authorString, pg); } catch (IOException e1) { - Instance.getTraceHandler().error(e1); + Instance.getInstance().getTraceHandler().error(e1); exitCode = 23; } break; case READ: if (BasicReader.getReader() == null) { - Instance.getTraceHandler() - .error(new Exception( - "No reader type has been configured")); + Instance.getInstance().getTraceHandler().error(new Exception("No reader type has been configured")); exitCode = 10; break; } @@@ -467,9 -454,7 +454,7 @@@ break; case READ_URL: if (BasicReader.getReader() == null) { - Instance.getTraceHandler() - .error(new Exception( - "No reader type has been configured")); + Instance.getInstance().getTraceHandler().error(new Exception("No reader type has been configured")); exitCode = 10; break; } @@@ -478,22 -463,20 +463,20 @@@ case SEARCH: page = page == null ? 1 : page; if (page < 0) { - Instance.getTraceHandler().error("Incorrect page number"); + Instance.getInstance().getTraceHandler().error("Incorrect page number"); exitCode = 255; break; } item = item == null ? 0 : item; if (item < 0) { - Instance.getTraceHandler().error("Incorrect item number"); + Instance.getInstance().getTraceHandler().error("Incorrect item number"); exitCode = 255; break; } if (BasicReader.getReader() == null) { - Instance.getTraceHandler() - .error(new Exception( - "No reader type has been configured")); + Instance.getInstance().getTraceHandler().error(new Exception("No reader type has been configured")); exitCode = 10; break; } @@@ -509,7 -492,7 +492,7 @@@ exitCode = 255; } } catch (IOException e1) { - Instance.getTraceHandler().error(e1); + Instance.getInstance().getTraceHandler().error(e1); exitCode = 20; } @@@ -522,22 -505,20 +505,20 @@@ page = page == null ? 1 : page; if (page < 0) { - Instance.getTraceHandler().error("Incorrect page number"); + Instance.getInstance().getTraceHandler().error("Incorrect page number"); exitCode = 255; break; } item = item == null ? 0 : item; if (item < 0) { - Instance.getTraceHandler().error("Incorrect item number"); + Instance.getInstance().getTraceHandler().error("Incorrect item number"); exitCode = 255; break; } if (BasicReader.getReader() == null) { - Instance.getTraceHandler() - .error(new Exception( - "No reader type has been configured")); + Instance.getInstance().getTraceHandler().error(new Exception("No reader type has been configured")); exitCode = 10; break; } @@@ -546,7 -527,7 +527,7 @@@ BasicReader.getReader().searchTag(searchOn, page, item, true, tags.toArray(new Integer[] {})); } catch (IOException e1) { - Instance.getTraceHandler().error(e1); + Instance.getInstance().getTraceHandler().error(e1); } break; @@@ -567,22 -548,20 +548,20 @@@ break; case START: if (BasicReader.getReader() == null) { - Instance.getTraceHandler() - .error(new Exception( - "No reader type has been configured")); + Instance.getInstance().getTraceHandler().error(new Exception("No reader type has been configured")); exitCode = 10; break; } try { BasicReader.getReader().browse(null); } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); exitCode = 66; } break; case SERVER: - key = Instance.getConfig().getString(Config.SERVER_KEY); - port = Instance.getConfig().getInteger(Config.SERVER_PORT); + key = Instance.getInstance().getConfig().getString(Config.SERVER_KEY); + port = Instance.getInstance().getConfig().getInteger(Config.SERVER_PORT); if (port == null) { System.err.println("No port configured in the config file"); exitCode = 15; @@@ -590,28 -569,32 +569,32 @@@ } try { ServerObject server = new RemoteLibraryServer(key, port); - server.setTraceHandler(Instance.getTraceHandler()); + server.setTraceHandler(Instance.getInstance().getTraceHandler()); server.run(); } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } return; case STOP_SERVER: - key = Instance.getConfig().getString(Config.SERVER_KEY); - port = Instance.getConfig().getInteger(Config.SERVER_PORT); + // Can be given via "--remote XX XX XX" + if (key == null) + key = Instance.getInstance().getConfig().getString(Config.SERVER_KEY); + if (port == null) + port = Instance.getInstance().getConfig().getInteger(Config.SERVER_PORT); + if (port == null) { - System.err.println("No port configured in the config file"); + System.err.println("No port given nor configured in the config file"); exitCode = 15; break; } try { new RemoteLibrary(key, host, port).exit(); } catch (SSLException e) { - Instance.getTraceHandler().error( + Instance.getInstance().getTraceHandler().error( "Bad access key for remote library"); exitCode = 43; } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); exitCode = 44; } @@@ -623,11 -606,9 +606,9 @@@ } try { - Instance.getTempFiles().close(); + Instance.getInstance().getTempFiles().close(); } catch (IOException e) { - Instance.getTraceHandler() - .error(new IOException( - "Cannot dispose of the temporary files", e)); + Instance.getInstance().getTraceHandler().error(new IOException("Cannot dispose of the temporary files", e)); } if (exitCode == 255) { @@@ -649,12 -630,10 +630,10 @@@ */ public static int imprt(String urlString, Progress pg) { try { - MetaData meta = Instance.getLibrary().imprt( - BasicReader.getUrl(urlString), pg); - System.out.println(meta.getLuid() + ": \"" + meta.getTitle() - + "\" imported."); + MetaData meta = Instance.getInstance().getLibrary().imprt(BasicReader.getUrl(urlString), pg); + System.out.println(meta.getLuid() + ": \"" + meta.getTitle() + "\" imported."); } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); return 1; } @@@ -680,15 -659,14 +659,14 @@@ Progress pg) { OutputType type = OutputType.valueOfNullOkUC(typeString, null); if (type == null) { - Instance.getTraceHandler().error( - new Exception(trans(StringId.OUTPUT_DESC, typeString))); + Instance.getInstance().getTraceHandler().error(new Exception(trans(StringId.OUTPUT_DESC, typeString))); return 1; } try { - Instance.getLibrary().export(luid, type, target, pg); + Instance.getInstance().getLibrary().export(luid, type, target, pg); } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); return 4; } @@@ -710,7 -688,7 +688,7 @@@ try { BasicReader.getReader().browse(source); } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); return 66; } @@@ -746,16 -724,15 +724,15 @@@ reader.setChapter(Integer.parseInt(chapString)); reader.read(true); } catch (NumberFormatException e) { - Instance.getTraceHandler().error( - new IOException("Chapter number cannot be parsed: " - + chapString, e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Chapter number cannot be parsed: " + chapString, e)); return 2; } } else { reader.read(true); } } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); return 1; } @@@ -783,7 -760,7 +760,7 @@@ String target, boolean infoCover, Progress pg) { int exitCode = 0; - Instance.getTraceHandler().trace("Convert: " + urlString); + Instance.getInstance().getTraceHandler().trace("Convert: " + urlString); String sourceName = urlString; try { URL source = BasicReader.getUrl(urlString); @@@ -794,9 -771,8 +771,8 @@@ OutputType type = OutputType.valueOfAllOkUC(typeString, null); if (type == null) { - Instance.getTraceHandler().error( - new IOException(trans(StringId.ERR_BAD_OUTPUT_TYPE, - typeString))); + Instance.getInstance().getTraceHandler() + .error(new IOException(trans(StringId.ERR_BAD_OUTPUT_TYPE, typeString))); exitCode = 2; } else { @@@ -804,8 -780,7 +780,7 @@@ BasicSupport support = BasicSupport.getSupport(source); if (support != null) { - Instance.getTraceHandler().trace( - "Support found: " + support.getClass()); + Instance.getInstance().getTraceHandler().trace("Support found: " + support.getClass()); Progress pgIn = new Progress(); Progress pgOut = new Progress(); if (pg != null) { @@@ -817,32 -792,26 +792,26 @@@ Story story = support.process(pgIn); try { target = new File(target).getAbsolutePath(); - BasicOutput.getOutput(type, infoCover, infoCover) - .process(story, target, pgOut); + BasicOutput.getOutput(type, infoCover, infoCover).process(story, target, pgOut); } catch (IOException e) { - Instance.getTraceHandler().error( - new IOException(trans(StringId.ERR_SAVING, - target), e)); + Instance.getInstance().getTraceHandler() + .error(new IOException(trans(StringId.ERR_SAVING, target), e)); exitCode = 5; } } else { - Instance.getTraceHandler().error( - new IOException(trans( - StringId.ERR_NOT_SUPPORTED, source))); + Instance.getInstance().getTraceHandler() + .error(new IOException(trans( StringId.ERR_NOT_SUPPORTED, source))); exitCode = 4; } } catch (IOException e) { - Instance.getTraceHandler().error( - new IOException(trans(StringId.ERR_LOADING, - sourceName), e)); + Instance.getInstance().getTraceHandler() + .error(new IOException(trans(StringId.ERR_LOADING, sourceName), e)); exitCode = 3; } } } catch (MalformedURLException e) { - Instance.getTraceHandler() - .error(new IOException(trans(StringId.ERR_BAD_URL, - sourceName), e)); + Instance.getInstance().getTraceHandler().error(new IOException(trans(StringId.ERR_BAD_URL, sourceName), e)); exitCode = 1; } @@@ -858,7 -827,7 +827,7 @@@ * @return the translated result */ private static String trans(StringId id, Object... params) { - return Instance.getTrans().getString(id, params); + return Instance.getInstance().getTrans().getString(id, params); } /** @@@ -908,9 -877,8 +877,8 @@@ BasicReader.setDefaultReaderType(readerType); return 0; } catch (IllegalArgumentException e) { - Instance.getTraceHandler().error( - new IOException("Unknown reader type: " + readerTypeString, - e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Unknown reader type: " + readerTypeString, e)); return 1; } } diff --combined VersionCheck.java index 2c9a032,f64159a..f64159a --- a/VersionCheck.java +++ b/VersionCheck.java @@@ -93,7 -93,7 +93,7 @@@ public class VersionCheck * time has elapsed. */ public void ok() { - Instance.setVersionChecked(); + Instance.getInstance().setVersionChecked(); } /** @@@ -107,10 -107,10 +107,10 @@@ List newer = new ArrayList(); Map> changes = new HashMap>(); - if (Instance.isVersionCheckNeeded()) { + if (Instance.getInstance().isVersionCheckNeeded()) { try { // Prepare the URLs according to the user's language - Locale lang = Instance.getTrans().getLocale(); + Locale lang = Instance.getInstance().getTrans().getLocale(); String fr = lang.getLanguage(); String BE = lang.getCountry().replace(".UTF8", ""); String urlFrBE = base.replace("${LANG}", "-" + fr + "_" + BE); @@@ -120,8 -120,7 +120,7 @@@ InputStream in = null; for (String url : new String[] { urlFrBE, urlFr, urlDefault }) { try { - in = Instance.getCache() - .open(new URL(url), null, false); + in = Instance.getInstance().getCache().open(new URL(url), null, false); break; } catch (IOException e) { } @@@ -162,10 -161,8 +161,8 @@@ reader.close(); } } catch (IOException e) { - Instance.getTraceHandler() - .error(new IOException( - "Cannot download latest changelist on github.com", - e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Cannot download latest changelist on github.com", e)); } } diff --combined bundles/resources_core.properties index d656ed6,dc7881a..dc7881a --- a/bundles/resources_core.properties +++ b/bundles/resources_core.properties @@@ -124,9 -124,7 +124,7 @@@ INPUT_DESC_FANFICTION = Fanfictions of INPUT_DESC_FIMFICTION = Fanfictions devoted to the My Little Pony show # Description of this input type # (FORMAT: STRING) - INPUT_DESC_MANGAFOX = A well filled repository of mangas, or, as their website states: \n\ - \tMost popular manga scanlations read online for free at mangafox, \n\ - \tas well as a close-knit community to chat and make friends. + INPUT_DESC_MANGAHUB = A well filled repository of mangas, in English # Description of this input type # (FORMAT: STRING) INPUT_DESC_E621 = Furry website supporting comics, including MLP diff --combined bundles/resources_core_fr.properties index 9bf3626,a64a5a0..a64a5a0 --- a/bundles/resources_core_fr.properties +++ b/bundles/resources_core_fr.properties @@@ -109,7 -109,7 +109,7 @@@ INPUT_DESC_FANFICTION = Fanfictions ven INPUT_DESC_FIMFICTION = Fanfictions dévouées à la série My Little Pony # Description of this input type # (FORMAT: STRING) - INPUT_DESC_MANGAFOX = Un site répertoriant une quantité non négligeable de mangas + INPUT_DESC_MANGAHUB = Un site répertoriant une quantité non négligeable de mangas, en anglais # Description of this input type # (FORMAT: STRING) INPUT_DESC_E621 = Un site Furry proposant des comics, y compris de MLP diff --combined library/BasicLibrary.java index 099859d,c2ab12b..c2ab12b --- a/library/BasicLibrary.java +++ b/library/BasicLibrary.java @@@ -122,6 -122,13 +122,13 @@@ abstract public class BasicLibrary */ public abstract Image getCover(String luid) throws IOException; + // TODO: ensure it is the main used interface + public synchronized MetaResultList getList(Progress pg) throws IOException { + return new MetaResultList(getMetas(pg)); + } + + //TODO: make something for (normal and custom) not-story covers + /** * Return the cover image associated to this source. *

@@@ -142,7 -149,7 +149,7 @@@ return custom; } - List metas = getListBySource(source); + List metas = getList().filter(source, null, null); if (metas.size() > 0) { return getCover(metas.get(0).getLuid()); } @@@ -170,7 -177,7 +177,7 @@@ return custom; } - List metas = getListByAuthor(author); + List metas = getList().filter(null, author, null); if (metas.size() > 0) { return getCover(metas.get(0).getLuid()); } @@@ -245,6 -252,8 +252,8 @@@ /** * Return the list of stories (represented by their {@link MetaData}, which * MAY not have the cover included). + *

+ * The returned list MUST be a copy, not the original one. * * @param pg * the optional {@link Progress} @@@ -329,13 -338,43 +338,43 @@@ * @param pg * the optional progress reporter */ - public void refresh(Progress pg) { + public synchronized void refresh(Progress pg) { try { getMetas(pg); } catch (IOException e) { // We will let it fail later } } + + /** + * Check if the {@link Story} denoted by this Library UID is present in the + * cache (if we have no cache, we default to true). + * + * @param luid + * the Library UID + * + * @return TRUE if it is + */ + public boolean isCached(String luid) { + // By default, everything is cached + return true; + } + + /** + * Clear the {@link Story} from the cache, if needed. + *

+ * The next time we try to retrieve the {@link Story}, it may be required to + * cache it again. + * + * @param luid + * the story to clear + * + * @throws IOException + * in case of I/O error + */ + public void clearFromCache(String luid) throws IOException { + // By default, this is a noop. + } /** * List all the known types (sources) of stories. @@@ -558,64 -597,8 +597,8 @@@ * @throws IOException * in case of IOException */ - public synchronized List getList() throws IOException { - return getMetas(null); - } - - /** - * List all the stories of the given source type in the {@link BasicLibrary} - * , or all the stories if NULL is passed as a type. - *

- * Cover images not included. - * - * @param type - * the type of story to retrieve, or NULL for all - * - * @return the stories - * - * @throws IOException - * in case of IOException - */ - public synchronized List getListBySource(String type) - throws IOException { - List list = new ArrayList(); - for (MetaData meta : getMetas(null)) { - String storyType = meta.getSource(); - if (type == null || type.equalsIgnoreCase(storyType)) { - list.add(meta); - } - } - - Collections.sort(list); - return list; - } - - /** - * List all the stories of the given author in the {@link BasicLibrary}, or - * all the stories if NULL is passed as an author. - *

- * Cover images not included. - * - * @param author - * the author of the stories to retrieve, or NULL for all - * - * @return the stories - * - * @throws IOException - * in case of IOException - */ - public synchronized List getListByAuthor(String author) - throws IOException { - List list = new ArrayList(); - for (MetaData meta : getMetas(null)) { - String storyAuthor = meta.getAuthor(); - if (author == null || author.equalsIgnoreCase(storyAuthor)) { - list.add(meta); - } - } - - Collections.sort(list); - return list; + public MetaResultList getList() throws IOException { + return getList(null); } /** @@@ -730,10 -713,8 +713,8 @@@ } catch (IOException e) { // We should not have not-supported files in the // library - Instance.getTraceHandler().error( - new IOException(String.format( - "Cannot load file of type '%s' from library: %s", - meta.getType(), file), e)); + Instance.getInstance().getTraceHandler().error(new IOException( + String.format("Cannot load file of type '%s' from library: %s", meta.getType(), file), e)); } finally { pgProcess.done(); pg.done(); @@@ -892,8 -873,7 +873,7 @@@ public synchronized Story save(Story story, String luid, Progress pg) throws IOException { - Instance.getTraceHandler().trace( - this.getClass().getSimpleName() + ": saving story " + luid); + Instance.getInstance().getTraceHandler().trace(this.getClass().getSimpleName() + ": saving story " + luid); // Do not change the original metadata, but change the original story MetaData meta = story.getMeta().clone(); @@@ -913,9 -893,8 +893,8 @@@ updateInfo(story.getMeta()); - Instance.getTraceHandler().trace( - this.getClass().getSimpleName() + ": story saved (" + luid - + ")"); + Instance.getInstance().getTraceHandler() + .trace(this.getClass().getSimpleName() + ": story saved (" + luid + ")"); return story; } @@@ -930,14 -909,13 +909,13 @@@ * in case of I/O error */ public synchronized void delete(String luid) throws IOException { - Instance.getTraceHandler().trace( - this.getClass().getSimpleName() + ": deleting story " + luid); + Instance.getInstance().getTraceHandler().trace(this.getClass().getSimpleName() + ": deleting story " + luid); doDelete(luid); invalidateInfo(luid); - Instance.getTraceHandler().trace( - this.getClass().getSimpleName() + ": story deleted (" + luid + Instance.getInstance().getTraceHandler() + .trace(this.getClass().getSimpleName() + ": story deleted (" + luid + ")"); } @@@ -1039,8 -1017,6 +1017,6 @@@ meta.setTitle(newTitle); meta.setAuthor(newAuthor); saveMeta(meta, pg); - - invalidateInfo(luid); } /** diff --combined library/CacheLibrary.java index e8743b6,694f9ec..694f9ec --- a/library/CacheLibrary.java +++ b/library/CacheLibrary.java @@@ -3,12 -3,16 +3,16 @@@ package be.nikiroo.fanfix.library import java.io.File; import java.io.IOException; import java.net.URL; + import java.util.ArrayList; import java.util.List; + import java.util.TreeSet; import be.nikiroo.fanfix.Instance; import be.nikiroo.fanfix.bundles.UiConfig; + import be.nikiroo.fanfix.bundles.UiConfigBundle; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Story; + import be.nikiroo.fanfix.output.BasicOutput.OutputType; import be.nikiroo.utils.Image; import be.nikiroo.utils.Progress; @@@ -18,26 -22,26 +22,26 @@@ * @author niki */ public class CacheLibrary extends BasicLibrary { - private List metas; + private List metasReal; + private List metasMixed; private BasicLibrary lib; private LocalLibrary cacheLib; /** * Create a cache library around the given one. *

- * It will return the same result, but those will be saved to disk at the - * same time to be fetched quicker the next time. + * It will return the same result, but those will be saved to disk at the same + * time to be fetched quicker the next time. * - * @param cacheDir - * the cache directory where to save the files to disk - * @param lib - * the original library to wrap + * @param cacheDir the cache directory where to save the files to disk + * @param lib the original library to wrap + * @param config the configuration used to know which kind of default + * {@link OutputType} to use for images and non-images stories */ - public CacheLibrary(File cacheDir, BasicLibrary lib) { - this.cacheLib = new LocalLibrary(cacheDir, Instance.getUiConfig() - .getString(UiConfig.GUI_NON_IMAGES_DOCUMENT_TYPE), Instance - .getUiConfig().getString(UiConfig.GUI_IMAGES_DOCUMENT_TYPE), - true); + public CacheLibrary(File cacheDir, BasicLibrary lib, UiConfigBundle config) { + this.cacheLib = new LocalLibrary(cacheDir, // + config.getString(UiConfig.GUI_NON_IMAGES_DOCUMENT_TYPE), + config.getString(UiConfig.GUI_IMAGES_DOCUMENT_TYPE), true); this.lib = lib; } @@@ -52,32 -56,37 +56,37 @@@ } @Override - protected List getMetas(Progress pg) throws IOException { + protected synchronized List getMetas(Progress pg) throws IOException { + // We make sure that cached metas have precedence + if (pg == null) { pg = new Progress(); } - if (metas == null) { - metas = lib.getMetas(pg); + if (metasMixed == null) { + if (metasReal == null) { + metasReal = lib.getMetas(pg); + } + + metasMixed = new ArrayList(); + TreeSet cachedLuids = new TreeSet(); + for (MetaData cachedMeta : cacheLib.getMetas(null)) { + metasMixed.add(cachedMeta); + cachedLuids.add(cachedMeta.getLuid()); + } + for (MetaData realMeta : metasReal) { + if (!cachedLuids.contains(realMeta.getLuid())) { + metasMixed.add(realMeta); + } + } } pg.done(); - return metas; + return new ArrayList(metasMixed); } @Override - public synchronized MetaData getInfo(String luid) throws IOException { - MetaData info = cacheLib.getInfo(luid); - if (info == null) { - info = lib.getInfo(luid); - } - - return info; - } - - @Override - public synchronized Story getStory(String luid, MetaData meta, Progress pg) - throws IOException { + public synchronized Story getStory(String luid, MetaData meta, Progress pg) throws IOException { if (pg == null) { pg = new Progress(); } @@@ -92,10 -101,10 +101,10 @@@ if (!isCached(luid)) { try { cacheLib.imprt(lib, luid, pgImport); - updateInfo(cacheLib.getInfo(luid)); + updateMetaCache(metasMixed, cacheLib.getInfo(luid)); pgImport.done(); } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } pgImport.done(); @@@ -110,8 -119,7 +119,7 @@@ } @Override - public synchronized File getFile(final String luid, Progress pg) - throws IOException { + public synchronized File getFile(final String luid, Progress pg) throws IOException { if (pg == null) { pg = new Progress(); } @@@ -213,8 -221,29 +221,29 @@@ cacheLib.setAuthorCover(author, getCover(luid)); } + /** + * Invalidate the {@link Story} cache (when the content has changed, but we + * already have it) with the new given meta. + *

+ * Make sure to always use {@link MetaData} from the cached library + * in priority, here. + * + * @param meta + * the {@link Story} to clear from the cache + * + * @throws IOException + * in case of IOException + */ @Override + @Deprecated protected void updateInfo(MetaData meta) throws IOException { + throw new IOException( + "This method is not supported in a CacheLibrary, please use updateMetaCache"); + } + + // relplace the meta in Metas by Meta, add it if needed + // return TRUE = added + private boolean updateMetaCache(List metas, MetaData meta) { if (meta != null && metas != null) { boolean changed = false; for (int i = 0; i < metas.size(); i++) { @@@ -226,32 -255,40 +255,40 @@@ if (!changed) { metas.add(meta); + return true; } } - - cacheLib.updateInfo(meta); - lib.updateInfo(meta); + + return false; } @Override protected void invalidateInfo(String luid) { if (luid == null) { - metas = null; - } else if (metas != null) { + metasReal = null; + metasMixed = null; + } else { + invalidateInfo(metasReal, luid); + invalidateInfo(metasMixed, luid); + } + + cacheLib.invalidateInfo(luid); + lib.invalidateInfo(luid); + } + + // luid cannot be null + private void invalidateInfo(List metas, String luid) { + if (metas != null) { for (int i = 0; i < metas.size(); i++) { if (metas.get(i).getLuid().equals(luid)) { metas.remove(i--); } } } - - cacheLib.invalidateInfo(luid); - lib.invalidateInfo(luid); } @Override - public synchronized Story save(Story story, String luid, Progress pg) - throws IOException { + public synchronized Story save(Story story, String luid, Progress pg) throws IOException { Progress pgLib = new Progress(); Progress pgCacheLib = new Progress(); @@@ -264,9 -301,10 +301,10 @@@ pg.addProgress(pgCacheLib, 1); story = lib.save(story, luid, pgLib); + updateMetaCache(metasReal, story.getMeta()); + story = cacheLib.save(story, story.getMeta().getLuid(), pgCacheLib); - - updateInfo(story.getMeta()); + updateMetaCache(metasMixed, story.getMeta()); return story; } @@@ -282,8 -320,8 +320,8 @@@ } @Override - protected synchronized void changeSTA(String luid, String newSource, - String newTitle, String newAuthor, Progress pg) throws IOException { + protected synchronized void changeSTA(String luid, String newSource, String newTitle, String newAuthor, Progress pg) + throws IOException { if (pg == null) { pg = new Progress(); } @@@ -312,18 -350,15 +350,15 @@@ meta.setAuthor(newAuthor); pg.done(); - invalidateInfo(luid); + if (isCached(luid)) { + updateMetaCache(metasMixed, meta); + updateMetaCache(metasReal, lib.getInfo(luid)); + } else { + updateMetaCache(metasReal, meta); + } } - /** - * Check if the {@link Story} denoted by this Library UID is present in the - * cache. - * - * @param luid - * the Library UID - * - * @return TRUE if it is - */ + @Override public boolean isCached(String luid) { try { return cacheLib.getInfo(luid) != null; @@@ -332,18 -367,7 +367,7 @@@ } } - /** - * Clear the {@link Story} from the cache. - *

- * The next time we try to retrieve the {@link Story}, it may be required to - * cache it again. - * - * @param luid - * the story to clear - * - * @throws IOException - * in case of I/O error - */ + @Override public void clearFromCache(String luid) throws IOException { if (isCached(luid)) { cacheLib.delete(luid); @@@ -351,7 -375,7 +375,7 @@@ } @Override - public MetaData imprt(URL url, Progress pg) throws IOException { + public synchronized MetaData imprt(URL url, Progress pg) throws IOException { if (pg == null) { pg = new Progress(); } @@@ -363,10 -387,10 +387,10 @@@ pg.addProgress(pgCache, 3); MetaData meta = lib.imprt(url, pgImprt); - updateInfo(meta); - + updateMetaCache(metasReal, meta); + metasMixed = null; clearFromCache(meta.getLuid()); - + pg.done(); return meta; } diff --combined library/LocalLibrary.java index ffcd8af,80d216b..80d216b --- a/library/LocalLibrary.java +++ b/library/LocalLibrary.java @@@ -13,6 -13,8 +13,8 @@@ import java.util.Map import be.nikiroo.fanfix.Instance; import be.nikiroo.fanfix.bundles.Config; + import be.nikiroo.fanfix.bundles.ConfigBundle; + import be.nikiroo.fanfix.bundles.UiConfigBundle; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.output.BasicOutput; @@@ -42,13 -44,14 +44,14 @@@ public class LocalLibrary extends Basic /** * Create a new {@link LocalLibrary} with the given back-end directory. * - * @param baseDir - * the directory where to find the {@link Story} objects + * @param baseDir the directory where to find the {@link Story} objects + * @param config the configuration used to know which kind of default + * {@link OutputType} to use for images and non-images stories */ - public LocalLibrary(File baseDir) { - this(baseDir, Instance.getConfig().getString( - Config.FILE_FORMAT_NON_IMAGES_DOCUMENT_TYPE), Instance.getConfig() - .getString(Config.FILE_FORMAT_IMAGES_DOCUMENT_TYPE), false); + public LocalLibrary(File baseDir, ConfigBundle config) { + this(baseDir, // + config.getString(Config.FILE_FORMAT_NON_IMAGES_DOCUMENT_TYPE), + config.getString(Config.FILE_FORMAT_IMAGES_DOCUMENT_TYPE), false); } /** @@@ -95,14 -98,13 +98,13 @@@ } @Override - protected List getMetas(Progress pg) { + protected synchronized List getMetas(Progress pg) { return new ArrayList(getStories(pg).keySet()); } @Override public File getFile(String luid, Progress pg) throws IOException { - Instance.getTraceHandler().trace( - this.getClass().getSimpleName() + ": get file for " + luid); + Instance.getInstance().getTraceHandler().trace(this.getClass().getSimpleName() + ": get file for " + luid); File file = null; String mess = "no file found for "; @@@ -114,9 -116,8 +116,8 @@@ file = files[1]; } - Instance.getTraceHandler().trace( - this.getClass().getSimpleName() + ": " + mess + luid + " (" - + meta.getTitle() + ")"); + Instance.getInstance().getTraceHandler() + .trace(this.getClass().getSimpleName() + ": " + mess + luid + " (" + meta.getTitle() + ")"); return file; } @@@ -137,7 -138,7 +138,7 @@@ meta = InfoReader.readMeta(infoFile, true); return meta.getCover(); } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } } } @@@ -205,7 -206,7 +206,7 @@@ InfoCover.writeInfo(newDir, name, meta); relatedFile.getParentFile().delete(); } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } } else { relatedFile.renameTo(new File(newDir, relatedFile.getName())); @@@ -213,7 -214,7 +214,7 @@@ } } - invalidateInfo(); + updateInfo(meta); } @Override @@@ -242,10 -243,8 +243,8 @@@ } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { - Instance.getTraceHandler().error( - new IOException( - "Cannot load the existing custom source cover: " - + cover, e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Cannot load the existing custom source cover: " + cover, e)); } } } @@@ -277,10 -276,8 +276,8 @@@ } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { - Instance.getTraceHandler().error( - new IOException( - "Cannot load the existing custom author cover: " - + cover, e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Cannot load the existing custom author cover: " + cover, e)); } } @@@ -310,12 -307,12 +307,12 @@@ dir.mkdirs(); File cover = new File(dir, ".cover"); try { - Instance.getCache().saveAsImage(coverImage, cover, true); + Instance.getInstance().getCache().saveAsImage(coverImage, cover, true); if (sourceCovers != null) { sourceCovers.put(source, coverImage); } } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } } @@@ -331,12 -328,12 +328,12 @@@ File cover = getAuthorCoverFile(author); cover.getParentFile().mkdirs(); try { - Instance.getCache().saveAsImage(coverImage, cover, true); + Instance.getInstance().getCache().saveAsImage(coverImage, cover, true); if (authorCovers != null) { authorCovers.put(author, coverImage); } } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } } @@@ -516,7 -513,7 +513,7 @@@ private File getAuthorCoverFile(String author) { File aDir = new File(baseDir, "_AUTHORS"); String hash = StringUtils.getMd5Hash(author); - String ext = Instance.getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER); + String ext = Instance.getInstance().getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER); return new File(aDir, hash + "." + ext.toLowerCase()); } @@@ -562,8 -559,7 +559,7 @@@ } String coverExt = "." - + Instance.getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER) - .toLowerCase(); + + Instance.getInstance().getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER).toLowerCase(); File coverFile = new File(path + coverExt); if (!coverFile.exists()) { coverFile = new File(path.substring(0, @@@ -675,9 -671,8 +671,8 @@@ } catch (IOException e) { // We should not have not-supported files in the // library - Instance.getTraceHandler().error( - new IOException("Cannot load file from library: " - + infoFileOrSubdir, e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Cannot load file from library: " + infoFileOrSubdir, e)); } } diff --combined library/MetaResultList.java index 0000000,886defe..886defe mode 000000,100644..100644 --- a/library/MetaResultList.java +++ b/library/MetaResultList.java @@@ -1,0 -1,168 +1,168 @@@ + package be.nikiroo.fanfix.library; + + import java.util.ArrayList; + import java.util.Arrays; + import java.util.Collections; + import java.util.List; + + import be.nikiroo.fanfix.data.MetaData; + + public class MetaResultList { + private List metas; + + // Lazy lists: + // TODO: sync-protect them? + private List sources; + private List authors; + private List tags; + + // can be null (will consider it empty) + public MetaResultList(List metas) { + if (metas == null) { + metas = new ArrayList(); + } + + Collections.sort(metas); + this.metas = metas; + } + + // not NULL + // sorted + public List getMetas() { + return metas; + } + + public List getSources() { + if (sources == null) { + sources = new ArrayList(); + for (MetaData meta : metas) { + if (!sources.contains(meta.getSource())) + sources.add(meta.getSource()); + } + } + + return sources; + } + + // A -> (A), A/ -> (A, A/*) if we can find something for "*" + public List getSources(String source) { + List linked = new ArrayList(); + if (source != null && !source.isEmpty()) { + if (!source.endsWith("/")) { + linked.add(source); + } else { + linked.add(source.substring(0, source.length() - 1)); + for (String src : getSources()) { + if (src.startsWith(source)) { + linked.add(src); + } + } + } + } + + return linked; + } + + public List getAuthors() { + if (authors == null) { + authors = new ArrayList(); + for (MetaData meta : metas) { + if (!authors.contains(meta.getAuthor())) + authors.add(meta.getAuthor()); + } + } + + return authors; + } + + public List getTags() { + if (tags == null) { + tags = new ArrayList(); + for (MetaData meta : metas) { + for (String tag : meta.getTags()) { + if (!tags.contains(tag)) + tags.add(tag); + } + } + } + + return authors; + } + + // helper + public List filter(String source, String author, String tag) { + List sources = source == null ? null : Arrays.asList(source); + List authors = author == null ? null : Arrays.asList(author); + List tags = tag == null ? null : Arrays.asList(tag); + + return filter(sources, authors, tags); + } + + // null or empty -> no check, rest = must be included + // source: a source ending in "/" means "this or any source starting with this", + // i;e., to enable source hierarchy + // + sorted + public List filter(List sources, List authors, List tags) { + if (sources != null && sources.isEmpty()) + sources = null; + if (authors != null && authors.isEmpty()) + authors = null; + if (tags != null && tags.isEmpty()) + tags = null; + + // Quick check + if (sources == null && authors == null && tags == null) { + return metas; + } + + // allow "sources/" hierarchy + if (sources != null) { + List folders = new ArrayList(); + List leaves = new ArrayList(); + for (String source : sources) { + if (source.endsWith("/")) { + if (!folders.contains(source)) + folders.add(source); + } else { + if (!leaves.contains(source)) + leaves.add(source); + } + } + + sources = leaves; + for (String folder : folders) { + for (String otherLeaf : getSources(folder)) { + if (!sources.contains(otherLeaf)) { + sources.add(otherLeaf); + } + } + } + } + + List result = new ArrayList(); + for (MetaData meta : metas) { + if (sources != null && !sources.contains(meta.getSource())) { + continue; + } + if (authors != null && !authors.contains(meta.getAuthor())) { + continue; + } + + if (tags != null) { + boolean keep = false; + for (String thisTag : meta.getTags()) { + if (tags.contains(thisTag)) + keep = true; + } + + if (!keep) + continue; + } + + result.add(meta); + } + + Collections.sort(result); + return result; + } + } diff --combined library/RemoteLibrary.java index ce4305a,65be7b1..65be7b1 --- a/library/RemoteLibrary.java +++ b/library/RemoteLibrary.java @@@ -124,9 -124,9 +124,9 @@@ public class RemoteLibrary extends Basi @Override public Status getStatus() { - Instance.getTraceHandler().trace("Getting remote lib status..."); + Instance.getInstance().getTraceHandler().trace("Getting remote lib status..."); Status status = getStatusDo(); - Instance.getTraceHandler().trace("Remote lib status: " + status); + Instance.getInstance().getTraceHandler().trace("Remote lib status: " + status); return status; } @@@ -443,6 -443,7 +443,7 @@@ public void action(ConnectActionClientObject action) throws Exception { action.send(new Object[] { subkey, "EXIT" }); + Thread.sleep(100); } }); } @@@ -458,7 -459,7 +459,7 @@@ } @Override - protected List getMetas(Progress pg) throws IOException { + protected synchronized List getMetas(Progress pg) throws IOException { return getMetasList("*", pg); } @@@ -559,7 -560,7 +560,7 @@@ @Override protected void onError(Exception e) { if (!(e instanceof IOException)) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); return; } diff --combined library/RemoteLibraryServer.java index dc9688c,4f89a1f..4f89a1f --- a/library/RemoteLibraryServer.java +++ b/library/RemoteLibraryServer.java @@@ -80,7 -80,7 +80,7 @@@ public class RemoteLibraryServer extend */ public RemoteLibraryServer(String key, int port) throws IOException { super("Fanfix remote library", port, key); - setTraceHandler(Instance.getTraceHandler()); + setTraceHandler(Instance.getInstance().getTraceHandler()); } @Override @@@ -110,8 -110,7 +110,7 @@@ } } - List whitelist = Instance.getConfig().getList( - Config.SERVER_WHITELIST); + List whitelist = Instance.getInstance().getConfig().getList(Config.SERVER_WHITELIST); if (whitelist == null) { whitelist = new ArrayList(); } @@@ -120,10 -119,9 +119,9 @@@ wl = false; } - rw = Instance.getConfig().getBoolean(Config.SERVER_RW, rw); + rw = Instance.getInstance().getConfig().getBoolean(Config.SERVER_RW, rw); if (!subkey.isEmpty()) { - List allowed = Instance.getConfig().getList( - Config.SERVER_ALLOWED_SUBKEYS); + List allowed = Instance.getInstance().getConfig().getList(Config.SERVER_ALLOWED_SUBKEYS); if (allowed.contains(subkey)) { if ((subkey + "|").contains("|rw|")) { rw = true; @@@ -203,7 -201,7 +201,7 @@@ if ("*".equals(args[0])) { Progress pg = createPgForwarder(action); - for (MetaData meta : Instance.getLibrary().getMetas(pg)) { + for (MetaData meta : Instance.getInstance().getLibrary().getMetas(pg)) { MetaData light; if (meta.getCover() == null) { light = meta; @@@ -217,7 -215,7 +215,7 @@@ forcePgDoneSent(pg); } else { - MetaData meta = Instance.getLibrary().getInfo((String) args[0]); + MetaData meta = Instance.getInstance().getLibrary().getInfo((String) args[0]); MetaData light; if (meta.getCover() == null) { light = meta; @@@ -240,7 -238,7 +238,7 @@@ return metas.toArray(new MetaData[0]); } else if ("GET_STORY".equals(command)) { - MetaData meta = Instance.getLibrary().getInfo((String) args[0]); + MetaData meta = Instance.getInstance().getLibrary().getInfo((String) args[0]); if (meta == null) { return null; } @@@ -257,8 -255,7 +255,7 @@@ action.send(meta); action.rec(); - Story story = Instance.getLibrary() - .getStory((String) args[0], null); + Story story = Instance.getInstance().getLibrary().getStory((String) args[0], null); for (Object obj : breakStory(story)) { action.send(obj); action.rec(); @@@ -280,7 -277,7 +277,7 @@@ } Story story = rebuildStory(list); - Instance.getLibrary().save(story, (String) args[0], null); + Instance.getInstance().getLibrary().save(story, (String) args[0], null); return story.getMeta().getLuid(); } else if ("IMPORT".equals(command)) { if (!rw) { @@@ -289,8 -286,7 +286,7 @@@ } Progress pg = createPgForwarder(action); - MetaData meta = Instance.getLibrary().imprt( - new URL((String) args[0]), pg); + MetaData meta = Instance.getInstance().getLibrary().imprt(new URL((String) args[0]), pg); forcePgDoneSent(pg); return meta.getLuid(); } else if ("DELETE_STORY".equals(command)) { @@@ -299,16 -295,14 +295,14 @@@ + args[0], false); } - Instance.getLibrary().delete((String) args[0]); + Instance.getInstance().getLibrary().delete((String) args[0]); } else if ("GET_COVER".equals(command)) { - return Instance.getLibrary().getCover((String) args[0]); + return Instance.getInstance().getLibrary().getCover((String) args[0]); } else if ("GET_CUSTOM_COVER".equals(command)) { if ("SOURCE".equals(args[0])) { - return Instance.getLibrary().getCustomSourceCover( - (String) args[1]); + return Instance.getInstance().getLibrary().getCustomSourceCover((String) args[1]); } else if ("AUTHOR".equals(args[0])) { - return Instance.getLibrary().getCustomAuthorCover( - (String) args[1]); + return Instance.getInstance().getLibrary().getCustomAuthorCover((String) args[1]); } else { return null; } @@@ -319,21 -313,18 +313,18 @@@ } if ("SOURCE".equals(args[0])) { - Instance.getLibrary().setSourceCover((String) args[1], - (String) args[2]); + Instance.getInstance().getLibrary().setSourceCover((String) args[1], (String) args[2]); } else if ("AUTHOR".equals(args[0])) { - Instance.getLibrary().setAuthorCover((String) args[1], - (String) args[2]); + Instance.getInstance().getLibrary().setAuthorCover((String) args[1], (String) args[2]); } } else if ("CHANGE_STA".equals(command)) { if (!rw) { - throw new RemoteLibraryException("Read-Only remote library: " - + args[0] + ", " + args[1], false); + throw new RemoteLibraryException("Read-Only remote library: " + args[0] + ", " + args[1], false); } Progress pg = createPgForwarder(action); - Instance.getLibrary().changeSTA((String) args[0], (String) args[1], - (String) args[2], (String) args[3], pg); + Instance.getInstance().getLibrary().changeSTA((String) args[0], (String) args[1], (String) args[2], + (String) args[3], pg); forcePgDoneSent(pg); } else if ("EXIT".equals(command)) { if (!rw) { @@@ -341,7 -332,7 +332,7 @@@ "Read-Only remote library: EXIT", false); } - stop(0, false); + stop(10000, false); } return null; diff --combined output/BasicOutput.java index 15d8cc1,41634fa..41634fa --- a/output/BasicOutput.java +++ b/output/BasicOutput.java @@@ -63,10 -63,10 +63,10 @@@ public abstract class BasicOutput StringId id = longDesc ? StringId.OUTPUT_DESC : StringId.OUTPUT_DESC_SHORT; - String desc = Instance.getTrans().getStringX(id, this.name()); + String desc = Instance.getInstance().getTrans().getStringX(id, this.name()); if (desc == null) { - desc = Instance.getTrans().getString(id, this.toString()); + desc = Instance.getInstance().getTrans().getString(id, this.toString()); } if (desc == null || desc.isEmpty()) { diff --combined output/Cbz.java index 3d90082,ee671e7..ee671e7 --- a/output/Cbz.java +++ b/output/Cbz.java @@@ -23,7 -23,7 +23,7 @@@ class Cbz extends BasicOutput File target = new File(targetDir, targetName); - dir = Instance.getTempFiles().createTempDir("fanfic-reader-cbz-dir"); + dir = Instance.getInstance().getTempFiles().createTempDir("fanfic-reader-cbz-dir"); try { // will also save the images! (except the cover -> false) BasicOutput diff --combined output/Epub.java index b7401d3,fc2dc8c..fc2dc8c --- a/output/Epub.java +++ b/output/Epub.java @@@ -36,7 -36,7 +36,7 @@@ class Epub extends BasicOutput String targetNameOrig = targetName; targetName += getDefaultExtension(false); - tmpDir = Instance.getTempFiles().createTempDir("fanfic-reader-epub"); + tmpDir = Instance.getInstance().getTempFiles().createTempDir("fanfic-reader-epub"); tmpDir.delete(); if (!tmpDir.mkdir()) { @@@ -137,10 -137,9 +137,9 @@@ if (story.getMeta() != null && story.getMeta().getCover() != null) { File file = new File(images, "cover"); try { - Instance.getCache().saveAsImage(story.getMeta().getCover(), - file, true); + Instance.getInstance().getCache().saveAsImage(story.getMeta().getCover(), file, true); } catch (Exception e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } } @@@ -246,8 -245,7 +245,7 @@@ break; case IMAGE: File file = new File(images, getCurrentImageBestName(false)); - Instance.getCache().saveAsImage(para.getContentImage(), file, - nextParaIsCover); + Instance.getInstance().getCache().saveAsImage(para.getContentImage(), file, nextParaIsCover); writer.write(" page image"); break; @@@ -358,11 -356,10 +356,10 @@@ private void generateNcx(Chapter chap, StringBuilder builder, int navPoint) { String name; if (chap.getName() != null && !chap.getName().isEmpty()) { - name = Instance.getTrans().getString(StringId.CHAPTER_NAMED, - chap.getNumber(), chap.getName()); + name = Instance.getInstance().getTrans().getString(StringId.CHAPTER_NAMED, chap.getNumber(), + chap.getName()); } else { - name = Instance.getTrans().getString(StringId.CHAPTER_UNNAMED, - chap.getNumber()); + name = Instance.getInstance().getTrans().getString(StringId.CHAPTER_UNNAMED, chap.getNumber()); } String nnn = String.format("%03d", (navPoint - 2)); @@@ -440,7 -437,7 +437,7 @@@ builder.append("\n "); if (story.getMeta() != null && story.getMeta().getCover() != null) { - String format = Instance.getConfig() + String format = Instance.getInstance().getConfig() .getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER) .toLowerCase(); builder.append("\n "); diff --combined output/Html.java index ca802a5,da79466..da79466 --- a/output/Html.java +++ b/output/Html.java @@@ -50,8 -50,7 +50,7 @@@ class Html extends BasicOutput .process(story, dir, targetNameOrig); if (story.getMeta().getCover() != null) { - Instance.getCache().saveAsImage(story.getMeta().getCover(), - new File(dir, "cover"), true); + Instance.getInstance().getCache().saveAsImage(story.getMeta().getCover(), new File(dir, "cover"), true); } return target; @@@ -91,7 -90,7 +90,7 @@@ author = meta.getAuthor(); } - String format = Instance.getConfig() + String format = Instance.getInstance().getConfig() .getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER).toLowerCase(); InputStream inStyle = getClass().getResourceAsStream("html.style.css"); diff --combined output/InfoCover.java index 6bfa4dd,d8ca49a..d8ca49a --- a/output/InfoCover.java +++ b/output/InfoCover.java @@@ -46,7 -46,7 +46,7 @@@ public class InfoCover meta.isImageDocument() ? "true" : "false"); writeMeta(infoWriter, "TYPE", meta.getType()); if (meta.getCover() != null) { - String format = Instance.getConfig() + String format = Instance.getInstance().getConfig() .getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER).toLowerCase(); writeMeta(infoWriter, "COVER", targetName + "." + format); } else { @@@ -71,12 -71,10 +71,10 @@@ MetaData meta) { if (meta != null && meta.getCover() != null) { try { - Instance.getCache().saveAsImage(meta.getCover(), - new File(targetDir, targetName), true); + Instance.getInstance().getCache().saveAsImage(meta.getCover(), new File(targetDir, targetName), true); } catch (IOException e) { // Allow to continue without cover - Instance.getTraceHandler().error( - new IOException("Failed to save the cover image", e)); + Instance.getInstance().getTraceHandler().error(new IOException("Failed to save the cover image", e)); } } } diff --combined output/InfoText.java index cce715d,935da87..935da87 --- a/output/InfoText.java +++ b/output/InfoText.java @@@ -9,14 -9,10 +9,10 @@@ import be.nikiroo.fanfix.data.Paragraph class InfoText extends Text { // quote chars - private char openQuote = Instance.getTrans().getCharacter( - StringId.OPEN_SINGLE_QUOTE); - private char closeQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_SINGLE_QUOTE); - private char openDoubleQuote = Instance.getTrans().getCharacter( - StringId.OPEN_DOUBLE_QUOTE); - private char closeDoubleQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_DOUBLE_QUOTE); + private char openQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_SINGLE_QUOTE); + private char closeQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_SINGLE_QUOTE); + private char openDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_DOUBLE_QUOTE); + private char closeDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_DOUBLE_QUOTE); @Override public String getDefaultExtension(boolean readerTarget) { @@@ -28,11 -24,10 +24,10 @@@ writer.write("\n"); if (chap.getName() != null && !chap.getName().isEmpty()) { - writer.write(Instance.getTrans().getString(StringId.CHAPTER_NAMED, - chap.getNumber(), chap.getName())); + writer.write(Instance.getInstance().getTrans().getString(StringId.CHAPTER_NAMED, chap.getNumber(), + chap.getName())); } else { - writer.write(Instance.getTrans().getString( - StringId.CHAPTER_UNNAMED, chap.getNumber())); + writer.write(Instance.getInstance().getTrans().getString(StringId.CHAPTER_UNNAMED, chap.getNumber())); } writer.write("\n\n"); diff --combined output/LaTeX.java index 321556f,a15e67c..a15e67c --- a/output/LaTeX.java +++ b/output/LaTeX.java @@@ -19,14 -19,10 +19,10 @@@ class LaTeX extends BasicOutput private boolean lastWasQuote = false; // quote chars - private char openQuote = Instance.getTrans().getCharacter( - StringId.OPEN_SINGLE_QUOTE); - private char closeQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_SINGLE_QUOTE); - private char openDoubleQuote = Instance.getTrans().getCharacter( - StringId.OPEN_DOUBLE_QUOTE); - private char closeDoubleQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_DOUBLE_QUOTE); + private char openQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_SINGLE_QUOTE); + private char closeQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_SINGLE_QUOTE); + private char openDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_DOUBLE_QUOTE); + private char closeDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_DOUBLE_QUOTE); @Override public File process(Story story, File targetDir, String targetName) @@@ -66,10 -62,9 +62,9 @@@ author = "\\author{" + latexEncode(meta.getAuthor()) + "}"; lang = meta.getLang().toLowerCase(); if (lang != null && !lang.isEmpty()) { - lang = Instance.getConfig().getStringX(Config.CONF_LATEX_LANG, lang); + lang = Instance.getInstance().getConfig().getStringX(Config.CONF_LATEX_LANG, lang); if (lang == null) { - System.err.println(Instance.getTrans().getString( - StringId.LATEX_LANG_UNKNOWN, lang)); + System.err.println(Instance.getInstance().getTrans().getString(StringId.LATEX_LANG_UNKNOWN, lang)); } } } diff --combined output/Text.java index 4a45e54,f0516dc..f0516dc --- a/output/Text.java +++ b/output/Text.java @@@ -62,8 -62,7 +62,7 @@@ class Text extends BasicOutput writer.write(title); writer.write("\n"); if (author != null && !author.isEmpty()) { - writer.write(Instance.getTrans().getString(StringId.BY) + " " - + author); + writer.write(Instance.getInstance().getTrans().getString(StringId.BY) + " " + author); } if (date != null && !date.isEmpty()) { writer.write(" ("); @@@ -82,11 -81,9 +81,9 @@@ protected void writeChapterHeader(Chapter chap) throws IOException { String txt; if (chap.getName() != null && !chap.getName().isEmpty()) { - txt = Instance.getTrans().getString(StringId.CHAPTER_NAMED, - chap.getNumber(), chap.getName()); + txt = Instance.getInstance().getTrans().getString(StringId.CHAPTER_NAMED, chap.getNumber(), chap.getName()); } else { - txt = Instance.getTrans().getString(StringId.CHAPTER_UNNAMED, - chap.getNumber()); + txt = Instance.getInstance().getTrans().getString(StringId.CHAPTER_UNNAMED, chap.getNumber()); } writer.write("\n" + txt + "\n"); @@@ -106,11 -103,9 +103,9 @@@ if (para.getType() == ParagraphType.IMAGE) { File file = new File(targetDir, getCurrentImageBestName(true)); try { - Instance.getCache().saveAsImage(para.getContentImage(), file, - nextParaIsCover); + Instance.getInstance().getCache().saveAsImage(para.getContentImage(), file, nextParaIsCover); } catch (IOException e) { - Instance.getTraceHandler().error( - new IOException("Cannot save an image", e)); + Instance.getInstance().getTraceHandler().error(new IOException("Cannot save an image", e)); } } diff --combined reader/BasicReader.java index 61769c0,7f79da3..7f79da3 --- a/reader/BasicReader.java +++ b/reader/BasicReader.java @@@ -30,7 -30,7 +30,7 @@@ import be.nikiroo.utils.serial.SerialUt * @author niki */ public abstract class BasicReader implements Reader { - private static BasicLibrary defaultLibrary = Instance.getLibrary(); + private static BasicLibrary defaultLibrary = Instance.getInstance().getLibrary(); private static ReaderType defaultType = ReaderType.GUI; private BasicLibrary lib; @@@ -42,7 -42,7 +42,7 @@@ * Take the default reader type configuration from the config file. */ static { - String typeString = Instance.getConfig().getString(Config.READER_TYPE); + String typeString = Instance.getInstance().getConfig().getString(Config.READER_TYPE); if (typeString != null && !typeString.isEmpty()) { try { ReaderType type = ReaderType.valueOf(typeString.toUpperCase()); @@@ -137,9 -137,8 +137,8 @@@ .getTypeName()); } } catch (Exception e) { - Instance.getTraceHandler().error( - new Exception("Cannot create a reader of type: " - + defaultType + " (Not compiled in?)", e)); + Instance.getInstance().getTraceHandler() + .error(new Exception("Cannot create a reader of type: " + defaultType + " (Not compiled in?)", e)); } return null; @@@ -292,11 -291,9 +291,9 @@@ throws IOException { String program = null; if (meta.isImageDocument()) { - program = Instance.getUiConfig().getString( - UiConfig.IMAGES_DOCUMENT_READER); + program = Instance.getInstance().getUiConfig().getString(UiConfig.IMAGES_DOCUMENT_READER); } else { - program = Instance.getUiConfig().getString( - UiConfig.NON_IMAGES_DOCUMENT_READER); + program = Instance.getInstance().getUiConfig().getString(UiConfig.NON_IMAGES_DOCUMENT_READER); } if (program != null && program.trim().isEmpty()) { @@@ -330,10 -327,8 +327,8 @@@ for (String starter : new String[] { "xdg-open", "open", "see", "start", "run" }) { try { - Instance.getTraceHandler().trace( - "starting external program"); - proc = Runtime.getRuntime().exec( - new String[] { starter, target.getAbsolutePath() }); + Instance.getInstance().getTraceHandler().trace("starting external program"); + proc = Runtime.getRuntime().exec(new String[] { starter, target.getAbsolutePath() }); ok = true; break; } catch (IOException e) { @@@ -343,7 -338,7 +338,7 @@@ throw new IOException("Cannot find a program to start the file"); } } else { - Instance.getTraceHandler().trace("starting external program"); + Instance.getInstance().getTraceHandler().trace("starting external program"); proc = Runtime.getRuntime().exec( new String[] { program, target.getAbsolutePath() }); } diff --combined searchable/BasicSearchable.java index d38505e,b943abd..b943abd --- a/searchable/BasicSearchable.java +++ b/searchable/BasicSearchable.java @@@ -222,8 -222,7 +222,7 @@@ public abstract class BasicSearchable * in case of I/O error */ protected Document load(URL url, boolean stable) throws IOException { - return DataUtil.load(Instance.getCache().open(url, support, stable), - "UTF-8", url.toString()); + return DataUtil.load(Instance.getInstance().getCache().open(url, support, stable), "UTF-8", url.toString()); } /** @@@ -247,7 -246,7 +246,7 @@@ case FANFICTION: support = new Fanfiction(type); break; - case MANGAFOX: + case MANGAHUB: // TODO break; case E621: diff --combined searchable/Fanfiction.java index c2dfd5d,e2fba1f..e2fba1f --- a/searchable/Fanfiction.java +++ b/searchable/Fanfiction.java @@@ -285,8 -285,7 +285,7 @@@ class Fanfiction extends BasicSearchabl String coverUrl = cover.absUrl("src"); try { - InputStream in = Instance.getCache().open( - new URL(coverUrl), getSupport(), true); + InputStream in = Instance.getInstance().getCache().open(new URL(coverUrl), getSupport(), true); try { meta.setCover(new Image(in)); } finally { @@@ -294,10 -293,8 +293,8 @@@ } } catch (Exception e) { // Should not happen on Fanfiction.net - Instance.getTraceHandler().error( - new Exception( - "Cannot download cover for Fanfiction story in search mode: " - + meta.getTitle(), e)); + Instance.getInstance().getTraceHandler().error(new Exception( + "Cannot download cover for Fanfiction story in search mode: " + meta.getTitle(), e)); } } } @@@ -337,8 -334,7 +334,7 @@@ } meta.setResume(getSupport().makeChapter(new URL(sourceUrl), 0, - Instance.getTrans().getString(StringId.DESCRIPTION), - resume)); + Instance.getInstance().getTrans().getString(StringId.DESCRIPTION), resume)); } // How are the tags ordered? diff --combined searchable/MangaLel.java index 3e2924f,5ba21a0..5ba21a0 --- a/searchable/MangaLel.java +++ b/searchable/MangaLel.java @@@ -89,9 -89,8 +89,8 @@@ class MangaLel extends BasicSearchable private List getResults(String sourceUrl) throws IOException { List metas = new ArrayList(); - Document doc = DataUtil.load( - Instance.getCache().open(new URL(sourceUrl), getSupport(), - false), "UTF-8", sourceUrl); + Document doc = DataUtil.load(Instance.getInstance().getCache().open(new URL(sourceUrl), getSupport(), false), + "UTF-8", sourceUrl); for (Element result : doc.getElementsByClass("rechercheAffichage")) { Element a = result.getElementsByTag("a").first(); @@@ -123,13 -122,8 +122,8 @@@ meta.setAuthor(getVal(tab, 1)); meta.setTags(Arrays.asList(getVal(tab, 2).split(" "))); - meta.setResume(getSupport() - .makeChapter( - new URL(sourceUrl), - 0, - Instance.getTrans().getString( - StringId.DESCRIPTION), - getVal(tab, 5))); + meta.setResume(getSupport().makeChapter(new URL(sourceUrl), 0, + Instance.getInstance().getTrans().getString(StringId.DESCRIPTION), getVal(tab, 5))); } Element img = result.getElementsByTag("img").first(); @@@ -143,8 -137,8 +137,8 @@@ String coverUrl = img.absUrl("src"); try { - InputStream in = Instance.getCache().open( - new URL(coverUrl), getSupport(), true); + InputStream in = Instance.getInstance().getCache().open(new URL(coverUrl), getSupport(), + true); try { meta.setCover(new Image(in)); } finally { @@@ -152,15 -146,13 +146,13 @@@ } } catch (Exception e) { // Happen often on MangaLEL... - Instance.getTraceHandler().trace( - "Cannot download cover for MangaLEL story in search mode: " - + meta.getTitle()); + Instance.getInstance().getTraceHandler().trace( + "Cannot download cover for MangaLEL story in search mode: " + meta.getTitle()); } } catch (Exception e) { // no project id... cannot use the story :( - Instance.getTraceHandler().error( - "Cannot find ProjectId for MangaLEL story in search mode: " - + meta.getTitle()); + Instance.getInstance().getTraceHandler() + .error("Cannot find ProjectId for MangaLEL story in search mode: " + meta.getTitle()); } } diff --combined supported/BasicSupport.java index d3c0ebb,bc91e8b..bc91e8b --- a/supported/BasicSupport.java +++ b/supported/BasicSupport.java @@@ -224,8 -224,7 +224,7 @@@ public abstract class BasicSupport */ protected Document loadDocument(URL source) throws IOException { String url = getCanonicalUrl(source).toString(); - return DataUtil.load(Instance.getCache().open(source, this, false), - "UTF-8", url.toString()); + return DataUtil.load(Instance.getInstance().getCache().open(source, this, false), "UTF-8", url.toString()); } /** @@@ -284,12 -283,9 +283,9 @@@ pg.setProgress(60); if (getDesc) { - String descChapterName = Instance.getTrans().getString( - StringId.DESCRIPTION); - story.getMeta().setResume( - bsPara.makeChapter(this, source, 0, - descChapterName, // - getDesc(), isHtml(), null)); + String descChapterName = Instance.getInstance().getTrans().getString(StringId.DESCRIPTION); + story.getMeta().setResume(bsPara.makeChapter(this, source, 0, descChapterName, // + getDesc(), isHtml(), null)); } pg.done(); @@@ -493,8 -489,8 +489,8 @@@ case TEXT: support = new Text(); break; - case MANGAFOX: - support = new MangaFox(); + case MANGAHUB: + support = new MangaHub(); break; case E621: support = new E621(); diff --combined supported/BasicSupportHelper.java index 41716df,b5c7bb9..b5c7bb9 --- a/supported/BasicSupportHelper.java +++ b/supported/BasicSupportHelper.java @@@ -26,10 -26,9 +26,9 @@@ public class BasicSupportHelper * @return the cover if any, or NULL */ public Image getDefaultCover(String subject) { - if (subject != null && !subject.isEmpty() - && Instance.getCoverDir() != null) { + if (subject != null && !subject.isEmpty() && Instance.getInstance().getCoverDir() != null) { try { - File fileCover = new File(Instance.getCoverDir(), subject); + File fileCover = new File(Instance.getInstance().getCoverDir(), subject); return getImage(null, fileCover.toURI().toURL(), subject); } catch (MalformedURLException e) { } @@@ -81,7 -80,7 +80,7 @@@ } InputStream in = null; try { - in = Instance.getCache().open(url, support, true); + in = Instance.getInstance().getCache().open(url, support, true); return new Image(in); } catch (IOException e) { } finally { @@@ -156,8 -155,7 +155,7 @@@ // try for URLs try { for (String ext : getImageExt(true)) { - if (Instance.getCache() - .check(new URL(line + ext), true)) { + if (Instance.getInstance().getCache().check(new URL(line + ext), true)) { url = new URL(line + ext); break; } @@@ -168,7 -166,7 +166,7 @@@ for (String ext : getImageExt(true)) { try { url = new URL(line + ext); - Instance.getCache().refresh(url, support, true); + Instance.getInstance().getCache().refresh(url, support, true); break; } catch (IOException e) { // no image with this ext @@@ -184,7 -182,7 +182,7 @@@ // refresh the cached file if (url != null) { try { - Instance.getCache().refresh(url, support, true); + Instance.getInstance().getCache().refresh(url, support, true); } catch (IOException e) { // woops, broken image url = null; @@@ -206,7 -204,7 +204,7 @@@ public String fixAuthor(String author) { if (author != null) { for (String suffix : new String[] { " ", ":" }) { - for (String byString : Instance.getConfig().getList(Config.CONF_BYS)) { + for (String byString : Instance.getInstance().getConfig().getList(Config.CONF_BYS)) { byString += suffix; if (author.toUpperCase().startsWith(byString.toUpperCase())) { author = author.substring(byString.length()).trim(); diff --combined supported/BasicSupportImages.java index 69a7c86,576cb17..576cb17 --- a/supported/BasicSupportImages.java +++ b/supported/BasicSupportImages.java @@@ -20,16 -20,32 +20,32 @@@ public class BasicSupportImages * Check if the given resource can be a local image or a remote image, then * refresh the cache with it if it is. * + * @param support + * the support to use to download the resource (can be NULL) * @param dir * the local directory to search, if any * @param line * the resource to check * * @return the image if found, or NULL - * */ public Image getImage(BasicSupport support, File dir, String line) { URL url = getImageUrl(support, dir, line); + return getImage(support,url); + } + + /** + * Check if the given resource can be a local image or a remote image, then + * refresh the cache with it if it is. + * + * @param support + * the support to use to download the resource (can be NULL) + * @param url + * the actual URL to check (file or remote, can be NULL) + * + * @return the image if found, or NULL + */ + public Image getImage(BasicSupport support, URL url) { if (url != null) { if ("file".equals(url.getProtocol())) { if (new File(url.getPath()).isDirectory()) { @@@ -38,7 -54,7 +54,7 @@@ } InputStream in = null; try { - in = Instance.getCache().open(url, support, true); + in = Instance.getInstance().getCache().open(url, support, true); return new Image(in); } catch (IOException e) { } finally { @@@ -58,6 -74,8 +74,8 @@@ * Check if the given resource can be a local image or a remote image, then * refresh the cache with it if it is. * + * @param support + * the support to use to download the resource (can be NULL) * @param dir * the local directory to search, if any * @param line @@@ -108,7 -126,7 +126,7 @@@ // try for URLs try { for (String ext : getImageExt(true)) { - if (Instance.getCache() + if (Instance.getInstance().getCache() .check(new URL(line + ext), true)) { url = new URL(line + ext); break; @@@ -120,7 -138,7 +138,7 @@@ for (String ext : getImageExt(true)) { try { url = new URL(line + ext); - Instance.getCache().refresh(url, support, true); + Instance.getInstance().getCache().refresh(url, support, true); break; } catch (IOException e) { // no image with this ext @@@ -136,7 -154,7 +154,7 @@@ // refresh the cached file if (url != null) { try { - Instance.getCache().refresh(url, support, true); + Instance.getInstance().getCache().refresh(url, support, true); } catch (IOException e) { // woops, broken image url = null; diff --combined supported/BasicSupportPara.java index ef4d7d7,58c363a..58c363a --- a/supported/BasicSupportPara.java +++ b/supported/BasicSupportPara.java @@@ -26,14 -26,10 +26,10 @@@ import be.nikiroo.utils.StringUtils */ public class BasicSupportPara { // quote chars - private static char openQuote = Instance.getTrans().getCharacter( - StringId.OPEN_SINGLE_QUOTE); - private static char closeQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_SINGLE_QUOTE); - private static char openDoubleQuote = Instance.getTrans().getCharacter( - StringId.OPEN_DOUBLE_QUOTE); - private static char closeDoubleQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_DOUBLE_QUOTE); + private static char openQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_SINGLE_QUOTE); + private static char closeQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_SINGLE_QUOTE); + private static char openDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_DOUBLE_QUOTE); + private static char closeDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_DOUBLE_QUOTE); // used by this class: BasicSupportHelper bsHelper; @@@ -77,9 -73,8 +73,8 @@@ // "Chapter 5: - Fun!" after the ": " was automatically added) String chapterName = processPara(name, false) .getContent().trim(); - for (String lang : Instance.getConfig().getList(Config.CONF_CHAPTER)) { - String chapterWord = Instance.getConfig().getStringX( - Config.CONF_CHAPTER, lang); + for (String lang : Instance.getInstance().getConfig().getList(Config.CONF_CHAPTER)) { + String chapterWord = Instance.getInstance().getConfig().getStringX(Config.CONF_CHAPTER, lang); if (chapterName.startsWith(chapterWord)) { chapterName = chapterName.substring(chapterWord.length()) .trim(); diff --combined supported/BasicSupport_Deprecated.java index 1faac03,4a7b65b..4a7b65b --- a/supported/BasicSupport_Deprecated.java +++ b/supported/BasicSupport_Deprecated.java @@@ -42,14 -42,10 +42,10 @@@ public abstract class BasicSupport_Depr private InputStream in; // quote chars - private char openQuote = Instance.getTrans().getCharacter( - StringId.OPEN_SINGLE_QUOTE); - private char closeQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_SINGLE_QUOTE); - private char openDoubleQuote = Instance.getTrans().getCharacter( - StringId.OPEN_DOUBLE_QUOTE); - private char closeDoubleQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_DOUBLE_QUOTE); + private char openQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_SINGLE_QUOTE); + private char closeQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_SINGLE_QUOTE); + private char openDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_DOUBLE_QUOTE); + private char closeDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_DOUBLE_QUOTE); // New methods not used in Deprecated mode @Override @@@ -223,11 -219,8 +219,8 @@@ pg.setProgress(60); if (getDesc) { - String descChapterName = Instance.getTrans().getString( - StringId.DESCRIPTION); - story.getMeta().setResume( - makeChapter(url, 0, descChapterName, - getDesc(url, getInput()), null)); + String descChapterName = Instance.getInstance().getTrans().getString(StringId.DESCRIPTION); + story.getMeta().setResume(makeChapter(url, 0, descChapterName, getDesc(url, getInput()), null)); } pg.setProgress(100); @@@ -299,8 -292,7 +292,7 @@@ InputStream chapIn = null; if (chap.getValue() != null) { setCurrentReferer(chap.getValue()); - chapIn = Instance.getCache().open(chap.getValue(), - this, false); + chapIn = Instance.getInstance().getCache().open(chap.getValue(), this, false); } pgChaps.setProgress(i * 100); try { @@@ -391,9 -383,8 +383,8 @@@ // redundant "Chapter x: " in front of it, or "-" (as in // "Chapter 5: - Fun!" after the ": " was automatically added) String chapterName = processPara(name).getContent().trim(); - for (String lang : Instance.getConfig().getList(Config.CONF_CHAPTER)) { - String chapterWord = Instance.getConfig().getStringX( - Config.CONF_CHAPTER, lang); + for (String lang : Instance.getInstance().getConfig().getList(Config.CONF_CHAPTER)) { + String chapterWord = Instance.getInstance().getConfig().getStringX(Config.CONF_CHAPTER, lang); if (chapterName.startsWith(chapterWord)) { chapterName = chapterName.substring(chapterWord.length()) .trim(); @@@ -591,10 -582,9 +582,9 @@@ * @return the cover if any, or NULL */ static Image getDefaultCover(String subject) { - if (subject != null && !subject.isEmpty() - && Instance.getCoverDir() != null) { + if (subject != null && !subject.isEmpty() && Instance.getInstance().getCoverDir() != null) { try { - File fileCover = new File(Instance.getCoverDir(), subject); + File fileCover = new File(Instance.getInstance().getCoverDir(), subject); return getImage(null, fileCover.toURI().toURL(), subject); } catch (MalformedURLException e) { } @@@ -644,7 -634,7 +634,7 @@@ } InputStream in = null; try { - in = Instance.getCache().open(url, getSupport(url), true); + in = Instance.getInstance().getCache().open(url, getSupport(url), true); return new Image(in); } catch (IOException e) { } finally { @@@ -716,8 -706,7 +706,7 @@@ // try for URLs try { for (String ext : getImageExt(true)) { - if (Instance.getCache() - .check(new URL(line + ext), true)) { + if (Instance.getInstance().getCache().check(new URL(line + ext), true)) { url = new URL(line + ext); break; } @@@ -728,7 -717,7 +717,7 @@@ for (String ext : getImageExt(true)) { try { url = new URL(line + ext); - Instance.getCache().refresh(url, support, true); + Instance.getInstance().getCache().refresh(url, support, true); break; } catch (IOException e) { // no image with this ext @@@ -744,7 -733,7 +733,7 @@@ // refresh the cached file if (url != null) { try { - Instance.getCache().refresh(url, support, true); + Instance.getInstance().getCache().refresh(url, support, true); } catch (IOException e) { // woops, broken image url = null; @@@ -770,7 -759,7 +759,7 @@@ * in case of I/O error */ protected InputStream openInput(URL source) throws IOException { - return Instance.getCache().open(source, this, false); + return Instance.getInstance().getCache().open(source, this, false); } /** diff --combined supported/Cbz.java index 22e436a,76b66ab..76b66ab --- a/supported/Cbz.java +++ b/supported/Cbz.java @@@ -71,7 -71,7 +71,7 @@@ class Cbz extends Epub pgMeta.done(); // 10% - File tmpDir = Instance.getTempFiles().createTempDir("info-text"); + File tmpDir = Instance.getInstance().getTempFiles().createTempDir("info-text"); String basename = null; Map images = new HashMap(); @@@ -97,7 -97,7 +97,7 @@@ try { images.put(uuid, new Image(zipIn)); } catch (Exception e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } if (pg.getProgress() < 85) { @@@ -114,9 -114,7 +114,7 @@@ } String ext = "." - + Instance.getConfig() - .getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER) - .toLowerCase(); + + Instance.getInstance().getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER).toLowerCase(); String coverName = meta.getUuid() + "_" + basename + ext; Image cover = images.get(coverName); images.remove(coverName); @@@ -176,7 -174,7 +174,7 @@@ chap.getParagraphs().add( new Paragraph(images.get(uuid))); } catch (Exception e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } } } diff --combined supported/E621.java index dfa9e5e,f1660e1..f1660e1 --- a/supported/E621.java +++ b/supported/E621.java @@@ -9,22 -9,25 +9,25 @@@ import java.net.URLDecoder import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collections; + import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; - import java.util.Scanner; + + import org.jsoup.helper.DataUtil; + import org.jsoup.nodes.Document; + import org.jsoup.nodes.Element; import be.nikiroo.fanfix.Instance; - import be.nikiroo.fanfix.data.Chapter; import be.nikiroo.fanfix.data.MetaData; - import be.nikiroo.fanfix.data.Story; + import be.nikiroo.utils.IOUtils; import be.nikiroo.utils.Image; import be.nikiroo.utils.Progress; import be.nikiroo.utils.StringUtils; /** - * Support class for e621.net and e926.net, a Furry website supporting comics, + * Support class for e621.net and + * e926.net, a Furry website supporting comics, * including some of MLP. *

* e926.net only shows the "clean" images and @@@ -32,335 -35,226 +35,226 @@@ * * @author niki */ - class E621 extends BasicSupport_Deprecated { + class E621 extends BasicSupport { + @Override + protected boolean supports(URL url) { + String host = url.getHost(); + if (host.startsWith("www.")) { + host = host.substring("www.".length()); + } + + return ("e621.net".equals(host) || "e926.net".equals(host)) && (isPool(url) || isSearchOrSet(url)); + } + @Override - protected MetaData getMeta(URL source, InputStream in) throws IOException { + protected boolean isHtml() { + return true; + } + + @Override + protected MetaData getMeta() throws IOException { MetaData meta = new MetaData(); - meta.setTitle(getTitle(reset(in))); - meta.setAuthor(getAuthor(source, reset(in))); + meta.setTitle(getTitle()); + meta.setAuthor(getAuthor()); meta.setDate(""); - meta.setTags(getTags(source, reset(in), false)); + meta.setTags(getTags()); meta.setSource(getType().getSourceName()); - meta.setUrl(source.toString()); + meta.setUrl(getSource().toString()); meta.setPublisher(getType().getSourceName()); - meta.setUuid(source.toString()); + meta.setUuid(getSource().toString()); meta.setLuid(""); meta.setLang("en"); meta.setSubject("Furry"); meta.setType(getType().toString()); meta.setImageDocument(true); - meta.setCover(getCover(source, reset(in))); + meta.setCover(getCover()); meta.setFakeCover(true); return meta; } - private List getTags(URL source, InputStream in, boolean authors) { - List tags = new ArrayList(); - - if (isSearch(source)) { - String tagLine = getLine(in, "id=\"tag-sidebar\"", 1); - if (tagLine != null) { - String key = "href=\""; - for (int pos = tagLine.indexOf(key); pos >= 0; pos = tagLine - .indexOf(key, pos + 1)) { - int end = tagLine.indexOf("\"", pos + key.length()); - if (end >= 0) { - String href = tagLine.substring(pos, end); - String subkey; - if (authors) - subkey = "?name="; - else - subkey = "?title="; - if (href.contains(subkey)) { - String tag = href.substring(href.indexOf(subkey) - + subkey.length()); - try { - tags.add(URLDecoder.decode(tag, "UTF-8")); - } catch (UnsupportedEncodingException e) { - // supported JVMs must have UTF-8 support - e.printStackTrace(); - } - } - } - } - - } - } - - return tags; - } - @Override - public Story process(URL url, Progress pg) throws IOException { - // There is no chapters on e621, just pagination... - Story story = super.process(url, pg); + protected String getDesc() throws IOException { + if (isSearchOrSet(getSource())) { + StringBuilder builder = new StringBuilder(); + builder.append("A collection of images from ").append(getSource().getHost()).append("\n") // + .append("\tTime of creation: " + StringUtils.fromTime(new Date().getTime())).append("\n") // + .append("\tTags: ");// + for (String tag : getTags()) { + builder.append("\t\t").append(tag); + } - Chapter only = new Chapter(1, null); - for (Chapter chap : story) { - only.getParagraphs().addAll(chap.getParagraphs()); + return builder.toString(); } - story.getChapters().clear(); - story.getChapters().add(only); - - return story; - } - - @Override - protected boolean supports(URL url) { - String host = url.getHost(); - if (host.startsWith("www.")) { - host = host.substring("www.".length()); + if (isPool(getSource())) { + Element el = getSourceNode().getElementById("description"); + if (el != null) { + return el.text(); + } } - return ("e621.net".equals(host) || "e926.net".equals(host)) - && (isPool(url) || isSearch(url)); + return null; } @Override - protected boolean isHtml() { - return true; - } - - private Image getCover(URL source, InputStream in) throws IOException { - URL urlForCover = source; - if (isPool(source)) { - urlForCover = new URL(source.toString() + "?page=1"); - } - - String images = getChapterContent(urlForCover, in, 1, null); - if (!images.isEmpty()) { - int pos = images.indexOf("
"); - if (pos >= 0) { - images = images.substring(1, pos - 1); - return getImage(this, null, images); - } + protected List> getChapters(Progress pg) throws IOException { + if (isPool(getSource())) { + String baseUrl = "https://e621.net/" + getSource().getPath() + "?page="; + return getChapters(getSource(), pg, baseUrl, ""); + } else if (isSearchOrSet(getSource())) { + String baseUrl = "https://e621.net/posts/?page="; + String search = "&tags=" + getTagsFromUrl(getSource()); + return getChapters(getSource(), pg, baseUrl, search); } - return null; + return new LinkedList>(); } - private String getAuthor(URL source, InputStream in) { - if (isSearch(source)) { - StringBuilder builder = new StringBuilder(); - for (String author : getTags(source, in, true)) { - if (builder.length() > 0) - builder.append(", "); - builder.append(author); - } + private List> getChapters(URL source, Progress pg, String baseUrl, String parameters) + throws IOException { + List> urls = new ArrayList>(); - return builder.toString(); + if (source.getHost().contains("e926")) { + baseUrl = baseUrl.replace("e621", "e926"); } - String author = getLine(in, "href=\"/post/show/", 0); - if (author != null) { - String key = "href=\""; - int pos = author.indexOf(key); - if (pos >= 0) { - author = author.substring(pos + key.length()); - pos = author.indexOf("\""); - if (pos >= 0) { - author = author.substring(0, pos - 1); - String page = source.getProtocol() + "://" - + source.getHost() + author; - try { - InputStream pageIn = Instance.getCache().open( - new URL(page), this, false); - try { - key = "class=\"tag-type-artist\""; - author = getLine(pageIn, key, 0); - if (author != null) { - pos = author.indexOf("= 0) { - author = author.substring(pos); - pos = author.indexOf(""); - if (pos >= 0) { - author = author.substring(0, pos); - return StringUtils.unhtml(author); - } - } - } - } finally { - pageIn.close(); - } - } catch (Exception e) { - // No author found + for (int i = 1; true; i++) { + URL url = new URL(baseUrl + i + parameters); + try { + InputStream pageI = Instance.getInstance().getCache().open(url, this, false); + try { + if (IOUtils.readSmallStream(pageI).contains("Nobody here but us chickens!")) { + break; } + urls.add(new AbstractMap.SimpleEntry("Page " + Integer.toString(i), url)); + } finally { + pageI.close(); } + } catch (Exception e) { + break; } } - return null; + // They are sorted in reverse order on the website + Collections.reverse(urls); + return urls; } - private String getTitle(InputStream in) { - String title = getLine(in, "", 0); - if (title != null) { - int pos = title.indexOf('>'); - if (pos >= 0) { - title = title.substring(pos + 1); - pos = title.indexOf('<'); - if (pos >= 0) { - title = title.substring(0, pos); - } - } - - if (title.startsWith("Pool:")) { - title = title.substring("Pool:".length()); - } - - title = StringUtils.unhtml(title).trim(); + @Override + protected String getChapterContent(URL chapUrl, int number, Progress pg) throws IOException { + StringBuilder builder = new StringBuilder(); + Document chapterNode = loadDocument(chapUrl); + for (Element el : chapterNode.getElementsByTag("article")) { + builder.append("["); + builder.append(el.attr("data-file-url")); + builder.append("]<br/>"); } - return title; + return builder.toString(); } @Override - protected String getDesc(URL source, InputStream in) throws IOException { - String desc = getLine(in, "margin-bottom: 2em;", 0); - - if (desc != null) { - StringBuilder builder = new StringBuilder(); - - boolean inTags = false; - for (char car : desc.toCharArray()) { - if ((inTags && car == '>') || (!inTags && car == '<')) { - inTags = !inTags; - } - - if (inTags) { - builder.append(car); + protected URL getCanonicalUrl(URL source) { + if (isSetOriginalUrl(source)) { + try { + Document doc = DataUtil.load(Instance.getInstance().getCache().open(source, this, false), "UTF-8", source.toString()); + for (Element shortname : doc.getElementsByClass("set-shortname")) { + for (Element el : shortname.getElementsByTag("a")) { + if (!el.attr("href").isEmpty()) + return new URL(el.absUrl("href")); + } } + } catch (IOException e) { + Instance.getInstance().getTraceHandler().error(e); } - - return builder.toString().trim(); } - return null; - } - - @Override - protected List<Entry<String, URL>> getChapters(URL source, InputStream in, - Progress pg) throws IOException { if (isPool(source)) { - return getChaptersPool(source, in, pg); - } else if (isSearch(source)) { - return getChaptersSearch(source, in, pg); + try { + return new URL(source.toString().replace("/pool/show/", "/pools/")); + } catch (MalformedURLException e) { + } } - return new LinkedList<Entry<String, URL>>(); + return super.getCanonicalUrl(source); } - private List<Entry<String, URL>> getChaptersSearch(URL source, - InputStream in, Progress pg) throws IOException { - List<Entry<String, URL>> urls = new ArrayList<Entry<String, URL>>(); + // returns "xxx+ddd+ggg" if "tags=xxx+ddd+ggg" was present in the query + private String getTagsFromUrl(URL url) { + String tags = url == null ? "" : url.getQuery(); + int pos = tags.indexOf("tags="); - String search = source.getPath(); - if (search.endsWith("/")) { - search = search.substring(0, search.length() - 1); + if (pos >= 0) { + tags = tags.substring(pos).substring("tags=".length()); + } else { + return ""; } - int pos = search.lastIndexOf('/'); - if (pos >= 0) { - search = search.substring(pos + 1); + pos = tags.indexOf('&'); + if (pos > 0) { + tags = tags.substring(0, pos); + } + pos = tags.indexOf('/'); + if (pos > 0) { + tags = tags.substring(0, pos); } - String baseUrl = "https://e621.net/post/index/"; - if (source.getHost().contains("e926")) { - baseUrl = baseUrl.replace("e621", "e926"); + return tags; + } + + private String getTitle() { + String title = ""; + + Element el = getSourceNode().getElementsByTag("title").first(); + if (el != null) { + title = el.text().trim(); } - for (int i = 1; true; i++) { - URL url = new URL(baseUrl + i + "/" + search + "/"); - try { - InputStream pageI = Instance.getCache().open(url, this, false); - try { - if (getLine(pageI, "No posts matched your search.", 0) != null) - break; - urls.add(new AbstractMap.SimpleEntry<String, URL>("Page " - + Integer.toString(i), url)); - } finally { - pageI.close(); - } - } catch (Exception e) { - break; + for (String s : new String[] { "e621", "-", "e621" }) { + if (title.startsWith(s)) { + title = title.substring(s.length()).trim(); + } + if (title.endsWith(s)) { + title = title.substring(0, title.length() - s.length()).trim(); } + } - // They are sorted in reverse order on the website - Collections.reverse(urls); - return urls; + if (isSearchOrSet(getSource())) { + title = title.isEmpty() ? "e621" : "[e621] " + title; + } + return title; } - private List<Entry<String, URL>> getChaptersPool(URL source, - InputStream in, Progress pg) throws IOException { - List<Entry<String, URL>> urls = new ArrayList<Entry<String, URL>>(); - int last = 1; // no pool/show when only one page - - @SuppressWarnings("resource") - Scanner scan = new Scanner(in, "UTF-8"); - scan.useDelimiter("\\n"); - while (scan.hasNext()) { - String line = scan.next(); - for (int pos = line.indexOf(source.getPath()); pos >= 0; pos = line - .indexOf(source.getPath(), pos + source.getPath().length())) { - int equalPos = line.indexOf("=", pos); - int quotePos = line.indexOf("\"", pos); - if (equalPos >= 0 && quotePos > equalPos) { - String snum = line.substring(equalPos + 1, quotePos); - try { - int num = Integer.parseInt(snum); - if (num > last) { - last = num; - } - } catch (NumberFormatException e) { + private String getAuthor() throws IOException { + StringBuilder builder = new StringBuilder(); + + if (isSearchOrSet(getSource())) { + for (Element el : getSourceNode().getElementsByClass("search-tag")) { + if (el.attr("itemprop").equals("author")) { + if (builder.length() > 0) { + builder.append(", "); } + builder.append(el.text().trim()); } } } - for (int i = 1; i <= last; i++) { - urls.add(new AbstractMap.SimpleEntry<String, URL>(Integer - .toString(i), new URL(source.toString() + "?page=" + i))); - } + if (isPool(getSource())) { + String desc = getDesc(); + String descL = desc.toLowerCase(); - return urls; - } - - @Override - protected String getChapterContent(URL source, InputStream in, int number, - Progress pg) throws IOException { - StringBuilder builder = new StringBuilder(); - String staticSite = "https://static1.e621.net"; - if (source.getHost().contains("e926")) { - staticSite = staticSite.replace("e621", "e926"); - } + if (descL.startsWith("by:") || descL.startsWith("by ")) { + desc = desc.substring(3).trim(); + desc = desc.split("\n")[0]; - String key = staticSite + "/data/preview/"; - - @SuppressWarnings("resource") - Scanner scan = new Scanner(in, "UTF-8"); - scan.useDelimiter("\\n"); - while (scan.hasNext()) { - String line = scan.next(); - if (line.contains("class=\"preview")) { - for (int pos = line.indexOf(key); pos >= 0; pos = line.indexOf( - key, pos + key.length())) { - int endPos = line.indexOf("\"", pos); - if (endPos >= 0) { - String id = line.substring(pos + key.length(), endPos); - id = staticSite + "/data/" + id; - - int dotPos = id.lastIndexOf("."); - if (dotPos >= 0) { - id = id.substring(0, dotPos); - builder.append("["); - builder.append(id); - builder.append("]<br/>"); - } - } + String tab[] = desc.split(" "); + for (int i = 0; i < Math.min(tab.length, 5); i++) { + if (tab[i].startsWith("http")) + break; + builder.append(" ").append(tab[i]); } } } @@@ -368,42 -262,52 +262,52 @@@ return builder.toString(); } - @Override - protected URL getCanonicalUrl(URL source) { - if (isSearch(source)) { - // /post?tags=tag1+tag2 -> ../post/index/1/tag1%32tag2 - String key = "?tags="; - if (source.toString().contains(key)) { - int pos = source.toString().indexOf(key); - String tags = source.toString().substring(pos + key.length()); - tags = tags.replace("+", "%20"); - - String base = source.toString().substring(0, pos); - if (!base.endsWith("/")) { - base += "/"; - } - if (base.endsWith("/search/")) { - base = base.substring(0, base.indexOf("/search/") + 1); - } - + // no tags for pools + private List<String> getTags() { + List<String> tags = new ArrayList<String>(); + if (isSearchOrSet(getSource())) { + String str = getTagsFromUrl(getSource()); + for (String tag : str.split("\\+")) { try { - return new URL(base + "index/1/" + tags); - } catch (MalformedURLException e) { - Instance.getTraceHandler().error(e); + tags.add(URLDecoder.decode(tag.trim(), "UTF-8").trim()); + } catch (UnsupportedEncodingException e) { } } } - return super.getCanonicalUrl(source); + return tags; + } + + private Image getCover() throws IOException { + Image image = null; + List<Entry<String, URL>> chapters = getChapters(null); + if (!chapters.isEmpty()) { + URL chap1Url = chapters.get(0).getValue(); + String imgsChap1 = getChapterContent(chap1Url, 1, null); + if (!imgsChap1.isEmpty()) { + imgsChap1 = imgsChap1.split("]")[0].substring(1).trim(); + image = bsImages.getImage(this, new URL(imgsChap1)); + } + } + + return image; + } + + // note: will be removed at getCanonicalUrl() + private boolean isSetOriginalUrl(URL originalUrl) { + return originalUrl.getPath().startsWith("/post_sets/"); } private boolean isPool(URL url) { - return url.getPath().startsWith("/pool/"); + return url.getPath().startsWith("/pools/") || url.getPath().startsWith("/pool/show/"); } - private boolean isSearch(URL url) { - return url.getPath().startsWith("/post/index/") - || (url.getPath().equals("/post/search") && url.getQuery() - .startsWith("tags=")); + // set will be renamed into search by canonical url + private boolean isSearchOrSet(URL url) { + return + // search: + (url.getPath().equals("/posts") && url.getQuery().contains("tags=")) + // or set: + || isSetOriginalUrl(url); } } diff --combined supported/EHentai.java index 67585cd,03c1557..03c1557 --- a/supported/EHentai.java +++ b/supported/EHentai.java @@@ -255,10 -255,8 +255,8 @@@ class EHentai extends BasicSupport_Depr try { pages.add(new URL(line)); } catch (MalformedURLException e) { - Instance.getTraceHandler().error( - new IOException( - "Parsing error, a link is not correctly parsed: " - + line, e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Parsing error, a link is not correctly parsed: " + line, e)); } } } @@@ -272,7 -270,7 +270,7 @@@ StringBuilder builder = new StringBuilder(); for (URL page : pages) { - InputStream pageIn = Instance.getCache().open(page, this, false); + InputStream pageIn = Instance.getInstance().getCache().open(page, this, false); try { String link = getKeyLine(pageIn, "id=\"img\"", "src=\"", "\""); if (link != null && !link.isEmpty()) { diff --combined supported/Epub.java index 82af118,f8e4678..f8e4678 --- a/supported/Epub.java +++ b/supported/Epub.java @@@ -42,10 -42,8 +42,8 @@@ class Epub extends InfoText try { return new File(fakeSource.toURI()); } catch (URISyntaxException e) { - Instance.getTraceHandler() - .error(new IOException( - "Cannot get the source file from the info-text URL", - e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Cannot get the source file from the info-text URL", e)); } return null; @@@ -57,9 -55,7 +55,7 @@@ try { fakeIn.reset(); } catch (IOException e) { - Instance.getTraceHandler() - .error(new IOException( - "Cannot reset the Epub Text stream", e)); + Instance.getInstance().getTraceHandler().error(new IOException("Cannot reset the Epub Text stream", e)); } return fakeIn; @@@ -87,8 -83,7 +83,7 @@@ ZipInputStream zipIn = null; try { zipIn = new ZipInputStream(in); - tmpDir = Instance.getTempFiles().createTempDir( - "fanfic-reader-parser"); + tmpDir = Instance.getInstance().getTempFiles().createTempDir("fanfic-reader-parser"); File tmp = new File(tmpDir, "file.txt"); File tmpInfo = new File(tmpDir, "file.info"); @@@ -129,7 -124,7 +124,7 @@@ try { cover = new Image(zipIn); } catch (Exception e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } } } else if (entry.getName().equals(getDataPrefix() + "URL")) { diff --combined supported/Fanfiction.java index fcf773b,282192e..282192e --- a/supported/Fanfiction.java +++ b/supported/Fanfiction.java @@@ -157,10 -157,8 +157,8 @@@ class Fanfiction extends BasicSupport_D return sdf .format(new Date(1000 * Long.parseLong(line))); } catch (NumberFormatException e) { - Instance.getTraceHandler().error( - new IOException( - "Cannot convert publication date: " - + line, e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Cannot convert publication date: " + line, e)); } } } @@@ -243,11 -241,8 +241,8 @@@ urls.add(new AbstractMap.SimpleEntry<String, URL>( name.trim(), new URL(base + i + suffix))); } catch (MalformedURLException e) { - Instance.getTraceHandler() - .error(new IOException( - "Cannot parse chapter " + i - + " url: " - + (base + i + suffix), e)); + Instance.getInstance().getTraceHandler().error( + new IOException("Cannot parse chapter " + i + " url: " + (base + i + suffix), e)); } } } @@@ -304,10 -299,9 +299,9 @@@ int pos = line.indexOf("<hr"); if (pos >= 0) { boolean chaptered = false; - for (String lang : Instance.getConfig().getList( - Config.CONF_CHAPTER)) { - String chapterWord = Instance.getConfig() - .getStringX(Config.CONF_CHAPTER, lang); + for (String lang : Instance.getInstance().getConfig().getList(Config.CONF_CHAPTER)) { + String chapterWord = Instance.getInstance().getConfig().getStringX(Config.CONF_CHAPTER, + lang); int posChap = line.indexOf(chapterWord + " "); if (posChap < pos) { chaptered = true; diff --combined supported/Fimfiction.java index e96ac4f,77267c1..77267c1 --- a/supported/Fimfiction.java +++ b/supported/Fimfiction.java @@@ -230,7 -230,7 +230,7 @@@ class Fimfiction extends BasicSupport_D urls.add(new AbstractMap.SimpleEntry<String, URL>(name, new URL("http://www.fimfiction.net" + line))); } catch (MalformedURLException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } } } diff --combined supported/FimfictionApi.java index c97ecf7,6c6d7ba..6c6d7ba --- a/supported/FimfictionApi.java +++ b/supported/FimfictionApi.java @@@ -37,22 -37,17 +37,17 @@@ class FimfictionApi extends BasicSuppor private Map<Integer, String> chapterContents; public FimfictionApi() throws IOException { - if (Instance.getConfig().getBoolean( - Config.LOGIN_FIMFICTION_APIKEY_FORCE_HTML, false)) { - throw new IOException( - "Configuration is set to force HTML scrapping"); + if (Instance.getInstance().getConfig().getBoolean(Config.LOGIN_FIMFICTION_APIKEY_FORCE_HTML, false)) { + throw new IOException("Configuration is set to force HTML scrapping"); } - String oauth = Instance.getConfig().getString( - Config.LOGIN_FIMFICTION_APIKEY_TOKEN); + String oauth = Instance.getInstance().getConfig().getString(Config.LOGIN_FIMFICTION_APIKEY_TOKEN); if (oauth == null || oauth.isEmpty()) { - String clientId = Instance.getConfig().getString( - Config.LOGIN_FIMFICTION_APIKEY_CLIENT_ID) - + ""; - String clientSecret = Instance.getConfig().getString( - Config.LOGIN_FIMFICTION_APIKEY_CLIENT_SECRET) + String clientId = Instance.getInstance().getConfig().getString(Config.LOGIN_FIMFICTION_APIKEY_CLIENT_ID) + ""; + String clientSecret = Instance.getInstance().getConfig() + .getString(Config.LOGIN_FIMFICTION_APIKEY_CLIENT_SECRET) + ""; if (clientId.trim().isEmpty() || clientSecret.trim().isEmpty()) { throw new IOException("API key required for the beta API v2"); @@@ -60,9 -55,8 +55,8 @@@ oauth = generateOAuth(clientId, clientSecret); - Instance.getConfig().setString( - Config.LOGIN_FIMFICTION_APIKEY_TOKEN, oauth); - Instance.getConfig().updateFile(); + Instance.getInstance().getConfig().setString(Config.LOGIN_FIMFICTION_APIKEY_TOKEN, oauth); + Instance.getInstance().getConfig().updateFile(); } this.oauth = oauth; @@@ -116,7 -110,7 +110,7 @@@ urlString = urlString.replace("[", "%5B").replace("]", "%5D"); URL url = new URL(urlString); - InputStream jsonIn = Instance.getCache().open(url, this, false); + InputStream jsonIn = Instance.getInstance().getCache().open(url, this, false); try { return IOUtils.readSmallStream(jsonIn); } finally { @@@ -150,17 -144,15 +144,15 @@@ // No need to use the oauth, cookies... for the cover // Plus: it crashes on Android because of the referer try { - InputStream in = Instance.getCache().open(coverImageUrl, null, - true); + InputStream in = Instance.getInstance().getCache().open(coverImageUrl, null, true); try { meta.setCover(new Image(in)); } finally { in.close(); } } catch (IOException e) { - Instance.getTraceHandler().error( - new IOException( - "Cannot get the story cover, ignoring...", e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Cannot get the story cover, ignoring...", e)); } } @@@ -258,8 -250,7 +250,7 @@@ params.put("client_id", clientId); params.put("client_secret", clientSecret); params.put("grant_type", "client_credentials"); - InputStream in = Instance.getCache().openNoCache(url, null, params, - null, null); + InputStream in = Instance.getInstance().getCache().openNoCache(url, null, params, null, null); String jsonToken = IOUtils.readSmallStream(in); in.close(); diff --combined supported/Html.java index c27dd32,900fa0a..900fa0a --- a/supported/Html.java +++ b/supported/Html.java @@@ -51,9 -51,8 +51,8 @@@ class Html extends InfoText try { source = txt.toURI().toURL(); } catch (MalformedURLException e) { - Instance.getTraceHandler().error( - new IOException("Cannot convert the right URL for " - + source, e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Cannot convert the right URL for " + source, e)); } } diff --combined supported/InfoReader.java index c22dbd7,220350e..220350e --- a/supported/InfoReader.java +++ b/supported/InfoReader.java @@@ -98,8 -98,7 +98,7 @@@ public class InfoReader File basefile = new File(sourceInfoFile.getFile()); String ext = "." - + Instance.getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER) - .toLowerCase(); + + Instance.getInstance().getConfig().getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER).toLowerCase(); // Without removing ext cover = bsHelper.getImage(null, sourceInfoFile, diff --combined supported/MangaHub.java index 0000000,706a591..706a591 mode 000000,100644..100644 --- a/supported/MangaHub.java +++ b/supported/MangaHub.java @@@ -1,0 -1,214 +1,214 @@@ + package be.nikiroo.fanfix.supported; + + import java.io.IOException; + import java.net.HttpURLConnection; + import java.net.MalformedURLException; + import java.net.URL; + import java.util.AbstractMap; + import java.util.ArrayList; + import java.util.Collections; + import java.util.List; + import java.util.Map.Entry; + + import org.jsoup.nodes.Element; + + import be.nikiroo.fanfix.Instance; + import be.nikiroo.fanfix.bundles.Config; + import be.nikiroo.fanfix.data.MetaData; + import be.nikiroo.utils.Image; + import be.nikiroo.utils.Progress; + import be.nikiroo.utils.StringUtils; + + /** + * Support class for <a href="https://mangahub.io/">MangaHub</a>, a website + * dedicated to Manga. + * + * @author niki + */ + class MangaHub extends BasicSupport { + @Override + protected boolean isHtml() { + return true; + } + + @Override + protected MetaData getMeta() throws IOException { + MetaData meta = new MetaData(); + + meta.setTitle(getTitle()); + meta.setDate(""); + meta.setAuthor(getAuthor()); + meta.setTags(getTags()); + meta.setSource(getType().getSourceName()); + meta.setUrl(getSource().toString()); + meta.setPublisher(getType().getSourceName()); + meta.setUuid(getSource().toString()); + meta.setLuid(""); + meta.setLang("en"); + meta.setSubject("manga"); + meta.setType(getType().toString()); + meta.setImageDocument(true); + meta.setCover(getCover()); + + return meta; + } + + private String getTitle() { + Element doc = getSourceNode(); + + Element el = doc.getElementsByTag("h1").first(); + if (el != null) { + return StringUtils.unhtml(el.text()).trim(); + } + + return null; + } + + private String getAuthor() { + String author = ""; + + Element el = getSourceNode().select("h1+div span:not([class])").first(); + if (el != null) + author = StringUtils.unhtml(el.text()).trim(); + return author; + } + + private List<String> getTags() { + return getListA("genre-label"); + } + + private List<String> getListA(String uniqueClass) { + List<String> list = new ArrayList<String>(); + + Element doc = getSourceNode(); + Element el = doc.getElementsByClass(uniqueClass).first(); + if (el != null) { + for (Element valueA : el.getElementsByTag("a")) { + list.add(StringUtils.unhtml(valueA.text()).trim()); + } + } + + return list; + } + + @Override + protected String getDesc() { + Element doc = getSourceNode(); + Element title = doc.getElementsByClass("fullcontent").first(); + if (title != null) { + return StringUtils.unhtml(title.text()).trim(); + } + + return null; + } + + private Image getCover() { + Element doc = getSourceNode(); + Element cover = doc.getElementsByClass("manga-thumb").first(); + if (cover != null) { + try { + return bsImages.getImage(this, new URL(cover.absUrl("src"))); + } catch (MalformedURLException e) { + Instance.getInstance().getTraceHandler().error(e); + } + } + + return null; + } + + @Override + protected List<Entry<String, URL>> getChapters(Progress pg) { + List<Entry<String, URL>> urls = new ArrayList<Entry<String, URL>>(); + + Element doc = getSourceNode(); + for (Element el : doc.getElementsByClass("list-group-item")) { + Element urlEl = el.getElementsByTag("a").first(); + if (urlEl == null) + continue; + + String url = urlEl.absUrl("href"); + + String title = ""; + el = el.getElementsByClass("text-secondary").first(); + if (el != null) { + title = StringUtils.unhtml(el.text()).trim(); + } + + try { + urls.add(new AbstractMap.SimpleEntry<String, URL>(title, new URL(url))); + } catch (Exception e) { + Instance.getInstance().getTraceHandler().error(e); + } + } + + // by default, the chapters are in reversed order + Collections.reverse(urls); + + return urls; + } + + @Override + protected String getChapterContent(URL chapUrl, int number, Progress pg) throws IOException { + if (pg == null) { + pg = new Progress(); + } + + // 1. Get the title and chapter url part + String path = chapUrl.getPath(); + if (path.endsWith("/")) { + path = path.substring(0, path.length() - "/".length()); + } + String tab[] = path.split("/"); + String chap = tab[tab.length - 1]; + String title = tab[tab.length - 2]; + + if (chap.startsWith("chapter-")) { + chap = chap.substring("chapter-".length()); + } + + // 2. generate an image base + String base = "https://img.mghubcdn.com/file/imghub/" + title + "/" + chap + "/"; + + // 3. add each chapter + StringBuilder builder = new StringBuilder(); + + int i = 1; + String url = base + i + ".jpg"; + while (getHttpStatus(new URL(url)) != 404) { + builder.append("["); + builder.append(url); + builder.append("]<br/>"); + + i++; + url = base + i + ".jpg"; + } + + return builder.toString(); + } + + // HTTP response code, or -1 if other error + // TODO: move that to Downloader? + private int getHttpStatus(URL url) { + try { + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + try { + conn.setRequestMethod("HEAD"); + conn.setRequestProperty("User-Agent", Instance.getInstance().getConfig().getString(Config.NETWORK_USER_AGENT)); + conn.setRequestProperty("Accept-Encoding", "gzip"); + conn.setRequestProperty("Accept", "*/*"); + conn.setRequestProperty("Charset", "utf-8"); + + return conn.getResponseCode(); + } finally { + conn.disconnect(); + } + } catch (Exception e) { + return -1; + } + } + + @Override + protected boolean supports(URL url) { + return "mangahub.io".equals(url.getHost()) || "www.mangahub.io".equals(url.getHost()); + } + } diff --combined supported/MangaLel.java index 1ba51bc,9929699..9929699 --- a/supported/MangaLel.java +++ b/supported/MangaLel.java @@@ -149,15 -149,14 +149,14 @@@ class MangaLel extends BasicSupport InputStream coverIn; try { - coverIn = Instance.getCache().open(new URL(coverUrl), this, - true); + coverIn = Instance.getInstance().getCache().open(new URL(coverUrl), this, true); try { return new Image(coverIn); } finally { coverIn.close(); } } catch (IOException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } } } @@@ -209,7 -208,7 +208,7 @@@ StringBuilder builder = new StringBuilder(); - InputStream in = Instance.getCache().open(chapUrl, this, false); + InputStream in = Instance.getInstance().getCache().open(chapUrl, this, false); try { Element pageDoc = DataUtil.load(in, "UTF-8", chapUrl.toString()); Element content = pageDoc.getElementById("content"); diff --combined supported/SupportType.java index ba18949,7ace726..7ace726 --- a/supported/SupportType.java +++ b/supported/SupportType.java @@@ -20,7 -20,7 +20,7 @@@ public enum SupportType /** Fanfictions from a lot of different universes */ FANFICTION, /** Website with lots of Mangas */ - MANGAFOX, + MANGAHUB, /** Furry website with comics support */ E621, /** Furry website with stories */ @@@ -59,8 -59,8 +59,8 @@@ return "info-text"; case MANGA_LEL: return "MangaLEL"; - case MANGAFOX: - return "MangaFox.me"; + case MANGAHUB: + return "MangaHub.io"; case TEXT: return "text"; case YIFFSTAR: @@@ -77,11 -77,10 +77,10 @@@ * @return the description */ public String getDesc() { - String desc = Instance.getTrans().getStringX(StringId.INPUT_DESC, - this.name()); + String desc = Instance.getInstance().getTrans().getStringX(StringId.INPUT_DESC, this.name()); if (desc == null) { - desc = Instance.getTrans().getString(StringId.INPUT_DESC, this); + desc = Instance.getInstance().getTrans().getString(StringId.INPUT_DESC, this); } return desc; diff --combined supported/Text.java index 8af4a40,c54b6a5..c54b6a5 --- a/supported/Text.java +++ b/supported/Text.java @@@ -53,8 -53,7 +53,7 @@@ class Text extends BasicSupport try { in.reset(); } catch (IOException e) { - Instance.getTraceHandler().error( - new IOException("Cannot reset the Text stream", e)); + Instance.getInstance().getTraceHandler().error(new IOException("Cannot reset the Text stream", e)); } return in; @@@ -201,8 -200,7 +200,7 @@@ Image cover = bsImages.getImage(this, sourceFile.getParentFile(), path); if (cover != null) { try { - File tmp = Instance.getTempFiles().createTempFile( - "test_cover_image"); + File tmp = Instance.getInstance().getTempFiles().createTempFile("test_cover_image"); ImageUtils.getInstance().saveAsImage(cover, tmp, "png"); tmp.delete(); } catch (IOException e) { @@@ -271,9 -269,8 +269,8 @@@ try { in.close(); } catch (IOException e) { - Instance.getTraceHandler().error( - new IOException( - "Cannot close the text source file input", e)); + Instance.getInstance().getTraceHandler() + .error(new IOException("Cannot close the text source file input", e)); } } @@@ -310,7 -307,7 +307,7 @@@ file = assureNoTxt(file); file = new File(file.getPath() + ".info"); } catch (URISyntaxException e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); file = null; } @@@ -350,9 -347,8 +347,8 @@@ */ static private String detectChapter(String line, int number) { line = line.toUpperCase(); - for (String lang : Instance.getConfig().getList(Config.CONF_CHAPTER)) { - String chapter = Instance.getConfig().getStringX( - Config.CONF_CHAPTER, lang); + for (String lang : Instance.getInstance().getConfig().getList(Config.CONF_CHAPTER)) { + String chapter = Instance.getInstance().getConfig().getStringX(Config.CONF_CHAPTER, lang); if (chapter != null && !chapter.isEmpty()) { chapter = chapter.toUpperCase() + " "; if (line.startsWith(chapter)) { diff --combined supported/YiffStar.java index aad01a6,6974e9a..6974e9a --- a/supported/YiffStar.java +++ b/supported/YiffStar.java @@@ -67,10 -67,8 +67,8 @@@ class YiffStar extends BasicSupport_Dep public void login() throws IOException { // Note: this should not be necessary anymore // (the "/guest" trick is enough) - String login = Instance.getConfig().getString( - Config.LOGIN_YIFFSTAR_USER); - String password = Instance.getConfig().getString( - Config.LOGIN_YIFFSTAR_PASS); + String login = Instance.getInstance().getConfig().getString(Config.LOGIN_YIFFSTAR_USER); + String password = Instance.getInstance().getConfig().getString(Config.LOGIN_YIFFSTAR_PASS); if (login != null && !login.isEmpty() && password != null && !password.isEmpty()) { @@@ -84,9 -82,8 +82,8 @@@ // Cookies will actually be retained by the cache manager once // logged in - Instance.getCache() - .openNoCache(new URL("https://www.sofurry.com/user/login"), - this, post, null, null).close(); + Instance.getInstance().getCache() + .openNoCache(new URL("https://www.sofurry.com/user/login"), this, post, null, null).close(); } } @@@ -96,8 -93,7 +93,7 @@@ if (source.getPath().startsWith("/view")) { source = guest(source.toString()); // NO CACHE because we don't want the NotLoggedIn message later - InputStream in = Instance.getCache().openNoCache(source, this, - null, null, null); + InputStream in = Instance.getInstance().getCache().openNoCache(source, this, null, null, null); String line = getLine(in, "/browse/folder/", 0); if (line != null) { String[] tab = line.split("\""); @@@ -109,7 -105,7 +105,7 @@@ } } } catch (Exception e) { - Instance.getTraceHandler().error(e); + Instance.getInstance().getTraceHandler().error(e); } return super.getCanonicalUrl(source); @@@ -136,7 -132,7 +132,7 @@@ List<Entry<String, URL>> chaps = getChapters(source, in, null); if (!chaps.isEmpty()) { - in = Instance.getCache().open(chaps.get(0).getValue(), this, true); + in = Instance.getInstance().getCache().open(chaps.get(0).getValue(), this, true); String line = getLine(in, " name=\"og:image\"", 0); if (line != null) { int pos = -1; diff --combined test/BasicSupportDeprecatedTest.java index 9f40a80,c8c4b40..c8c4b40 --- a/test/BasicSupportDeprecatedTest.java +++ b/test/BasicSupportDeprecatedTest.java @@@ -24,14 -24,10 +24,10 @@@ import be.nikiroo.utils.test.TestLaunch class BasicSupportDeprecatedTest extends TestLauncher { // quote chars - private char openQuote = Instance.getTrans().getCharacter( - StringId.OPEN_SINGLE_QUOTE); - private char closeQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_SINGLE_QUOTE); - private char openDoubleQuote = Instance.getTrans().getCharacter( - StringId.OPEN_DOUBLE_QUOTE); - private char closeDoubleQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_DOUBLE_QUOTE); + private char openQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_SINGLE_QUOTE); + private char closeQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_SINGLE_QUOTE); + private char openDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_DOUBLE_QUOTE); + private char closeDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_DOUBLE_QUOTE); public BasicSupportDeprecatedTest(String[] args) { super("BasicSupportDeprecated", args); @@@ -256,9 -252,9 +252,9 @@@ public void test() throws Exception { BasicSupportEmpty support = new BasicSupportEmpty(); - char openDoubleQuote = Instance.getTrans() + char openDoubleQuote = Instance.getInstance().getTrans() .getCharacter(StringId.OPEN_DOUBLE_QUOTE); - char closeDoubleQuote = Instance.getTrans() + char closeDoubleQuote = Instance.getInstance().getTrans() .getCharacter(StringId.CLOSE_DOUBLE_QUOTE); String content = null; diff --combined test/BasicSupportUtilitiesTest.java index 4e34891,9cec220..9cec220 --- a/test/BasicSupportUtilitiesTest.java +++ b/test/BasicSupportUtilitiesTest.java @@@ -23,14 -23,10 +23,10 @@@ import be.nikiroo.utils.test.TestLaunch class BasicSupportUtilitiesTest extends TestLauncher { // quote chars - private char openQuote = Instance.getTrans().getCharacter( - StringId.OPEN_SINGLE_QUOTE); - private char closeQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_SINGLE_QUOTE); - private char openDoubleQuote = Instance.getTrans().getCharacter( - StringId.OPEN_DOUBLE_QUOTE); - private char closeDoubleQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_DOUBLE_QUOTE); + private char openQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_SINGLE_QUOTE); + private char closeQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_SINGLE_QUOTE); + private char openDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.OPEN_DOUBLE_QUOTE); + private char closeDoubleQuote = Instance.getInstance().getTrans().getCharacter(StringId.CLOSE_DOUBLE_QUOTE); public BasicSupportUtilitiesTest(String[] args) { super("BasicSupportUtilities", args); @@@ -229,9 -225,9 +225,9 @@@ public void test() throws Exception { BasicSupportParaPublic support = new BasicSupportParaPublic(); - char openDoubleQuote = Instance.getTrans() + char openDoubleQuote = Instance.getInstance().getTrans() .getCharacter(StringId.OPEN_DOUBLE_QUOTE); - char closeDoubleQuote = Instance.getTrans() + char closeDoubleQuote = Instance.getInstance().getTrans() .getCharacter(StringId.CLOSE_DOUBLE_QUOTE); String content = null; diff --combined test/ConversionTest.java index 607f49b,d730b3b..d730b3b --- a/test/ConversionTest.java +++ b/test/ConversionTest.java @@@ -138,8 -138,8 +138,8 @@@ class ConversionTest extends TestLaunch BasicOutput.OutputType type) throws Exception { final List<String> errors = new ArrayList<String>(); - TraceHandler previousTraceHandler = Instance.getTraceHandler(); - Instance.setTraceHandler(new TraceHandler(true, true, 0) { + TraceHandler previousTraceHandler = Instance.getInstance().getTraceHandler(); + Instance.getInstance().setTraceHandler(new TraceHandler(true, true, 0) { @Override public void error(String message) { errors.add(message); @@@ -178,7 -178,7 +178,7 @@@ return target; } finally { - Instance.setTraceHandler(previousTraceHandler); + Instance.getInstance().setTraceHandler(previousTraceHandler); } } diff --combined test/LibraryTest.java index cf85b71,da44438..da44438 --- a/test/LibraryTest.java +++ b/test/LibraryTest.java @@@ -43,7 -43,7 +43,7 @@@ class LibraryTest extends TestLauncher addTest(new TestCase("getList") { @Override public void test() throws Exception { - List<MetaData> metas = lib.getList(); + List<MetaData> metas = lib.getList().getMetas(); assertEquals(errMess, Arrays.asList(), titlesAsList(metas)); } @@@ -55,7 -55,7 +55,7 @@@ lib.save(story(luid1, name1, source1, author1), luid1, null); - List<MetaData> metas = lib.getList(); + List<MetaData> metas = lib.getList().getMetas(); assertEquals(errMess, Arrays.asList(name1), titlesAsList(metas)); } @@@ -69,14 -69,14 +69,14 @@@ lib.save(story(luid2, name2, source2, author1), luid2, null); - metas = lib.getList(); + metas = lib.getList().getMetas(); assertEquals(errMess, Arrays.asList(name1, name2), titlesAsList(metas)); lib.save(story(luid3, name3, source2, author1), luid3, null); - metas = lib.getList(); + metas = lib.getList().getMetas(); assertEquals(errMess, Arrays.asList(name1, name2, name3), titlesAsList(metas)); @@@ -90,7 -90,7 +90,7 @@@ lib.save(story(luid3, name3ex, source2, author2), luid3, null); - List<MetaData> metas = lib.getList(); + List<MetaData> metas = lib.getList().getMetas(); assertEquals(errMess, Arrays.asList(name1, name2, name3ex), titlesAsList(metas)); @@@ -100,7 -100,7 +100,7 @@@ addTest(new TestCase("getList with results") { @Override public void test() throws Exception { - List<MetaData> metas = lib.getList(); + List<MetaData> metas = lib.getList().getMetas(); assertEquals(3, metas.size()); } }); @@@ -110,13 -110,13 +110,13 @@@ public void test() throws Exception { List<MetaData> metas = null; - metas = lib.getListBySource(source1); + metas = lib.getList().filter(source1, null, null); assertEquals(1, metas.size()); - metas = lib.getListBySource(source2); + metas = lib.getList().filter(source2, null, null); assertEquals(2, metas.size()); - metas = lib.getListBySource(null); + metas = lib.getList().filter((String)null, null, null); assertEquals(3, metas.size()); } }); @@@ -126,13 -126,13 +126,13 @@@ public void test() throws Exception { List<MetaData> metas = null; - metas = lib.getListByAuthor(author1); + metas = lib.getList().filter(null, author1, null); assertEquals(2, metas.size()); - metas = lib.getListByAuthor(author2); + metas = lib.getList().filter(null, author2, null); assertEquals(1, metas.size()); - metas = lib.getListByAuthor(null); + metas = lib.getList().filter((String)null, null, null); assertEquals(3, metas.size()); } }); @@@ -144,13 -144,13 +144,13 @@@ lib.changeSource(luid3, source1, null); - metas = lib.getListBySource(source1); + metas = lib.getList().filter(source1, null, null); assertEquals(2, metas.size()); - metas = lib.getListBySource(source2); + metas = lib.getList().filter(source2, null, null); assertEquals(1, metas.size()); - metas = lib.getListBySource(null); + metas = lib.getList().filter((String)null, null, null); assertEquals(3, metas.size()); } }); @@@ -164,13 -164,13 +164,13 @@@ lib.save(story(luid3, "My story 3", source2, author2), luid3, null); - metas = lib.getListBySource(source1); + metas = lib.getList().filter(source1, null, null); assertEquals(1, metas.size()); - metas = lib.getListBySource(source2); + metas = lib.getList().filter(source2, null, null); assertEquals(2, metas.size()); - metas = lib.getListBySource(null); + metas = lib.getList().filter((String)null, null, null); assertEquals(3, metas.size()); } }); diff --combined test/Test.java index d772561,5ec24a4..5ec24a4 --- a/test/Test.java +++ b/test/Test.java @@@ -51,7 -51,7 +51,7 @@@ public class Test extends TestLauncher */ public Test(String[] args, boolean urlsAllowed) throws IOException { super("Fanfix", args); - Instance.setTraceHandler(null); + Instance.getInstance().setTraceHandler(null); addSeries(new BasicSupportUtilitiesTest(args)); addSeries(new BasicSupportDeprecatedTest(args)); addSeries(new LibraryTest(args)); @@@ -106,7 -106,7 +106,7 @@@ // Only download files if allowed: boolean offline = new File("test/OFFLINE").exists(); - Instance.getCache().setOffline(offline); + Instance.getInstance().getCache().setOffline(offline); @@@ -126,7 -126,7 +126,7 @@@ System.setProperty("CONFIG_DIR", tmpConfig.getAbsolutePath()); Instance.init(true); - Instance.getCache().setOffline(offline); + Instance.getInstance().getCache().setOffline(offline); TestLauncher tests = new Test(args, urlsAllowed); tests.setDetails(verbose); @@@ -140,7 -140,7 +140,7 @@@ tempFiles.close(); // This is usually done in Fanfix.Main: - Instance.getTempFiles().close(); + Instance.getInstance().getTempFiles().close(); } System.exit(result);