From 68e2c6d20049d713de1bd31b749450b2f60d8340 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Sun, 2 Jul 2017 13:22:14 +0200 Subject: [PATCH] Dependency fix + Local/Remote Library support - Fix nikiroo-utils (a bad file was used for version 1.6.1) - Implement a new remote Library and rework the Library class - Add unit tests for the Library --- libs/nikiroo-utils-1.6.1-sources.jar | Bin 70411 -> 70411 bytes src/be/nikiroo/fanfix/BasicLibrary.java | 488 ++++++++++++ src/be/nikiroo/fanfix/Instance.java | 12 +- src/be/nikiroo/fanfix/Library.java | 710 ------------------ src/be/nikiroo/fanfix/LocalLibrary.java | 362 +++++++++ src/be/nikiroo/fanfix/Main.java | 21 +- src/be/nikiroo/fanfix/RemoteLibrary.java | 108 ++- .../nikiroo/fanfix/RemoteLibraryServer.java | 6 +- src/be/nikiroo/fanfix/package-info.java | 2 +- src/be/nikiroo/fanfix/reader/BasicReader.java | 32 +- src/be/nikiroo/fanfix/reader/LocalReader.java | 28 +- .../fanfix/reader/LocalReaderFrame.java | 16 +- src/be/nikiroo/fanfix/reader/TuiReader.java | 2 +- .../fanfix/reader/TuiReaderStoryWindow.java | 14 +- src/be/nikiroo/fanfix/test/LibraryTest.java | 32 +- 15 files changed, 1013 insertions(+), 820 deletions(-) create mode 100644 src/be/nikiroo/fanfix/BasicLibrary.java delete mode 100644 src/be/nikiroo/fanfix/Library.java create mode 100644 src/be/nikiroo/fanfix/LocalLibrary.java diff --git a/libs/nikiroo-utils-1.6.1-sources.jar b/libs/nikiroo-utils-1.6.1-sources.jar index f3b1578e33be4dce4635d0b11b11efd27c51ce98..66f67343245d71b1cfe99b4603ea6d60ccb162ae 100644 GIT binary patch delta 49 zcmeBP#?rlvg(tw9nMH(wgM)*iDr+K-HB(hqqvuvnMiUkwYkEBkqa~2uzKex1kPiSl C + * Each {@link Story} object will be associated with a (local to the library) + * unique ID, the LUID, which will be used to identify the {@link Story}. + *

