Improve remote, fix bugs, update nikiroo-utils
authorNiki Roo <niki@nikiroo.be>
Sun, 26 Nov 2017 15:30:04 +0000 (16:30 +0100)
committerNiki Roo <niki@nikiroo.be>
Sun, 26 Nov 2017 15:30:04 +0000 (16:30 +0100)
changelog.md
libs/nikiroo-utils-2.2.2-sources.jar [moved from libs/nikiroo-utils-2.2.1-sources.jar with 98% similarity]
src/be/nikiroo/fanfix/DataLoader.java
src/be/nikiroo/fanfix/library/BasicLibrary.java
src/be/nikiroo/fanfix/library/CacheLibrary.java
src/be/nikiroo/fanfix/library/LocalLibrary.java
src/be/nikiroo/fanfix/library/RemoteLibrary.java
src/be/nikiroo/fanfix/library/RemoteLibraryServer.java
src/be/nikiroo/fanfix/reader/GuiReaderBook.java
src/be/nikiroo/fanfix/reader/GuiReaderFrame.java

index 3f5efc29c13e5848e74319cea17ed6b9c5a85f8f..0154d4e8b2ccc788d6bc6018deac8c6290572ddb 100644 (file)
@@ -1,5 +1,10 @@
 # Fanfix
 
+# Version WIP
+
+- Bug fixes
+- Remote server/client improvements
+
 ## Version 1.6.2
 
 - Better progress bars
similarity index 98%
rename from libs/nikiroo-utils-2.2.1-sources.jar
rename to libs/nikiroo-utils-2.2.2-sources.jar
index 199beb397df2393d81a4d924bcff803426fab7e3..318670fd7d1ef25318a10d5eeddfb1fcc7914f23 100644 (file)
Binary files a/libs/nikiroo-utils-2.2.1-sources.jar and b/libs/nikiroo-utils-2.2.2-sources.jar differ
index 0f3cad00de0b7a19d11e9ffd95b6421202f7b64a..e4f40a30289548069c9fb6a4c3b4c17d6136bdc2 100644 (file)
@@ -268,6 +268,18 @@ public class DataLoader {
                return cache.load(uniqueID, true, true);
        }
 
