- Bug fixes
- Remote server/client improvements
- Better support for some CBZ files (if SUMMARY or URL files are present in it)
+- Fix cover images not deleted on story delete
+- Fix some images not supported because not jpeg-able (now try again in png)
+- Fix some covers not found (normal and remote hopefully)
## Version 1.6.2
package be.nikiroo.fanfix;
+import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public void saveAsImage(URL url, File target) throws IOException {
InputStream in = open(url, null, true);
try {
- ImageIO.write(ImageUtils.fromStream(in), Instance.getConfig()
- .getString(Config.IMAGE_FORMAT_CONTENT).toLowerCase(),
- target);
- } catch (IOException e) {
- throw new IOException("Cannot write image " + url, e);
+ saveAsImage(ImageUtils.fromStream(in), target);
} finally {
in.close();
}
}
+ /**
+ * Save the given resource as an image on disk using the default image
+ * format for content.
+ *
+ * @param image
+ * the resource
+ * @param target
+ * the target file
+ *
+ * @throws IOException
+ * in case of I/O error
+ */
+ public void saveAsImage(BufferedImage image, File target)
+ throws IOException {
+ try {
+ String format = Instance.getConfig()
+ .getString(Config.IMAGE_FORMAT_CONTENT).toLowerCase();
+ boolean ok = ImageIO.write(image, format, target);
+ if (!ok) {
+ // Some formats are not reliable
+ // Second change: PNG
+ if (!format.equals("png")) {
+ ok = ImageIO.write(image, "png", target);
+ }
+
+ if (!ok) {
+ throw new IOException(
+ "Cannot find a writer for this image and format: "
+ + format);
+ }
+ }
+ } catch (IOException e) {
+ throw new IOException("Cannot write image to " + target, e);
+ }
+ }
+
/**
* Manually add this item to the cache.
*
package be.nikiroo.fanfix.data;
-import java.net.URL;
+import java.awt.image.BufferedImage;
/**
* A paragraph in a chapter of the story.
private ParagraphType type;
private String content;
+ private BufferedImage contentImage;
private long words;
/**
/**
* Create a new {@link Paragraph} with the given image.
*
- * @param imageUrl
- * the image as an URL
+ * @param contentImage
+ * the image
*/
- public Paragraph(URL imageUrl) {
- this(ParagraphType.IMAGE, imageUrl.toString(), 1);
+ public Paragraph(BufferedImage contentImage) {
+ this(ParagraphType.IMAGE, null, 1);
+ this.contentImage = contentImage;
}
/**
}
/**
- * The content of this {@link Paragraph}.
+ * The content of this {@link Paragraph} if it is not an image.
*
* @return the content
*/
this.content = content;
}
+ /**
+ * The content of this {@link Paragraph} if it is an image.
+ *
+ * @return the content
+ */
+ public BufferedImage getContentImage() {
+ return contentImage;
+ }
+
/**
* The number of words (or images) in this {@link Paragraph}.
*
}
String coverExt = "."
- + Instance.getConfig().getString(Config.IMAGE_FORMAT_COVER);
+ + Instance.getConfig().getString(Config.IMAGE_FORMAT_COVER)
+ .toLowerCase();
File coverFile = new File(path + coverExt);
if (!coverFile.exists()) {
coverFile = new File(path.substring(0,
} catch (IOException e) {
// We should not have not-supported files in the
// library
- Instance.getTraceHandler().error(new IOException(
- "Cannot load file from library: " + infoFile, e));
+ Instance.getTraceHandler().error(
+ new IOException(
+ "Cannot load file from library: "
+ + infoFile, e));
}
pgFiles.add(1);
}
/**
* Create a new remote server that will listen for order on the given port.
* <p>
- * The available commands are given as String arrays (first item is the key,
- * second is the command, the rest are the arguments):
+ * The available commands are given as arrays of objects (first item is the key,
+ * second is the command, the rest are the arguments).
+ * <p>
+ * The key is always a String, the commands are also Strings; the parameters
+ * vary depending upon the command.
* <ul>
- * <li>KEY GET_METADATA *: will return the metadata of all the stories in the
+ * <li>[key] GET_METADATA *: will return the metadata of all the stories in the
* library</li>
- * <li>KEY GET_STORY [luid]: will return the given story if it exists (or NULL
+ * <li>[key] GET_STORY [luid]: will return the given story if it exists (or NULL
* if not)</li>
- * <li>KEY SAVE_STORY [story] [luid]: save the story with the given LUID</li>
- * <li>KEY DELETE_STORY [luid]: delete the story of LUID luid</li>
- * <li>KEY GET_COVER [luid]: return the cover of the story</li>
- * <li>KEY GET_SOURCE_COVER [source]: return the cover for this source</li>
- * <li>KEY SET_SOURCE_COVER [source], [luid]: set the default cover for the
+ * <li>[key] SAVE_STORY [story] [luid]: save the story with the given LUID</li>
+ * <li>[key] DELETE_STORY [luid]: delete the story of LUID luid</li>
+ * <li>[key] GET_COVER [luid]: return the cover of the story</li>
+ * <li>[key] GET_SOURCE_COVER [source]: return the cover for this source</li>
+ * <li>[key] SET_SOURCE_COVER [source], [luid]: set the default cover for the
* given source to the cover of the story denoted by luid</li>
- * <li>KEY EXIT: stop the server</li>
+ * <li>[key] EXIT: stop the server</li>
* </ul>
*
* @author niki
dir = File.createTempFile("fanfic-reader-cbz-dir", ".wip");
dir.delete();
dir.mkdir();
+ try {
+ // will also save the images!
+ new InfoText().process(story, dir, targetNameOrig);
- // will also save the images!
- new InfoText().process(story, dir, targetNameOrig);
+ InfoCover.writeInfo(dir, targetNameOrig, story.getMeta());
+ if (story.getMeta() != null && !story.getMeta().isFakeCover()) {
+ InfoCover.writeCover(dir, targetNameOrig, story.getMeta());
+ }
- InfoCover.writeInfo(dir, targetNameOrig, story.getMeta());
- if (story.getMeta() != null && !story.getMeta().isFakeCover()) {
- InfoCover.writeCover(dir, targetNameOrig, story.getMeta());
- }
+ IOUtils.writeSmallFile(dir, "version", "3.0");
- IOUtils.writeSmallFile(dir, "version", "3.0");
+ try {
+ super.process(story, targetDir, targetNameOrig);
+ } finally {
+ }
- try {
- super.process(story, targetDir, targetNameOrig);
+ IOUtils.zip(dir, target, true);
} finally {
+ IOUtils.deltree(dir);
}
- IOUtils.zip(dir, target, true);
- IOUtils.deltree(dir);
-
return target;
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
-import java.net.URL;
import javax.imageio.ImageIO;
break;
case IMAGE:
File file = new File(images, getCurrentImageBestName(false));
- Instance.getCache().saveAsImage(new URL(para.getContent()), file);
+ Instance.getCache().saveAsImage(para.getContentImage(), file);
writer.write(" <img class='page-image' src='images/"
+ getCurrentImageBestName(false) + "'/>");
break;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
-import java.net.URL;
import be.nikiroo.fanfix.Instance;
import be.nikiroo.fanfix.bundles.StringId;
protected void writeParagraphHeader(Paragraph para) throws IOException {
if (para.getType() == ParagraphType.IMAGE) {
File file = new File(targetDir, getCurrentImageBestName(true));
- Instance.getCache().saveAsImage(new URL(para.getContent()), file);
+ Instance.getCache().saveAsImage(para.getContentImage(), file);
}
}
* @return the {@link Paragraph}
*/
private Paragraph makeParagraph(URL source, String line) {
- URL image = null;
+ BufferedImage image = null;
if (line.startsWith("[") && line.endsWith("]")) {
- image = getImageUrl(this, source,
- line.substring(1, line.length() - 1).trim());
+ image = getImage(this, source, line.substring(1, line.length() - 1)
+ .trim());
}
if (image != null) {
package be.nikiroo.fanfix.supported;
import java.awt.image.BufferedImage;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
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.zip.ZipEntry;
import java.util.zip.ZipInputStream;
-import javax.imageio.ImageIO;
-
import be.nikiroo.fanfix.Instance;
import be.nikiroo.fanfix.data.Chapter;
import be.nikiroo.fanfix.data.Paragraph;
import be.nikiroo.fanfix.data.Story;
+import be.nikiroo.utils.ImageUtils;
import be.nikiroo.utils.Progress;
/**
Progress pgMeta = new Progress();
pg.addProgress(pgMeta, 10);
Story story = processMeta(url, false, true, pgMeta);
- if (!pgMeta.isDone()) {
- pgMeta.setProgress(pgMeta.getMax()); // 10%
- }
+ pgMeta.done(); // 10%
story.setChapters(new ArrayList<Chapter>());
Chapter chap = new Chapter(1, null);
ZipInputStream zipIn = new ZipInputStream(getInput());
- List<String> images = new ArrayList<String>();
+ Map<String, BufferedImage> images = new HashMap<String, BufferedImage>();
for (ZipEntry entry = zipIn.getNextEntry(); entry != null; entry = zipIn
.getNextEntry()) {
if (!entry.isDirectory()
if (imageEntry) {
String uuid = meta.getUuid() + "_" + entry.getName();
try {
- File tmp = Instance.getCache().addToCache(zipIn, uuid);
- images.add(tmp.toURI().toURL().toString());
+ images.put(uuid, ImageUtils.fromStream(zipIn));
} catch (Exception e) {
Instance.getTraceHandler().error(e);
}
+
+ if (pg.getProgress() < 85) {
+ pg.add(1);
+ }
}
}
}
- pg.setProgress(80);
+ pg.setProgress(85);
// ZIP order is not correct for us
- Collections.sort(images);
+ List<String> imagesList = new ArrayList<String>(images.keySet());
+ Collections.sort(imagesList);
+
pg.setProgress(90);
- for (String uuid : images) {
+ for (String uuid : imagesList) {
try {
- chap.getParagraphs().add(new Paragraph(new URL(uuid)));
+ chap.getParagraphs().add(new Paragraph(images.get(uuid)));
} catch (Exception e) {
Instance.getTraceHandler().error(e);
}
}
if (meta.getCover() == null && !images.isEmpty()) {
- InputStream in = Instance.getCache().open(new URL(images.get(0)),
- this, true);
- try {
- BufferedImage fcover = ImageIO.read(in);
- meta.setCover(fcover);
- meta.setFakeCover(true);
- } finally {
- in.close();
- }
+ meta.setCover(images.get(imagesList.get(0)));
}
pg.setProgress(100);
if (info.endsWith(".info")) {
info = info.substring(0, info.length() - ".info".length());
String ext = "."
- + Instance.getConfig().getString(
- Config.IMAGE_FORMAT_COVER);
+ + Instance.getConfig()
+ .getString(Config.IMAGE_FORMAT_COVER)
+ .toLowerCase();
meta.setCover(BasicSupport.getImage(null, sourceInfoFile,
info + ext));
}
import be.nikiroo.utils.test.TestCase;
import be.nikiroo.utils.test.TestLauncher;
-public class BasicSupportTest extends TestLauncher {
+class BasicSupportTest extends TestLauncher {
// quote chars
private char openQuote = Instance.getTrans().getCharacter(
StringId.OPEN_SINGLE_QUOTE);
import be.nikiroo.utils.test.TestCase;
import be.nikiroo.utils.test.TestLauncher;
-public class LibraryTest extends TestLauncher {
+class LibraryTest extends TestLauncher {
private BasicLibrary lib;
private File tmp;
* @author niki
*/
public class Test extends TestLauncher {
+ /**
+ * Create the Fanfix {@link TestLauncher}.
+ *
+ * @param args
+ * the arguments to configure the number of columns and the ok/ko
+ * {@link String}s
+ */
public Test(String[] args) {
super("Fanfix", args);
Instance.setTraceHandler(null);
* @param args
* the arguments passed to the {@link TestLauncher}s.
* @throws IOException
+ * in case of I/O error
*/
static public void main(String[] args) throws IOException {
File tmpConfig = File.createTempFile("fanfix-config_", ".test");