+ * Most of the {@link BasicLibrary} functions work on a partial (cover + * MAY not be included) {@link MetaData} object. + * + * @author niki + */ +abstract public class BasicLibrary { + /** + * Retrieve the main {@link File} corresponding to the given {@link Story}, + * which can be passed to an external reader or instance. + *

+ * Do NOT alter this file. + * + * @param luid + * the Library UID of the story + * + * @return the corresponding {@link Story} + */ + public abstract File getFile(String luid); + + /** + * Return the cover image associated to this story. + * + * @param luid + * the Library UID of the story + * + * @return the cover image + */ + public abstract BufferedImage getCover(String luid); + + /** + * Return the list of stories (represented by their {@link MetaData}, which + * MAY not have the cover included). + * + * @param pg + * the optional {@link Progress} + * + * @return the list (can be empty but not NULL) + */ + protected abstract List getMetas(Progress pg); + + /** + * Invalidate the {@link Story} cache (when the content should be re-read + * because it was changed). + */ + protected abstract void clearCache(); + + /** + * Return the next LUID that can be used. + * + * @return the next luid + */ + protected abstract int getNextId(); + + /** + * Delete the target {@link Story}. + * + * @param luid + * the LUID of the {@link Story} + * + * @throws IOException + * in case of I/O error or if the {@link Story} wa not found + */ + protected abstract void doDelete(String luid) throws IOException; + + /** + * Actually save the story to the back-end. + * + * @param story + * the {@link Story} to save + * @param pg + * the optional {@link Progress} + * + * @return the saved {@link Story} (which may have changed, especially + * regarding the {@link MetaData}) + * + * @throws IOException + * in case of I/O error + */ + protected abstract Story doSave(Story story, Progress pg) + throws IOException; + + /** + * Refresh the {@link BasicLibrary}, that is, make sure all stories are + * loaded. + * + * @param full + * force the full content of the stories to be loaded, not just + * the {@link MetaData} + * + * @param pg + * the optional progress reporter + */ + public void refresh(boolean full, Progress pg) { + if (full) { + // TODO: progress + List metas = getMetas(pg); + for (MetaData meta : metas) { + getStory(meta.getLuid(), null); + } + } else { + getMetas(pg); + } + } + + /** + * List all the known types (sources) of stories. + * + * @return the sources + */ + public synchronized List getSources() { + List list = new ArrayList(); + for (MetaData meta : getMetas(null)) { + String storySource = meta.getSource(); + if (!list.contains(storySource)) { + list.add(storySource); + } + } + + Collections.sort(list); + return list; + } + + /** + * List all the known authors of stories. + * + * @return the authors + */ + public synchronized List getAuthors() { + List list = new ArrayList(); + for (MetaData meta : getMetas(null)) { + String storyAuthor = meta.getAuthor(); + if (!list.contains(storyAuthor)) { + list.add(storyAuthor); + } + } + + Collections.sort(list); + return list; + } + + /** + * List all the stories in the {@link BasicLibrary}. + *

+ * Cover images not included. + * + * @return the stories + */ + public synchronized List getList() { + 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 + */ + public synchronized List getListBySource(String type) { + 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 + */ + public synchronized List getListByAuthor(String author) { + 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; + } + + /** + * Retrieve a {@link MetaData} corresponding to the given {@link Story}, + * cover image MAY not be included. + * + * @param luid + * the Library UID of the story + * + * @return the corresponding {@link Story} + */ + public synchronized MetaData getInfo(String luid) { + if (luid != null) { + for (MetaData meta : getMetas(null)) { + if (luid.equals(meta.getLuid())) { + return meta; + } + } + } + + return null; + } + + /** + * Retrieve a specific {@link Story}. + * + * @param luid + * the Library UID of the story + * @param pg + * the optional progress reporter + * + * @return the corresponding {@link Story} or NULL if not found + */ + public synchronized Story getStory(String luid, Progress pg) { + // TODO: pg + if (pg == null) { + pg = new Progress(); + } + + Story story = null; + for (MetaData meta : getMetas(null)) { + if (meta.getLuid().equals(luid)) { + File file = getFile(luid); + try { + SupportType type = SupportType.valueOfAllOkUC(meta + .getType()); + URL url = file.toURI().toURL(); + if (type != null) { + story = BasicSupport.getSupport(type).process(url, pg); + } else { + throw new IOException("Unknown type: " + meta.getType()); + } + } catch (IOException e) { + // We should not have not-supported files in the + // library + Instance.syserr(new IOException( + "Cannot load file from library: " + file, e)); + } finally { + pg.done(); + } + + break; + } + } + + return story; + } + + /** + * Import the {@link Story} at the given {@link URL} into the + * {@link BasicLibrary}. + * + * @param url + * the {@link URL} to import + * @param pg + * the optional progress reporter + * + * @return the imported {@link Story} + * + * @throws IOException + * in case of I/O error + */ + public Story imprt(URL url, Progress pg) throws IOException { + BasicSupport support = BasicSupport.getSupport(url); + if (support == null) { + throw new IOException("URL not supported: " + url.toString()); + } + + return save(support.process(url, pg), null); + } + + /** + * Export the {@link Story} to the given target in the given format. + * + * @param luid + * the {@link Story} ID + * @param type + * the {@link OutputType} to transform it to + * @param target + * the target to save to + * @param pg + * the optional progress reporter + * + * @return the saved resource (the main saved {@link File}) + * + * @throws IOException + * in case of I/O error + */ + public File export(String luid, OutputType type, String target, Progress pg) + throws IOException { + Progress pgGetStory = new Progress(); + Progress pgOut = new Progress(); + if (pg != null) { + pg.setMax(2); + pg.addProgress(pgGetStory, 1); + pg.addProgress(pgOut, 1); + } + + BasicOutput out = BasicOutput.getOutput(type, true); + if (out == null) { + throw new IOException("Output type not supported: " + type); + } + + Story story = getStory(luid, pgGetStory); + if (story == null) { + throw new IOException("Cannot find story to export: " + luid); + } + + return out.process(story, target, pgOut); + } + + /** + * Save a {@link Story} to the {@link BasicLibrary}. + * + * @param story + * the {@link Story} to save + * @param pg + * the optional progress reporter + * + * @return the same {@link Story}, whose LUID may have changed + * + * @throws IOException + * in case of I/O error + */ + public Story save(Story story, Progress pg) throws IOException { + return save(story, null, pg); + } + + /** + * Save a {@link Story} to the {@link BasicLibrary} -- the LUID must + * be correct, or NULL to get the next free one. + *

+ * Will override any previous {@link Story} with the same LUID. + * + * @param story + * the {@link Story} to save + * @param luid + * the correct LUID or NULL to get the next free one + * @param pg + * the optional progress reporter + * + * @return the same {@link Story}, whose LUID may have changed + * + * @throws IOException + * in case of I/O error + */ + public synchronized Story save(Story story, String luid, Progress pg) + throws IOException { + // Do not change the original metadata, but change the original story + MetaData meta = story.getMeta().clone(); + story.setMeta(meta); + + if (luid == null || luid.isEmpty()) { + meta.setLuid(String.format("%03d", getNextId())); + } else { + meta.setLuid(luid); + } + + if (getInfo(luid) != null) { + delete(luid); + } + doSave(story, pg); + + clearCache(); + + return story; + } + + /** + * Delete the given {@link Story} from this {@link BasicLibrary}. + * + * @param luid + * the LUID of the target {@link Story} + * + * @throws IOException + * in case of I/O error + */ + public synchronized void delete(String luid) throws IOException { + doDelete(luid); + clearCache(); + } + + /** + * Change the type (source) of the given {@link Story}. + * + * @param luid + * the {@link Story} LUID + * @param newSource + * the new source + * @param pg + * the optional progress reporter + * + * @throws IOException + * in case of I/O error or if the {@link Story} was not found + */ + public synchronized void changeSource(String luid, String newSource, + Progress pg) throws IOException { + MetaData meta = getInfo(luid); + if (meta == null) { + throw new IOException("Story not found: " + luid); + } + + meta.setSource(newSource); + saveMeta(meta, pg); + } + + /** + * Save back the current state of the {@link MetaData} (LUID MUST NOT + * change) for this {@link Story}. + *

+ * By default, delete the old {@link Story} then recreate a new + * {@link Story}. + *

+ * Note that this behaviour can lead to data loss. + * + * @param meta + * the new {@link MetaData} (LUID MUST NOT change) + * @param pg + * the optional {@link Progress} + * + * @throws IOException + * in case of I/O error or if the {@link Story} was not found + */ + protected synchronized void saveMeta(MetaData meta, Progress pg) + throws IOException { + if (pg == null) { + pg = new Progress(); + } + + Progress pgGet = new Progress(); + Progress pgSet = new Progress(); + pg.addProgress(pgGet, 50); + pg.addProgress(pgSet, 50); + + Story story = getStory(meta.getLuid(), pgGet); + if (story == null) { + throw new IOException("Story not found: " + meta.getLuid()); + } + + delete(meta.getLuid()); + + story.setMeta(meta); + save(story, meta.getLuid(), pgSet); + + pg.done(); + } +} diff --git a/src/be/nikiroo/fanfix/Instance.java b/src/be/nikiroo/fanfix/Instance.java index 42e141e..f9060f4 100644 --- a/src/be/nikiroo/fanfix/Instance.java +++ b/src/be/nikiroo/fanfix/Instance.java @@ -23,7 +23,7 @@ public class Instance { private static UiConfigBundle uiconfig; private static StringIdBundle trans; private static Cache cache; - private static Library lib; + private static LocalLibrary lib; private static boolean debug; private static File coverDir; private static File readerTmp; @@ -74,7 +74,7 @@ public class Instance { uiconfig = new UiConfigBundle(); trans = new StringIdBundle(getLang()); try { - lib = new Library(getFile(Config.LIBRARY_DIR), + lib = new LocalLibrary(getFile(Config.LIBRARY_DIR), OutputType.INFO_TEXT, OutputType.CBZ); } catch (Exception e) { syserr(new IOException("Cannot create library for directory: " @@ -161,11 +161,11 @@ public class Instance { } /** - * Get the (unique) {@link Library} for the program. + * Get the (unique) {@link LocalLibrary} for the program. * - * @return the {@link Library} + * @return the {@link LocalLibrary} */ - public static Library getLibrary() { + public static BasicLibrary getLibrary() { return lib; } @@ -189,7 +189,7 @@ public class Instance { /** * Return the directory where to store temporary files for the remote - * {@link Library}. + * {@link LocalLibrary}. * * @param host * the remote for this host diff --git a/src/be/nikiroo/fanfix/Library.java b/src/be/nikiroo/fanfix/Library.java deleted file mode 100644 index 3868bd3..0000000 --- a/src/be/nikiroo/fanfix/Library.java +++ /dev/null @@ -1,710 +0,0 @@ -package be.nikiroo.fanfix; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import be.nikiroo.fanfix.bundles.Config; -import be.nikiroo.fanfix.data.MetaData; -import be.nikiroo.fanfix.data.Story; -import be.nikiroo.fanfix.output.BasicOutput; -import be.nikiroo.fanfix.output.BasicOutput.OutputType; -import be.nikiroo.fanfix.output.InfoCover; -import be.nikiroo.fanfix.supported.BasicSupport; -import be.nikiroo.fanfix.supported.BasicSupport.SupportType; -import be.nikiroo.fanfix.supported.InfoReader; -import be.nikiroo.utils.IOUtils; -import be.nikiroo.utils.Progress; - -/** - * Manage a library of Stories: import, export, list. - *

- * Each {@link Story} object will be associated with a (local to the library) - * unique ID, the LUID, which will be used to identify the {@link Story}. - *

- * Most of the {@link Library} functions work on either the LUID or a partial - * (cover not included) {@link MetaData} object. - * - * @author niki - */ -public class Library { - protected File baseDir; - protected boolean localSpeed; - protected Map stories; - - private int lastId; - private OutputType text; - private OutputType image; - - /** - * Create a new {@link Library} with the given backend directory. - * - * @param dir - * the directory where to find the {@link Story} objects - * @param text - * the {@link OutputType} to save the text-focused stories into - * @param image - * the {@link OutputType} to save the images-focused stories into - */ - public Library(File dir, OutputType text, OutputType image) { - this(); - - this.baseDir = dir; - - this.lastId = 0; - this.text = text; - this.image = image; - - dir.mkdirs(); - } - - /** - * Create a new {@link Library} with no link to the local machine. - *

- * Reserved for extensions. - */ - protected Library() { - this.stories = new HashMap(); - } - - /** - * Refresh the {@link Library}, that is, make sure all stories are loaded. - * - * @param pg - * the optional progress reporter - */ - public void refresh(Progress pg) { - getStories(pg); - } - - /** - * List all the known types (sources) of stories. - * - * @return the types - */ - public synchronized List getTypes() { - List list = new ArrayList(); - for (Entry entry : getStories(null).entrySet()) { - String storyType = entry.getKey().getSource(); - if (!list.contains(storyType)) { - list.add(storyType); - } - } - - Collections.sort(list); - return list; - } - - /** - * List all the known authors of stories. - * - * @return the authors - */ - public synchronized List getAuthors() { - List list = new ArrayList(); - for (Entry entry : getStories(null).entrySet()) { - String storyAuthor = entry.getKey().getAuthor(); - if (!list.contains(storyAuthor)) { - list.add(storyAuthor); - } - } - - Collections.sort(list); - return list; - } - - /** - * List all the stories of the given author in the {@link Library}, 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 - */ - public synchronized List getListByAuthor(String author) { - List list = new ArrayList(); - for (Entry entry : getStories(null).entrySet()) { - String storyAuthor = entry.getKey().getAuthor(); - if (author == null || author.equalsIgnoreCase(storyAuthor)) { - list.add(entry.getKey()); - } - } - - Collections.sort(list); - return list; - } - - /** - * List all the stories of the given source type in the {@link Library}, 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 - */ - public synchronized List getListBySource(String type) { - List list = new ArrayList(); - for (MetaData meta : getStories(null).keySet()) { - String storyType = meta.getSource(); - if (type == null || type.equalsIgnoreCase(storyType)) { - list.add(meta); - } - } - - Collections.sort(list); - return list; - } - - /** - * Retrieve a {@link MetaData} corresponding to the given {@link Story}, - * cover image MAY not be included. - * - * @param luid - * the Library UID of the story - * - * @return the corresponding {@link Story} - */ - public synchronized MetaData getInfo(String luid) { - if (luid != null) { - for (Entry entry : getStories(null).entrySet()) { - if (luid.equals(entry.getKey().getLuid())) { - return entry.getKey(); - } - } - } - - return null; - } - - /** - * Retrieve a {@link File} corresponding to the given {@link Story}. - * - * @param luid - * the Library UID of the story - * - * @return the corresponding {@link Story} - */ - public synchronized File getFile(String luid) { - if (luid != null) { - for (Entry entry : getStories(null).entrySet()) { - if (luid.equals(entry.getKey().getLuid())) { - return entry.getValue(); - } - } - } - - return null; - } - - /** - * Return the cover image associated to this story. - * - * @param luid - * the Library UID of the story - * - * @return the cover image - */ - public synchronized BufferedImage getCover(String luid) { - MetaData meta = getInfo(luid); - if (meta != null) { - getFile(luid); // to help remote implementation - try { - File infoFile = new File(getExpectedFile(meta).getPath() - + ".info"); - meta = readMeta(infoFile, true).getKey(); - return meta.getCover(); - } catch (IOException e) { - Instance.syserr(e); - } - } - - return null; - } - - /** - * Retrieve a specific {@link Story}. - * - * @param luid - * the Library UID of the story - * @param pg - * the optional progress reporter - * - * @return the corresponding {@link Story} or NULL if not found - */ - public synchronized Story getStory(String luid, Progress pg) { - if (luid != null) { - for (Entry entry : getStories(null).entrySet()) { - if (luid.equals(entry.getKey().getLuid())) { - MetaData meta = entry.getKey(); - File file = getFile(luid); // to help remote implementation - try { - SupportType type = SupportType.valueOfAllOkUC(meta - .getType()); - URL url = file.toURI().toURL(); - if (type != null) { - return BasicSupport.getSupport(type).process(url, - pg); - } else { - throw new IOException("Unknown type: " - + meta.getType()); - } - } catch (IOException e) { - // We should not have not-supported files in the - // library - Instance.syserr(new IOException( - "Cannot load file from library: " + file, e)); - } - } - } - } - - if (pg != null) { - pg.setMinMax(0, 1); - pg.setProgress(1); - } - - return null; - } - - /** - * Import the {@link Story} at the given {@link URL} into the - * {@link Library}. - * - * @param url - * the {@link URL} to import - * @param pg - * the optional progress reporter - * - * @return the imported {@link Story} - * - * @throws IOException - * in case of I/O error - */ - public Story imprt(URL url, Progress pg) throws IOException { - BasicSupport support = BasicSupport.getSupport(url); - if (support == null) { - throw new IOException("URL not supported: " + url.toString()); - } - - return save(support.process(url, pg), null); - } - - /** - * Export the {@link Story} to the given target in the given format. - * - * @param luid - * the {@link Story} ID - * @param type - * the {@link OutputType} to transform it to - * @param target - * the target to save to - * @param pg - * the optional progress reporter - * - * @return the saved resource (the main saved {@link File}) - * - * @throws IOException - * in case of I/O error - */ - public File export(String luid, OutputType type, String target, Progress pg) - throws IOException { - Progress pgGetStory = new Progress(); - Progress pgOut = new Progress(); - if (pg != null) { - pg.setMax(2); - pg.addProgress(pgGetStory, 1); - pg.addProgress(pgOut, 1); - } - - BasicOutput out = BasicOutput.getOutput(type, true); - if (out == null) { - throw new IOException("Output type not supported: " + type); - } - - Story story = getStory(luid, pgGetStory); - if (story == null) { - throw new IOException("Cannot find story to export: " + luid); - } - - return out.process(story, target, pgOut); - } - - /** - * Save a {@link Story} to the {@link Library}. - * - * @param story - * the {@link Story} to save - * @param pg - * the optional progress reporter - * - * @return the same {@link Story}, whose LUID may have changed - * - * @throws IOException - * in case of I/O error - */ - public Story save(Story story, Progress pg) throws IOException { - return save(story, null, pg); - } - - /** - * Save a {@link Story} to the {@link Library} -- the LUID must be - * correct, or NULL to get the next free one. - * - * @param story - * the {@link Story} to save - * @param luid - * the correct LUID or NULL to get the next free one - * @param pg - * the optional progress reporter - * - * @return the same {@link Story}, whose LUID may have changed - * - * @throws IOException - * in case of I/O error - */ - public synchronized Story save(Story story, String luid, Progress pg) - throws IOException { - // Do not change the original metadata, but change the original story - MetaData key = story.getMeta().clone(); - story.setMeta(key); - - if (luid == null || luid.isEmpty()) { - getStories(null); // refresh lastId if needed - key.setLuid(String.format("%03d", (++lastId))); - } else { - key.setLuid(luid); - } - - getExpectedDir(key.getSource()).mkdirs(); - if (!getExpectedDir(key.getSource()).exists()) { - throw new IOException("Cannot create library dir"); - } - - OutputType out; - if (key != null && key.isImageDocument()) { - out = image; - } else { - out = text; - } - - BasicOutput it = BasicOutput.getOutput(out, true); - it.process(story, getExpectedFile(key).getPath(), pg); - - // empty cache - stories.clear(); - - return story; - } - - /** - * Delete the given {@link Story} from this {@link Library}. - * - * @param luid - * the LUID of the target {@link Story} - * - * @return TRUE if it was deleted - */ - public synchronized boolean delete(String luid) { - boolean ok = false; - - List files = getFiles(luid); - if (!files.isEmpty()) { - for (File file : files) { - IOUtils.deltree(file); - } - - ok = true; - - // clear cache - stories.clear(); - } - - return ok; - } - - /** - * Change the type (source) of the given {@link Story}. - * - * @param luid - * the {@link Story} LUID - * @param newType - * the new type - * - * @return TRUE if the {@link Story} was found - */ - public synchronized boolean changeType(String luid, String newType) { - MetaData meta = getInfo(luid); - if (meta != null) { - meta.setSource(newType); - File newDir = getExpectedDir(meta.getSource()); - if (!newDir.exists()) { - newDir.mkdir(); - } - - List files = getFiles(luid); - for (File file : files) { - if (file.getName().endsWith(".info")) { - try { - String name = file.getName().replaceFirst("\\.info$", - ""); - InfoCover.writeInfo(newDir, name, meta); - file.delete(); - } catch (IOException e) { - Instance.syserr(e); - } - } else { - file.renameTo(new File(newDir, file.getName())); - } - } - - // clear cache - stories.clear(); - - return true; - } - - return false; - } - - /** - * The library is accessed locally or at local speed (for operations like - * {@link Library#getFile(String)}). - *

- * It could be cached, too, it is only about the access speed. - * - * @return TRUE if it is accessed locally - */ - public boolean isLocalSpeed() { - return localSpeed; - } - - /** - * Return the list of files/dirs on disk for this {@link Story}. - *

- * If the {@link Story} is not found, and empty list is returned. - * - * @param luid - * the {@link Story} LUID - * - * @return the list of {@link File}s - */ - private List getFiles(String luid) { - List files = new ArrayList(); - - MetaData meta = getInfo(luid); - File file = getFile(luid); // to help remote implementation - - if (file != null) { - files.add(file); - - String readerExt = getOutputType(meta).getDefaultExtension(true); - String fileExt = getOutputType(meta).getDefaultExtension(false); - - String path = file.getAbsolutePath(); - if (readerExt != null && !readerExt.equals(fileExt)) { - path = path.substring(0, path.length() - readerExt.length()) - + fileExt; - file = new File(path); - - if (file.exists()) { - files.add(file); - } - } - - File infoFile = new File(path + ".info"); - if (!infoFile.exists()) { - infoFile = new File(path.substring(0, - path.length() - fileExt.length()) - + ".info"); - } - - if (infoFile.exists()) { - files.add(infoFile); - } - - String coverExt = "." - + Instance.getConfig().getString(Config.IMAGE_FORMAT_COVER); - File coverFile = new File(path + coverExt); - if (!coverFile.exists()) { - coverFile = new File(path.substring(0, - path.length() - fileExt.length()) - + coverExt); - } - - if (coverFile.exists()) { - files.add(coverFile); - } - } - - return files; - } - - /** - * The directory (full path) where the {@link Story} related to this - * {@link MetaData} should be located on disk. - * - * @param type - * the type (source) - * - * @return the target directory - */ - private File getExpectedDir(String type) { - String source = type.replaceAll("[^a-zA-Z0-9._+-]", "_"); - return new File(baseDir, source); - } - - /** - * The target (full path) where the {@link Story} related to this - * {@link MetaData} should be located on disk. - * - * @param key - * the {@link Story} {@link MetaData} - * - * @return the target - */ - private File getExpectedFile(MetaData key) { - String title = key.getTitle(); - if (title == null) { - title = ""; - } - title = title.replaceAll("[^a-zA-Z0-9._+-]", "_"); - return new File(getExpectedDir(key.getSource()), key.getLuid() + "_" - + title); - } - - /** - * Return all the known stories in this {@link Library} object. - * - * @param pg - * the optional progress reporter - * - * @return the stories - */ - protected synchronized Map getStories(Progress pg) { - if (pg == null) { - pg = new Progress(); - } else { - pg.setMinMax(0, 100); - } - - if (stories.isEmpty()) { - lastId = 0; - - File[] dirs = baseDir.listFiles(new FileFilter() { - public boolean accept(File file) { - return file != null && file.isDirectory(); - } - }); - - Progress pgDirs = new Progress(0, 100 * dirs.length); - pg.addProgress(pgDirs, 100); - - for (File dir : dirs) { - File[] files = dir.listFiles(new FileFilter() { - public boolean accept(File file) { - return file != null - && file.getPath().toLowerCase() - .endsWith(".info"); - } - }); - - Progress pgFiles = new Progress(0, files.length); - pgDirs.addProgress(pgFiles, 100); - pgDirs.setName("Loading from: " + dir.getName()); - - for (File file : files) { - pgFiles.setName(file.getName()); - try { - Entry entry = readMeta(file, false); - try { - int id = Integer.parseInt(entry.getKey().getLuid()); - if (id > lastId) { - lastId = id; - } - - stories.put(entry.getKey(), entry.getValue()); - } catch (Exception e) { - // not normal!! - throw new IOException( - "Cannot understand the LUID of " - + file.getPath() + ": " - + entry.getKey().getLuid(), e); - } - } catch (IOException e) { - // We should not have not-supported files in the - // library - Instance.syserr(new IOException( - "Cannot load file from library: " - + file.getPath(), e)); - } - pgFiles.add(1); - } - - pgFiles.setName(null); - } - - pgDirs.setName("Loading directories"); - } - - return stories; - } - - private Entry readMeta(File infoFile, boolean withCover) - throws IOException { - - final MetaData meta = InfoReader.readMeta(infoFile, withCover); - - // Replace .info with whatever is needed: - String path = infoFile.getPath(); - path = path.substring(0, path.length() - ".info".length()); - - String newExt = getOutputType(meta).getDefaultExtension(true); - - File targetFile = new File(path + newExt); - - final File ffile = targetFile; - return new Entry() { - public File setValue(File value) { - return null; - } - - public File getValue() { - return ffile; - } - - public MetaData getKey() { - return meta; - } - }; - } - - /** - * Return the {@link OutputType} for this {@link Story}. - * - * @param meta - * the {@link Story} {@link MetaData} - * - * @return the type - */ - private OutputType getOutputType(MetaData meta) { - if (meta != null && meta.isImageDocument()) { - return image; - } else { - return text; - } - } -} diff --git a/src/be/nikiroo/fanfix/LocalLibrary.java b/src/be/nikiroo/fanfix/LocalLibrary.java new file mode 100644 index 0000000..35f13f0 --- /dev/null +++ b/src/be/nikiroo/fanfix/LocalLibrary.java @@ -0,0 +1,362 @@ +package be.nikiroo.fanfix; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import be.nikiroo.fanfix.bundles.Config; +import be.nikiroo.fanfix.data.MetaData; +import be.nikiroo.fanfix.data.Story; +import be.nikiroo.fanfix.output.BasicOutput; +import be.nikiroo.fanfix.output.BasicOutput.OutputType; +import be.nikiroo.fanfix.output.InfoCover; +import be.nikiroo.fanfix.supported.InfoReader; +import be.nikiroo.utils.IOUtils; +import be.nikiroo.utils.Progress; + +/** + * This {@link BasicLibrary} will store the stories locally on disk. + * + * @author niki + */ +public class LocalLibrary extends BasicLibrary { + private int lastId; + private Map stories; // Files: [ infoFile, TargetFile ] + + private File baseDir; + private OutputType text; + private OutputType image; + + /** + * Create a new {@link LocalLibrary} with the given back-end directory. + * + * @param baseDir + * the directory where to find the {@link Story} objects + * @param text + * the {@link OutputType} to save the text-focused stories into + * @param image + * the {@link OutputType} to save the images-focused stories into + */ + public LocalLibrary(File baseDir, OutputType text, OutputType image) { + this.baseDir = baseDir; + this.text = text; + this.image = image; + + this.lastId = 0; + this.stories = null; + + baseDir.mkdirs(); + } + + @Override + protected List getMetas(Progress pg) { + return new ArrayList(getStories(pg).keySet()); + } + + @Override + public File getFile(String luid) { + File[] files = getStories(null).get(getInfo(luid)); + if (files != null) { + return files[1]; + } + + return null; + } + + @Override + public BufferedImage getCover(String luid) { + MetaData meta = getInfo(luid); + if (meta != null) { + File[] files = getStories(null).get(meta); + if (files != null) { + File infoFile = files[0]; + + try { + meta = InfoReader.readMeta(infoFile, true); + return meta.getCover(); + } catch (IOException e) { + Instance.syserr(e); + } + } + } + + return null; + } + + @Override + protected void clearCache() { + stories = null; + } + + @Override + protected synchronized int getNextId() { + return ++lastId; + } + + @Override + protected void doDelete(String luid) throws IOException { + for (File file : getRelatedFiles(luid)) { + // TODO: throw an IOException if we cannot delete the files? + IOUtils.deltree(file); + } + } + + @Override + protected Story doSave(Story story, Progress pg) throws IOException { + MetaData meta = story.getMeta(); + + File expectedTarget = getExpectedFile(meta); + expectedTarget.getParentFile().mkdirs(); + + BasicOutput it = BasicOutput.getOutput(getOutputType(meta), true); + it.process(story, expectedTarget.getPath(), pg); + + return story; + } + + @Override + protected synchronized void saveMeta(MetaData meta, Progress pg) + throws IOException { + File newDir = getExpectedDir(meta.getSource()); + if (!newDir.exists()) { + newDir.mkdir(); + } + + List relatedFiles = getRelatedFiles(meta.getLuid()); + for (File relatedFile : relatedFiles) { + // TODO: this is not safe at all. + // We should copy all the files THEN delete them + // Maybe also adding some rollback cleanup if possible + if (relatedFile.getName().endsWith(".info")) { + try { + String name = relatedFile.getName().replaceFirst( + "\\.info$", ""); + InfoCover.writeInfo(newDir, name, meta); + relatedFile.delete(); + } catch (IOException e) { + Instance.syserr(e); + } + } else { + relatedFile.renameTo(new File(newDir, relatedFile.getName())); + } + } + + clearCache(); + } + + /** + * Return the {@link OutputType} for this {@link Story}. + * + * @param meta + * the {@link Story} {@link MetaData} + * + * @return the type + */ + private OutputType getOutputType(MetaData meta) { + if (meta != null && meta.isImageDocument()) { + return image; + } else { + return text; + } + } + + /** + * Get the target {@link File} related to the given .info + * {@link File} and {@link MetaData}. + * + * @param meta + * the meta + * @param infoFile + * the .info {@link File} + * + * @return the target {@link File} + */ + private File getTargetFile(MetaData meta, File infoFile) { + // Replace .info with whatever is needed: + String path = infoFile.getPath(); + path = path.substring(0, path.length() - ".info".length()); + String newExt = getOutputType(meta).getDefaultExtension(true); + + return new File(path + newExt); + } + + /** + * The target (full path) where the {@link Story} related to this + * {@link MetaData} should be located on disk for a new {@link Story}. + * + * @param key + * the {@link Story} {@link MetaData} + * + * @return the target + */ + private File getExpectedFile(MetaData key) { + String title = key.getTitle(); + if (title == null) { + title = ""; + } + title = title.replaceAll("[^a-zA-Z0-9._+-]", "_"); + return new File(getExpectedDir(key.getSource()), key.getLuid() + "_" + + title); + } + + /** + * The directory (full path) where the new {@link Story} related to this + * {@link MetaData} should be located on disk. + * + * @param type + * the type (source) + * + * @return the target directory + */ + private File getExpectedDir(String type) { + String source = type.replaceAll("[^a-zA-Z0-9._+-]", "_"); + return new File(baseDir, source); + } + + /** + * Return the list of files/directories on disk for this {@link Story}. + *

+ * If the {@link Story} is not found, and empty list is returned. + * + * @param luid + * the {@link Story} LUID + * + * @return the list of {@link File}s + * + * @throws IOException + * if the {@link Story} was not found + */ + private List getRelatedFiles(String luid) throws IOException { + List files = new ArrayList(); + + MetaData meta = getInfo(luid); + if (meta == null) { + throw new IOException("Story not found: " + luid); + } else { + File infoFile = getStories(null).get(meta)[0]; + File targetFile = getStories(null).get(meta)[1]; + + files.add(infoFile); + files.add(targetFile); + + String readerExt = getOutputType(meta).getDefaultExtension(true); + String fileExt = getOutputType(meta).getDefaultExtension(false); + + String path = targetFile.getAbsolutePath(); + if (readerExt != null && !readerExt.equals(fileExt)) { + path = path.substring(0, path.length() - readerExt.length()) + + fileExt; + File relatedFile = new File(path); + + if (relatedFile.exists()) { + files.add(relatedFile); + } + } + + String coverExt = "." + + Instance.getConfig().getString(Config.IMAGE_FORMAT_COVER); + File coverFile = new File(path + coverExt); + if (!coverFile.exists()) { + coverFile = new File(path.substring(0, + path.length() - fileExt.length()) + + coverExt); + } + + if (coverFile.exists()) { + files.add(coverFile); + } + } + + return files; + } + + /** + * Fill the list of stories by reading the content of the local directory + * {@link LocalLibrary#baseDir}. + *

+ * Will use a cached list when possible (see + * {@link BasicLibrary#clearCache()}). + * + * @param pg + * the optional {@link Progress} + * + * @return the list of stories + */ + private synchronized Map getStories(Progress pg) { + if (pg == null) { + pg = new Progress(); + } else { + pg.setMinMax(0, 100); + } + + if (stories == null) { + stories = new HashMap(); + + lastId = 0; + + File[] dirs = baseDir.listFiles(new FileFilter() { + public boolean accept(File file) { + return file != null && file.isDirectory(); + } + }); + + Progress pgDirs = new Progress(0, 100 * dirs.length); + pg.addProgress(pgDirs, 100); + + for (File dir : dirs) { + File[] infoFiles = dir.listFiles(new FileFilter() { + public boolean accept(File file) { + return file != null + && file.getPath().toLowerCase() + .endsWith(".info"); + } + }); + + Progress pgFiles = new Progress(0, infoFiles.length); + pgDirs.addProgress(pgFiles, 100); + pgDirs.setName("Loading from: " + dir.getName()); + + for (File infoFile : infoFiles) { + pgFiles.setName(infoFile.getName()); + try { + MetaData meta = InfoReader.readMeta(infoFile, false); + try { + int id = Integer.parseInt(meta.getLuid()); + if (id > lastId) { + lastId = id; + } + + stories.put(meta, new File[] { infoFile, + getTargetFile(meta, infoFile) }); + } catch (Exception e) { + // not normal!! + throw new IOException( + "Cannot understand the LUID of " + + infoFile + + ": " + + (meta == null ? "[meta is NULL]" + : meta.getLuid()), e); + } + } catch (IOException e) { + // We should not have not-supported files in the + // library + Instance.syserr(new IOException( + "Cannot load file from library: " + infoFile, e)); + } + pgFiles.add(1); + } + + pgFiles.setName(null); + } + + pgDirs.setName("Loading directories"); + } + + return stories; + } +} diff --git a/src/be/nikiroo/fanfix/Main.java b/src/be/nikiroo/fanfix/Main.java index 38f58af..a05df43 100644 --- a/src/be/nikiroo/fanfix/Main.java +++ b/src/be/nikiroo/fanfix/Main.java @@ -181,12 +181,8 @@ public class Main { host = args[i]; } else if (port == null) { port = Integer.parseInt(args[i]); - try { - BasicReader.setDefaultLibrary(new RemoteLibrary(host, - port)); - } catch (IOException e) { - Instance.syserr(e); - } + BasicReader + .setDefaultLibrary(new RemoteLibrary(host, port)); action = MainAction.START; } else { exitCode = 255; @@ -307,7 +303,7 @@ public class Main { } /** - * Import the given resource into the {@link Library}. + * Import the given resource into the {@link LocalLibrary}. * * @param urlString * the resource to import @@ -331,7 +327,8 @@ public class Main { } /** - * Export the {@link Story} from the {@link Library} to the given target. + * Export the {@link Story} from the {@link LocalLibrary} to the given + * target. * * @param luid * the story LUID @@ -364,8 +361,8 @@ public class Main { } /** - * List the stories of the given source from the {@link Library} (unless - * NULL is passed, in which case all stories will be listed). + * List the stories of the given source from the {@link LocalLibrary} + * (unless NULL is passed, in which case all stories will be listed). * * @param source * the source to list the known stories of, or NULL to list all @@ -382,8 +379,8 @@ public class Main { * Start the CLI reader for this {@link Story}. * * @param story - * the LUID of the {@link Story} in the {@link Library} or - * the {@link Story} {@link URL} + * the LUID of the {@link Story} in the {@link LocalLibrary} + * or the {@link Story} {@link URL} * @param chapString * which {@link Chapter} to read (starting at 1), or NULL to get * the {@link Story} description diff --git a/src/be/nikiroo/fanfix/RemoteLibrary.java b/src/be/nikiroo/fanfix/RemoteLibrary.java index cc78be9..5c111ca 100644 --- a/src/be/nikiroo/fanfix/RemoteLibrary.java +++ b/src/be/nikiroo/fanfix/RemoteLibrary.java @@ -1,8 +1,10 @@ package be.nikiroo.fanfix; +import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Story; @@ -11,56 +13,57 @@ import be.nikiroo.utils.Progress; import be.nikiroo.utils.Version; import be.nikiroo.utils.serial.ConnectActionClient; -public class RemoteLibrary extends Library { +/** + * This {@link BasicLibrary} will access a remote server to list the available + * stories, and download the one you try to load to the local directory + * specified in the configuration. + * + * @author niki + */ +public class RemoteLibrary extends BasicLibrary { private String host; private int port; - - private Library lib; - - public RemoteLibrary(String host, int port) throws IOException { + private File baseDir; + + private LocalLibrary lib; + private List metas; + + /** + * Create a {@link RemoteLibrary} linked to the given server. + * + * @param host + * the host to contact or NULL for localhost + * @param port + * the port to contact it on + */ + public RemoteLibrary(String host, int port) { this.host = host; this.port = port; - this.localSpeed = false; this.baseDir = Instance.getRemoteDir(host); this.baseDir.mkdirs(); - this.lib = new Library(baseDir, OutputType.INFO_TEXT, OutputType.CBZ); + this.lib = new LocalLibrary(baseDir, OutputType.INFO_TEXT, + OutputType.CBZ); } @Override - public synchronized Story save(Story story, String luid, Progress pg) - throws IOException { - throw new java.lang.InternalError( - "No write support allowed on remote Libraries"); - } + protected List getMetas(Progress pg) { + // TODO: progress - @Override - public synchronized boolean delete(String luid) { - throw new java.lang.InternalError( - "No write support allowed on remote Libraries"); - } + if (metas == null) { + metas = new ArrayList(); - @Override - public synchronized boolean changeType(String luid, String newType) { - throw new java.lang.InternalError( - "No write support allowed on remote Libraries"); - } - - @Override - protected synchronized Map getStories(Progress pg) { - // TODO: progress - if (stories.isEmpty()) { try { new ConnectActionClient(host, port, true, null) { public void action(Version serverVersion) throws Exception { try { Object rep = send("GET_METADATA *"); for (MetaData meta : (MetaData[]) rep) { - stories.put(meta, null); + metas.add(meta); } } catch (Exception e) { - e.printStackTrace(); + Instance.syserr(e); } } }.connect(); @@ -69,7 +72,7 @@ public class RemoteLibrary extends Library { } } - return stories; + return metas; } @Override @@ -101,9 +104,50 @@ public class RemoteLibrary extends Library { if (file != null) { MetaData meta = getInfo(luid); - stories.put(meta, file); + metas.add(meta); } return file; } + + @Override + public BufferedImage getCover(String luid) { + // Retrieve it from the network if needed: + if (lib.getInfo(luid) == null) { + getFile(luid); + } + + return lib.getCover(luid); + } + + @Override + protected void clearCache() { + metas = null; + lib.clearCache(); + } + + @Override + public synchronized Story save(Story story, String luid, Progress pg) + throws IOException { + throw new java.lang.InternalError( + "No write support allowed on remote Libraries"); + } + + @Override + protected int getNextId() { + throw new java.lang.InternalError( + "No write support allowed on remote Libraries"); + } + + @Override + protected void doDelete(String luid) throws IOException { + throw new java.lang.InternalError( + "No write support allowed on remote Libraries"); + } + + @Override + protected Story doSave(Story story, Progress pg) throws IOException { + throw new java.lang.InternalError( + "No write support allowed on remote Libraries"); + } } diff --git a/src/be/nikiroo/fanfix/RemoteLibraryServer.java b/src/be/nikiroo/fanfix/RemoteLibraryServer.java index 9ecbc41..e47a591 100644 --- a/src/be/nikiroo/fanfix/RemoteLibraryServer.java +++ b/src/be/nikiroo/fanfix/RemoteLibraryServer.java @@ -2,6 +2,7 @@ package be.nikiroo.fanfix; import java.io.File; import java.io.IOException; +import java.util.List; import java.util.Map; import be.nikiroo.fanfix.data.MetaData; @@ -35,9 +36,8 @@ public class RemoteLibraryServer extends Server { if (command != null) { if (command.equals("GET_METADATA")) { if (args != null && args.equals("*")) { - Map stories = Instance.getLibrary() - .getStories(null); - return stories.keySet().toArray(new MetaData[] {}); + List metas = Instance.getLibrary().getMetas(null); + return metas.toArray(new MetaData[] {}); } } else if (command.equals("GET_STORY")) { if (args != null) { diff --git a/src/be/nikiroo/fanfix/package-info.java b/src/be/nikiroo/fanfix/package-info.java index 4423974..eadaa5a 100644 --- a/src/be/nikiroo/fanfix/package-info.java +++ b/src/be/nikiroo/fanfix/package-info.java @@ -3,7 +3,7 @@ * which to retrieve stories, then process them into epub (or other) * files that you can read anywhere. *

- * It has support for a {@link be.nikiroo.fanfix.Library} system, too. + * It has support for a {@link be.nikiroo.fanfix.BasicLibrary} system, too. * * @author niki */ diff --git a/src/be/nikiroo/fanfix/reader/BasicReader.java b/src/be/nikiroo/fanfix/reader/BasicReader.java index 05e3e3d..f8341ae 100644 --- a/src/be/nikiroo/fanfix/reader/BasicReader.java +++ b/src/be/nikiroo/fanfix/reader/BasicReader.java @@ -6,8 +6,9 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import be.nikiroo.fanfix.BasicLibrary; import be.nikiroo.fanfix.Instance; -import be.nikiroo.fanfix.Library; +import be.nikiroo.fanfix.LocalLibrary; import be.nikiroo.fanfix.bundles.Config; import be.nikiroo.fanfix.bundles.UiConfig; import be.nikiroo.fanfix.data.MetaData; @@ -49,10 +50,10 @@ public abstract class BasicReader { } } - private static Library defaultLibrary = Instance.getLibrary(); + private static BasicLibrary defaultLibrary = Instance.getLibrary(); private static ReaderType defaultType = ReaderType.GUI; - private Library lib; + private BasicLibrary lib; private Story story; private ReaderType type; @@ -101,12 +102,12 @@ public abstract class BasicReader { } /** - * The {@link Library} to load the stories from (by default, takes the - * default {@link Library}). + * The {@link LocalLibrary} to load the stories from (by default, takes the + * default {@link LocalLibrary}). * - * @return the {@link Library} + * @return the {@link LocalLibrary} */ - public Library getLibrary() { + public BasicLibrary getLibrary() { if (lib == null) { lib = defaultLibrary; } @@ -115,19 +116,19 @@ public abstract class BasicReader { } /** - * Change the {@link Library} that will be managed by this + * Change the {@link LocalLibrary} that will be managed by this * {@link BasicReader}. * * @param lib - * the new {@link Library} + * the new {@link LocalLibrary} */ - public void setLibrary(Library lib) { + public void setLibrary(LocalLibrary lib) { this.lib = lib; } /** * Create a new {@link BasicReader} for a {@link Story} in the - * {@link Library}. + * {@link LocalLibrary}. * * @param luid * the {@link Story} ID @@ -247,12 +248,13 @@ public abstract class BasicReader { } /** - * Change the default {@link Library} to open with the {@link BasicReader}s. + * Change the default {@link LocalLibrary} to open with the + * {@link BasicReader}s. * * @param lib - * the new {@link Library} + * the new {@link LocalLibrary} */ - public static void setDefaultLibrary(Library lib) { + public static void setDefaultLibrary(BasicLibrary lib) { BasicReader.defaultLibrary = lib; } @@ -285,7 +287,7 @@ public abstract class BasicReader { } // open with external player the related file - public static void open(Library lib, String luid) throws IOException { + public static void open(BasicLibrary lib, String luid) throws IOException { MetaData meta = lib.getInfo(luid); File target = lib.getFile(luid); diff --git a/src/be/nikiroo/fanfix/reader/LocalReader.java b/src/be/nikiroo/fanfix/reader/LocalReader.java index 6c61503..593f58b 100644 --- a/src/be/nikiroo/fanfix/reader/LocalReader.java +++ b/src/be/nikiroo/fanfix/reader/LocalReader.java @@ -13,7 +13,7 @@ import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import be.nikiroo.fanfix.Instance; -import be.nikiroo.fanfix.Library; +import be.nikiroo.fanfix.LocalLibrary; import be.nikiroo.fanfix.VersionCheck; import be.nikiroo.fanfix.bundles.UiConfig; import be.nikiroo.fanfix.data.Story; @@ -25,7 +25,7 @@ import be.nikiroo.utils.ui.UIUtils; class LocalReader extends BasicReader { static private boolean nativeLookLoaded; - private Library localLibrary; + private LocalLibrary localLibrary; public LocalReader() throws IOException { if (!nativeLookLoaded) { @@ -66,7 +66,7 @@ class LocalReader extends BasicReader { key, value), e); } - localLibrary = new Library(dir, text, images); + localLibrary = new LocalLibrary(dir, text, images); } @Override @@ -197,13 +197,21 @@ class LocalReader extends BasicReader { // delete from local reader library void clearLocalReaderCache(String luid) { - localLibrary.delete(luid); + try { + localLibrary.delete(luid); + } catch (IOException e) { + Instance.syserr(e); + } } // delete from main library void delete(String luid) { - localLibrary.delete(luid); - Instance.getLibrary().delete(luid); + try { + localLibrary.delete(luid); + Instance.getLibrary().delete(luid); + } catch (IOException e) { + Instance.syserr(e); + } } // open the given book @@ -218,7 +226,11 @@ class LocalReader extends BasicReader { } void changeType(String luid, String newType) { - localLibrary.changeType(luid, newType); - Instance.getLibrary().changeType(luid, newType); + try { + localLibrary.changeSource(luid, newType, null); + Instance.getLibrary().changeSource(luid, newType, null); + } catch (IOException e) { + Instance.syserr(e); + } } } diff --git a/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java b/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java index edd1fb6..bf94f26 100644 --- a/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java +++ b/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java @@ -34,7 +34,7 @@ import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; import be.nikiroo.fanfix.Instance; -import be.nikiroo.fanfix.Library; +import be.nikiroo.fanfix.LocalLibrary; import be.nikiroo.fanfix.bundles.Config; import be.nikiroo.fanfix.bundles.UiConfig; import be.nikiroo.fanfix.data.MetaData; @@ -71,7 +71,7 @@ class LocalReaderFrame extends JFrame { * * @param reader * the associated {@link LocalReader} to forward some commands - * and access its {@link Library} + * and access its {@link LocalLibrary} * @param type * the type of {@link Story} to load, or NULL for all types */ @@ -125,7 +125,7 @@ class LocalReaderFrame extends JFrame { final String typeF = type; outOfUi(pg, new Runnable() { public void run() { - Instance.getLibrary().refresh(pg); + Instance.getLibrary().refresh(false, pg); invalidate(); setJMenuBar(createMenu()); addBookPane(typeF, true); @@ -151,7 +151,7 @@ class LocalReaderFrame extends JFrame { private void addBookPane(String value, boolean type) { if (value == null) { if (type) { - for (String tt : Instance.getLibrary().getTypes()) { + for (String tt : Instance.getLibrary().getSources()) { if (tt != null) { addBookPane(tt, type); } @@ -312,7 +312,7 @@ class LocalReaderFrame extends JFrame { JMenu sources = new JMenu("Sources"); sources.setMnemonic(KeyEvent.VK_S); - List tt = Instance.getLibrary().getTypes(); + List tt = Instance.getLibrary().getSources(); tt.add(0, null); for (final String type : tt) { JMenuItem item = new JMenuItem(type == null ? "All" : type); @@ -542,7 +542,7 @@ class LocalReaderFrame extends JFrame { List types = new ArrayList(); types.add(null); - types.addAll(Instance.getLibrary().getTypes()); + types.addAll(Instance.getLibrary().getSources()); for (String type : types) { JMenuItem item = new JMenuItem(type == null ? "New type..." : type); @@ -724,7 +724,7 @@ class LocalReaderFrame extends JFrame { } /** - * Import a {@link Story} into the main {@link Library}. + * Import a {@link Story} into the main {@link LocalLibrary}. *

* Should be called inside the UI thread. * @@ -764,7 +764,7 @@ class LocalReaderFrame extends JFrame { } /** - * Actually import the {@link Story} into the main {@link Library}. + * Actually import the {@link Story} into the main {@link LocalLibrary}. *

* Should be called inside the UI thread. * diff --git a/src/be/nikiroo/fanfix/reader/TuiReader.java b/src/be/nikiroo/fanfix/reader/TuiReader.java index d00bbc2..12f0482 100644 --- a/src/be/nikiroo/fanfix/reader/TuiReader.java +++ b/src/be/nikiroo/fanfix/reader/TuiReader.java @@ -4,7 +4,7 @@ import java.io.IOException; import java.util.List; import be.nikiroo.fanfix.Instance; -import be.nikiroo.fanfix.Library; +import be.nikiroo.fanfix.LocalLibrary; import be.nikiroo.fanfix.data.MetaData; class TuiReader extends BasicReader { diff --git a/src/be/nikiroo/fanfix/reader/TuiReaderStoryWindow.java b/src/be/nikiroo/fanfix/reader/TuiReaderStoryWindow.java index 0d47cde..f556a4b 100644 --- a/src/be/nikiroo/fanfix/reader/TuiReaderStoryWindow.java +++ b/src/be/nikiroo/fanfix/reader/TuiReaderStoryWindow.java @@ -12,15 +12,16 @@ import jexer.TLabel; import jexer.TText; import jexer.TWindow; import jexer.event.TResizeEvent; +import be.nikiroo.fanfix.BasicLibrary; import be.nikiroo.fanfix.Instance; -import be.nikiroo.fanfix.Library; +import be.nikiroo.fanfix.LocalLibrary; import be.nikiroo.fanfix.data.Chapter; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Paragraph; import be.nikiroo.fanfix.data.Story; public class TuiReaderStoryWindow extends TWindow { - private Library lib; + private BasicLibrary lib; private MetaData meta; private Story story; private TText textField; @@ -28,14 +29,15 @@ public class TuiReaderStoryWindow extends TWindow { private List navigationButtons; private TLabel chapterName; - public TuiReaderStoryWindow(TApplication app, Library lib, MetaData meta) { + public TuiReaderStoryWindow(TApplication app, BasicLibrary lib, + MetaData meta) { this(app, lib, meta, 0); } - public TuiReaderStoryWindow(TApplication app, Library lib, MetaData meta, - int chapter) { + public TuiReaderStoryWindow(TApplication app, BasicLibrary lib, + MetaData meta, int chapter) { super(app, desc(meta), 0, 0, 60, 18, CENTERED | RESIZABLE); - + this.lib = lib; this.meta = meta; diff --git a/src/be/nikiroo/fanfix/test/LibraryTest.java b/src/be/nikiroo/fanfix/test/LibraryTest.java index 222d9d5..f9d5299 100644 --- a/src/be/nikiroo/fanfix/test/LibraryTest.java +++ b/src/be/nikiroo/fanfix/test/LibraryTest.java @@ -4,7 +4,8 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -import be.nikiroo.fanfix.Library; +import be.nikiroo.fanfix.BasicLibrary; +import be.nikiroo.fanfix.LocalLibrary; import be.nikiroo.fanfix.data.Chapter; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Paragraph; @@ -15,7 +16,7 @@ import be.nikiroo.utils.test.TestCase; import be.nikiroo.utils.test.TestLauncher; public class LibraryTest extends TestLauncher { - private Library lib; + private BasicLibrary lib; private File tmp; public LibraryTest(String[] args) { @@ -34,8 +35,7 @@ public class LibraryTest extends TestLauncher { addTest(new TestCase("getList") { @Override public void test() throws Exception { - // TODO: getList - List metas = lib.getListBySource(null); + List metas = lib.getList(); assertEquals(0, metas.size()); } }); @@ -46,8 +46,7 @@ public class LibraryTest extends TestLauncher { lib.save(story(luid1, "My story 1", source1, author1), luid1, null); - // TODO: getList - List metas = lib.getListBySource(null); + List metas = lib.getList(); assertEquals(1, metas.size()); } }); @@ -60,15 +59,13 @@ public class LibraryTest extends TestLauncher { lib.save(story(luid2, "My story 2", source2, author1), luid2, null); - // TODO: getList - metas = lib.getListBySource(null); + metas = lib.getList(); assertEquals(2, metas.size()); lib.save(story(luid3, "My story 3", source2, author1), luid3, null); - // TODO: getList - metas = lib.getListBySource(null); + metas = lib.getList(); assertEquals(3, metas.size()); } }); @@ -77,11 +74,11 @@ public class LibraryTest extends TestLauncher { @Override public void test() throws Exception { // same luid as a previous one - lib.save(story(luid3, "My story 3", source2, author2), - luid3, null); + lib.save( + story(luid3, "My story 3 [edited]", source2, + author2), luid3, null); - // TODO: getList - List metas = lib.getListBySource(null); + List metas = lib.getList(); assertEquals(3, metas.size()); } }); @@ -89,8 +86,7 @@ public class LibraryTest extends TestLauncher { addTest(new TestCase("getList with results") { @Override public void test() throws Exception { - // TODO: getList - List metas = lib.getListBySource(null); + List metas = lib.getList(); assertEquals(3, metas.size()); } }); @@ -132,7 +128,7 @@ public class LibraryTest extends TestLauncher { public void test() throws Exception { List metas = null; - lib.changeType(luid3, source1); + lib.changeSource(luid3, source1, null); metas = lib.getListBySource(source1); assertEquals(2, metas.size()); @@ -174,7 +170,7 @@ public class LibraryTest extends TestLauncher { tmp.delete(); tmp.mkdir(); - lib = new Library(tmp, OutputType.INFO_TEXT, OutputType.CBZ); + lib = new LocalLibrary(tmp, OutputType.INFO_TEXT, OutputType.CBZ); } @Override -- 2.27.0