+       /**
+        * Remove the given resource from the cache.
+        * 
+        * @param uniqueID
+        *            a unique ID used to locate the cached resource
+        * 
+        * @return TRUE if it was removed
+        */
+       public boolean removeFromCache(String uniqueID) {
+               return cache.remove(uniqueID);
+       }
+
        /**
         * Clean the cache (delete the cached items).
         * 
index b11e1ec5276324743b2ea64fb0697d616567c7d4..d324008f0b4c2294e503d907a25b0edc226ed944 100644 (file)
@@ -319,6 +319,7 @@ abstract public class BasicLibrary {
                                        if (type != null) {
                                                story = BasicSupport.getSupport(type).process(url,
                                                                pgProcess);
+                                               story.setMeta(meta);
                                        } else {
                                                throw new IOException("Unknown type: " + meta.getType());
                                        }
index 40128979ee4dcfdf318272077a99c014ab396097..38fb7b3193e1b13da59e2abc285d2a6bd0461dfa 100644 (file)
@@ -78,9 +78,7 @@ public class CacheLibrary extends BasicLibrary {
                        try {
                                cacheLib.imprt(lib, luid, pgImport);
                                pgImport.done();
-                               Story story = cacheLib.getStory(luid, pgGet);
-                               metas.remove(story.getMeta());
-                               metas.add(story.getMeta());
+                               clearCache();
                        } catch (IOException e) {
                                Instance.syserr(e);
                        }
@@ -98,14 +96,26 @@ public class CacheLibrary extends BasicLibrary {
 
        @Override
        public BufferedImage getCover(final String luid) {
-               // Retrieve it from the cache if possible:
                if (isCached(luid)) {
                        return cacheLib.getCover(luid);
                }
 
+               // We could update the cache here, but it's not easy
                return lib.getCover(luid);
        }
 
+       @Override
+       public BufferedImage getSourceCover(String source) {
+               // no cache for the source cover
+               return lib.getSourceCover(source);
+       }
+
+       @Override
+       public void setSourceCover(String source, String luid) {
+               lib.setSourceCover(source, luid);
+               cacheLib.setSourceCover(source, getSourceCover(source));
+       }
+
        @Override
        protected void clearCache() {
                metas = null;
@@ -123,17 +133,13 @@ public class CacheLibrary extends BasicLibrary {
 
        @Override
        public synchronized void delete(String luid) throws IOException {
-               cacheLib.delete(luid);
+               if (isCached(luid)) {
+                       cacheLib.delete(luid);
+               }
                lib.delete(luid);
                clearCache();
        }
 
-       @Override
-       public void setSourceCover(String source, String luid) {
-               cacheLib.setSourceCover(source, luid);
-               lib.setSourceCover(source, luid);
-       }
-
        @Override
        public synchronized void changeSource(String luid, String newSource,
                        Progress pg) throws IOException {
index f9350efc4b353abc92bb9849ec8aac21368493a5..11c36630adcef96ea73d17c8712d84495260299a 100644 (file)
@@ -331,14 +331,14 @@ public class LocalLibrary extends BasicLibrary {
         * The directory (full path) where the new {@link Story} related to this
         * {@link MetaData} should be located on disk.
         * 
-        * @param type
+        * @param source
         *            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);
+       private File getExpectedDir(String source) {
+               String sanitizedSource = source.replaceAll("[^a-zA-Z0-9._+-]", "_");
+               return new File(baseDir, sanitizedSource);
        }
 
        /**
@@ -501,4 +501,23 @@ public class LocalLibrary extends BasicLibrary {
 
                return stories;
        }
+
+       /**
+        * Fix the source cover to the given story cover.
+        * 
+        * @param source
+        *            the source to change
+        * @param coverImage
+        *            the cover image
+        */
+       void setSourceCover(String source, BufferedImage coverImage) {
+               sourceCovers.put(source, coverImage);
+               File cover = new File(getExpectedDir(source), ".cover.png");
+               try {
+                       ImageIO.write(sourceCovers.get(source), "png", cover);
+               } catch (IOException e) {
+                       Instance.syserr(e);
+                       sourceCovers.remove(source);
+               }
+       }
 }
index acbd495d079b88047bfbc0e0e1769894b7375a1e..29d2bcda594f353afc882436438d071686db2dc0 100644 (file)
@@ -46,8 +46,8 @@ public class RemoteLibrary extends BasicLibrary {
        protected List<MetaData> getMetas(Progress pg) {
                // TODO: progress
                final List<MetaData> metas = new ArrayList<MetaData>();
-               MetaData[] fromNetwork = this
-                               .<MetaData[]> getRemoteObject("GET_METADATA *");
+               MetaData[] fromNetwork = this.<MetaData[]> getRemoteObject( //
+                               new Object[] { "GET_METADATA", "*" });
 
                if (fromNetwork != null) {
                        for (MetaData meta : fromNetwork) {
@@ -60,12 +60,19 @@ public class RemoteLibrary extends BasicLibrary {
 
        @Override
        public BufferedImage getCover(final String luid) {
-               return this.<BufferedImage> getRemoteObject("GET_COVER " + luid);
+               return this.<BufferedImage> getRemoteObject( //
+                               new Object[] { "GET_COVER", luid });
+       }
+
+       @Override
+       public BufferedImage getSourceCover(final String source) {
+               return this.<BufferedImage> getRemoteObject( //
+                               new Object[] { "GET_SOURCE_COVER", source });
        }
 
        @Override
        public synchronized Story getStory(final String luid, Progress pg) {
-               return this.<Story> getRemoteObject("GET_STORY " + luid);
+               return this.<Story> getRemoteObject(new Object[] { "GET_STORY", luid });
        }
 
        @Override
@@ -75,20 +82,24 @@ public class RemoteLibrary extends BasicLibrary {
        @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");
+               getRemoteObject(new Object[] { "SAVE_STORY", story, luid });
+
+               // because the meta changed:
+               clearCache();
+               story.setMeta(getInfo(luid));
+
+               return story;
        }
 
        @Override
        public synchronized void delete(String luid) throws IOException {
-               throw new java.lang.InternalError(
-                               "No write support allowed on remote Libraries");
+               getRemoteObject(new Object[] { "DELETE_STORY", luid });
        }
 
        @Override
        public void setSourceCover(String source, String luid) {
-               throw new java.lang.InternalError(
-                               "No write support allowed on remote Libraries");
+               this.<BufferedImage> getRemoteObject( //
+               new Object[] { "SET_SOURCE_COVER", source, luid });
        }
 
        @Override
@@ -125,7 +136,7 @@ public class RemoteLibrary extends BasicLibrary {
         * @return the object or NULL
         */
        @SuppressWarnings("unchecked")
-       private <T> T getRemoteObject(final String command) {
+       private <T> T getRemoteObject(final Object[] command) {
                final Object[] result = new Object[1];
                try {
                        new ConnectActionClient(host, port, true) {
index 221fbdfc05b21153f0c6a33ee012657f0b19cd95..8c836ed07ea6916c4c67818a6370bc66980631c1 100644 (file)
@@ -1,10 +1,12 @@
 package be.nikiroo.fanfix.library;
 
 import java.io.IOException;
+import java.security.InvalidParameterException;
 import java.util.List;
 
 import be.nikiroo.fanfix.Instance;
 import be.nikiroo.fanfix.data.MetaData;
+import be.nikiroo.fanfix.data.Story;
 import be.nikiroo.utils.Version;
 import be.nikiroo.utils.serial.ConnectActionServer;
 import be.nikiroo.utils.serial.Server;
@@ -12,11 +14,19 @@ import be.nikiroo.utils.serial.Server;
 /**
  * Create a new remote server that will listen for order on the given port.
  * <p>
- * The available commands are:
+ * The available commands are given as String arrays (first item is the command,
+ * the rest are the arguments):
  * <ul>
- * <li>GET_METADATA *: will get the metadata of all the stories in the library</li>
+ * <li>GET_METADATA *: will return the metadata of all the stories in the
+ * library</li>
  * <li>GET_STORY [luid]: will return the given story if it exists (or NULL if
  * not)</li>
+ * <li>SAVE_STORY [story] [luid]: save the story with the given LUID</li>
+ * <li>DELETE_STORY [luid]: delete the story of LUID luid</li>
+ * <li>GET_COVER [luid]: return the cover of the story</li>
+ * <li>GET_SOURCE_COVER [source]: return the cover for this source</li>
+ * <li>SET_SOURCE_COVER [source], [luid]: set the default cover for the given
+ * source to the cover of the story denoted by luid</li>
  * </ul>
  * 
  * @author niki
@@ -40,35 +50,43 @@ public class RemoteLibraryServer extends Server {
        @Override
        protected Object onRequest(ConnectActionServer action,
                        Version clientVersion, Object data) throws Exception {
-               String command = null;
-               String args = null;
-               if (data instanceof String) {
-                       command = (String) data;
-                       int pos = command.indexOf(" ");
-                       if (pos >= 0) {
-                               args = command.substring(pos + 1);
-                               command = command.substring(0, pos);
+
+               String command = "";
+               Object[] args = new Object[0];
+               if (data instanceof Object[]) {
+                       args = (Object[]) data;
+                       if (args.length > 0) {
+                               command = "" + args[0];
                        }
                }
 
-               System.out.println(String.format("COMMAND: [%s], ARGS: [%s]", command,
-                               args));
+               System.out.print("COMMAND: ");
+               for (Object arg : args) {
+                       System.out.print(arg + " ");
+               }
+               System.out.println("");
 
                // TODO: progress (+send name + %age info back to client)
 
                if ("GET_METADATA".equals(command)) {
-                       if (args != null && args.equals("*")) {
+                       if (args[1].equals("*")) {
                                List<MetaData> metas = Instance.getLibrary().getMetas(null);
                                return metas.toArray(new MetaData[] {});
                        }
+                       throw new InvalidParameterException(
+                                       "only * is valid here, but you passed: " + args[1]);
                } else if ("GET_STORY".equals(command)) {
-                       if (args != null) {
-                               return Instance.getLibrary().getStory(args, null);
-                       }
+                       return Instance.getLibrary().getStory("" + args[1], null);
+               } else if ("SAVE_STORY".equals(command)) {
+                       Instance.getLibrary().save((Story) args[1], "" + args[2], null);
+               } else if ("DELETE_STORY".equals(command)) {
+                       Instance.getLibrary().delete("" + args[1]);
                } else if ("GET_COVER".equals(command)) {
-                       if (args != null) {
-                               return Instance.getLibrary().getCover(args);
-                       }
+                       return Instance.getLibrary().getCover("" + args[1]);
+               } else if ("GET_SOURCE_COVER".equals(command)) {
+                       return Instance.getLibrary().getSourceCover("" + args[1]);
+               } else if ("SET_SOURCE_COVER".equals(command)) {
+                       Instance.getLibrary().setSourceCover("" + args[1], "" + args[2]);
                }
 
                return null;
index 2ae04b461d13bdd43cac911e0702190439d12cca..bf2b5ed96f233cd60a59228f5ac91f10080dd7cb 100644 (file)
@@ -140,7 +140,7 @@ class GuiReaderBook extends JPanel {
                        optSecondary = "";
                }
 
-               icon = new JLabel(generateCoverIcon(meta));
+               icon = new JLabel(generateCoverIcon());
                title = new JLabel(
                                String.format(
                                                "<html>"
@@ -371,24 +371,12 @@ class GuiReaderBook extends JPanel {
        /**
         * Generate a cover icon based upon the given {@link MetaData}.
         * 
-        * @param meta
-        *            the {@link MetaData} about the target {@link Story} or source
-        *            (if no LUID)
-        * 
         * @return the icon
         */
-       private ImageIcon generateCoverIcon(MetaData meta) {
+       private ImageIcon generateCoverIcon() {
                BufferedImage resizedImage = null;
-               String id = null;
-
-               String key = meta.getUuid();
-               if (key == null) {
-                       // a fake meta (a source)
-                       key = "source_" + meta.getSource();
-               }
+               String id = getIconId(meta);
 
-               id = key + ".thumb_" + SPINE_WIDTH + "x" + COVER_WIDTH + "+"
-                               + SPINE_HEIGHT + "+" + COVER_HEIGHT + "@" + HOFFSET;
                InputStream in = Instance.getCache().getFromCache(id);
                if (in != null) {
                        try {
@@ -444,4 +432,38 @@ class GuiReaderBook extends JPanel {
 
                return new ImageIcon(resizedImage);
        }
+
+       /**
+        * Manually clear the icon set for this item.
+        * 
+        * @param meta
+        *            the meta of the story or source (if luid is null)
+        */
+       public static void clearIcon(MetaData meta) {
+               String id = getIconId(meta);
+               Instance.getCache().removeFromCache(id);
+       }
+
+       /**
+        * Get a unique ID from this meta (note that if the luid is null, it is
+        * considered a source and not a {@link Story}).
+        * 
+        * @param meta
+        *            the meta
+        * @return the unique ID
+        */
+       private static String getIconId(MetaData meta) {
+               String id = null;
+
+               String key = meta.getUuid();
+               if (meta.getLuid() == null) {
+                       // a fake meta (== a source)
+                       key = "source_" + meta.getSource();
+               }
+
+               id = key + ".thumb_" + SPINE_WIDTH + "x" + COVER_WIDTH + "+"
+                               + SPINE_HEIGHT + "+" + COVER_HEIGHT + "@" + HOFFSET;
+
+               return id;
+       }
 }
index 39db588bee8ff225974663a0f445ffb642f70610..1b4cb66ebac26d1cd0cc6483de2f657dd9d535cc 100644 (file)
@@ -608,6 +608,7 @@ class GuiReaderFrame extends JFrame {
                                                        reader.clearLocalReaderCache(selectedBook.getMeta()
                                                                        .getLuid());
                                                        selectedBook.setCached(false);
+                                                       GuiReaderBook.clearIcon(selectedBook.getMeta());
                                                        SwingUtilities.invokeLater(new Runnable() {
                                                                @Override
                                                                public void run() {
@@ -785,6 +786,9 @@ class GuiReaderFrame extends JFrame {
                                        reader.getLibrary().setSourceCover(
                                                        selectedBook.getMeta().getSource(),
                                                        selectedBook.getMeta().getLuid());
+                                       MetaData source = selectedBook.getMeta().clone();
+                                       source.setLuid(null);
+                                       GuiReaderBook.clearIcon(source);
                                }
                        }
                });