--- /dev/null
+# Fanfix
+
+## Version 1.1.0
+
+- new Progress reporting system (currently only in CLI mode)
+- fix on e621 for "pending" pools, which were not downloaded before
+- unit tests system added (but no test yet, as all tests were moved into nikiroo-utils)
+
+## Version 1.0.0
+
+The GUI is now good enough to be released (export is still CLI-only though).
+
+- bugs fixed
+- GUI improved (a lot)
+- should be good enough for 1.0.0
+
+## Version 0.9.5
+
+- bugs fixed
+- WIN32 compatibility (tested on Windows 10)
+
+## Version 0.9.4
+
+- bugs fixed (lots of)
+- perf improved
+- use less cache files
+- GUI improvement (still not really OK, but OK enough I guess)
+
+## Version 0.9.3
+
+- bugs fixed (lots of)
+- first GUI implementation (which is ugly and buggy -- the buggly GUI)
+
+## Version 0.9.2
+
+Minimum JVM version: Java 1.6 (all binary JAR files will be released in 1.6).
+
+- bugs fixed
+
+## Version 0.9.1
+
+Initial version
+
fi;
echo "MAIN = be/nikiroo/fanfix/Main" > Makefile
-echo "TEST = " >> Makefile
+echo "TEST = be/nikiroo/fanfix/test/Test" >> Makefile
echo "TEST_PARAMS = $cols $ok $ko" >> Makefile
echo "NAME = fanfix" >> Makefile
echo "PREFIX = $PREFIX" >> Makefile
import be.nikiroo.fanfix.output.BasicOutput;
import be.nikiroo.fanfix.output.BasicOutput.OutputType;
import be.nikiroo.fanfix.supported.BasicSupport;
+import be.nikiroo.utils.ui.Progress;
import be.nikiroo.fanfix.supported.BasicSupport.SupportType;
import be.nikiroo.fanfix.supported.InfoReader;
*
* @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 Story getStory(String luid) {
+ public Story getStory(String luid, Progress pg) {
if (luid != null) {
for (Entry<MetaData, File> entry : getStories().entrySet()) {
if (luid.equals(entry.getKey().getLuid())) {
.getKey().getType());
URL url = entry.getValue().toURI().toURL();
if (type != null) {
- return BasicSupport.getSupport(type).process(url);
+ return BasicSupport.getSupport(type).process(url,
+ pg);
} else {
throw new IOException("Unknown type: "
+ entry.getKey().getType());
}
}
+ if (pg != null) {
+ pg.setMinMax(0, 1);
+ pg.setProgress(1);
+ }
+
return null;
}
*
* @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) throws IOException {
+ 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), null);
+ return save(support.process(url, pg), null);
}
/**
* 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)
+ public File export(String luid, OutputType type, String target, Progress pg)
throws IOException {
BasicOutput out = BasicOutput.getOutput(type, true);
if (out == null) {
throw new IOException("Output type not supported: " + type);
}
- Story story = getStory(luid);
+ Story story = getStory(luid, pg);
if (story == null) {
throw new IOException("Cannot find story to export: " + luid);
}
- return out.process(getStory(luid), target);
+ return out.process(story, target);
}
/**
import be.nikiroo.fanfix.supported.BasicSupport;
import be.nikiroo.fanfix.supported.BasicSupport.SupportType;
import be.nikiroo.utils.UIUtils;
+import be.nikiroo.utils.ui.Progress;
/**
* Main program entry point.
}
}
+ final Progress mainProgress = new Progress(0, 80);
+ mainProgress.addProgressListener(new Progress.ProgressListener() {
+ private int current = mainProgress.getMin();
+
+ public void progress(Progress progress, String name) {
+ int diff = progress.getProgress() - current;
+ current += diff;
+
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < diff; i++) {
+ builder.append('.');
+ }
+
+ System.err.print(builder.toString());
+
+ if (progress.isDone()) {
+ System.err.println("");
+ }
+ }
+ });
+ Progress pg = new Progress();
+ mainProgress.addProgress(pg, mainProgress.getMax());
+
if (exitCode != 255) {
switch (action) {
case IMPORT:
- exitCode = imprt(urlString);
+ exitCode = imprt(urlString, pg);
break;
case EXPORT:
- exitCode = export(luid, typeString, target);
+ exitCode = export(luid, typeString, target, pg);
break;
case CONVERT:
exitCode = convert(urlString, typeString, target,
- plusInfo == null ? false : plusInfo);
+ plusInfo == null ? false : plusInfo, pg);
break;
case LIST:
exitCode = list(typeString);
*
* @param urlString
* the resource to import
+ * @param pg
+ * the optional progress reporter
*
* @return the exit return code (0 = success)
*/
- public static int imprt(String urlString) {
+ public static int imprt(String urlString, Progress pg) {
try {
- Story story = Instance.getLibrary().imprt(getUrl(urlString));
+ Story story = Instance.getLibrary().imprt(getUrl(urlString), pg);
System.out.println(story.getMeta().getLuid() + ": \""
+ story.getMeta().getTitle() + "\" imported.");
} catch (IOException e) {
* the {@link OutputType} to use
* @param target
* the target
+ * @param pg
+ * the optional progress reporter
*
* @return the exit return code (0 = success)
*/
- public static int export(String luid, String typeString, String target) {
+ public static int export(String luid, String typeString, String target,
+ Progress pg) {
OutputType type = OutputType.valueOfNullOkUC(typeString);
if (type == null) {
Instance.syserr(new Exception(trans(StringId.OUTPUT_DESC,
}
try {
- Instance.getLibrary().export(luid, type, target);
+ Instance.getLibrary().export(luid, type, target, pg);
} catch (IOException e) {
Instance.syserr(e);
return 4;
try {
BasicReader reader = BasicReader.getReader();
if (library) {
- reader.setStory(story);
+ reader.setStory(story, null);
} else {
- reader.setStory(getUrl(story));
+ reader.setStory(getUrl(story), null);
}
if (chapString != null) {
* @param infoCover
* TRUE to also export the cover and info file, even if the given
* {@link OutputType} does not usually save them
+ * @param pg
+ * the optional progress reporter
*
* @return the exit return code (0 = success)
*/
private static int convert(String urlString, String typeString,
- String target, boolean infoCover) {
+ String target, boolean infoCover, Progress pg) {
int exitCode = 0;
String sourceName = urlString;
BasicSupport support = BasicSupport.getSupport(source);
if (support != null) {
- Story story = support.process(source);
+ Story story = support.process(source, pg);
try {
target = new File(target).getAbsolutePath();
import be.nikiroo.fanfix.bundles.Config;
import be.nikiroo.fanfix.data.Story;
import be.nikiroo.fanfix.supported.BasicSupport;
+import be.nikiroo.utils.ui.Progress;
/**
* The class that handles the different {@link Story} readers you can use.
*
* @param luid
* the {@link Story} ID
+ * @param pg
+ * the optional progress reporter
+ *
* @throws IOException
* in case of I/O error
*/
- public void setStory(String luid) throws IOException {
- story = Instance.getLibrary().getStory(luid);
+ public void setStory(String luid, Progress pg) throws IOException {
+ story = Instance.getLibrary().getStory(luid, pg);
if (story == null) {
- // if the LUID is wrong and < 3, pad it to 3 chars with "0" then
- // retry (since LUIDs are %03d)
- if (luid != null && luid.length() < 3) {
- while (luid.length() < 3) {
- luid = "0" + luid;
- }
- setStory(luid);
- } else {
- throw new IOException("Cannot retrieve story from library: "
- + luid);
- }
+ throw new IOException("Cannot retrieve story from library: " + luid);
}
}
*
* @param source
* the {@link Story} {@link URL}
+ * @param pg
+ * the optional progress reporter
+ *
* @throws IOException
* in case of I/O error
*/
- public void setStory(URL source) throws IOException {
+ public void setStory(URL source, Progress pg) throws IOException {
BasicSupport support = BasicSupport.getSupport(source);
if (support == null) {
throw new IOException("URL not supported: " + source.toString());
}
- story = support.process(source);
+ story = support.process(source, pg);
if (story == null) {
throw new IOException(
"Cannot retrieve story from external source: "
import be.nikiroo.fanfix.bundles.UiConfig;
import be.nikiroo.fanfix.data.Story;
import be.nikiroo.fanfix.output.BasicOutput.OutputType;
+import be.nikiroo.utils.ui.Progress;
class LocalReader extends BasicReader {
private Library lib;
public void read(int chapter) {
}
- // keep same luid
- public void imprt(String luid) throws IOException {
+ /**
+ * Import the story into the local reader library, and keep the same LUID.
+ *
+ * @param luid
+ * the Library UID
+ * @param pg
+ * the optional progress reporter
+ *
+ * @throws IOException
+ * in case of I/O error
+ */
+ public void imprt(String luid, Progress pg) throws IOException {
try {
- Story story = Instance.getLibrary().getStory(luid);
+ Story story = Instance.getLibrary().getStory(luid, pg);
if (story != null) {
story = lib.save(story, luid);
} else {
}
}
- public File getTarget(String luid) throws IOException {
+ /**
+ * Get the target file related to this {@link Story}.
+ *
+ * @param luid
+ * the LUID of the {@link Story}
+ * @param pg
+ * the optional progress reporter
+ *
+ * @return the target file
+ *
+ * @throws IOException
+ * in case of I/O error
+ */
+ public File getTarget(String luid, Progress pg) throws IOException {
File file = lib.getFile(luid);
if (file == null) {
- imprt(luid);
+ imprt(luid, pg);
file = lib.getFile(luid);
}
*
* @author niki
*/
- interface BookActionListner extends EventListener {
+ interface BookActionListener extends EventListener {
/**
* The book was selected (single click).
*
private boolean hovered;
private Date lastClick;
private long doubleClickDelay = 200; // in ms
- private List<BookActionListner> listeners;
+ private List<BookActionListener> listeners;
public LocalReaderBook(MetaData meta) {
if (meta.getCover() != null) {
}
private void setupListeners() {
- listeners = new ArrayList<LocalReaderBook.BookActionListner>();
+ listeners = new ArrayList<LocalReaderBook.BookActionListener>();
addMouseListener(new MouseListener() {
public void mouseReleased(MouseEvent e) {
}
}
private void click(boolean doubleClick) {
- for (BookActionListner listener : listeners) {
+ for (BookActionListener listener : listeners) {
if (doubleClick) {
listener.action(this);
} else {
}
}
- public void addActionListener(BookActionListner listener) {
+ public void addActionListener(BookActionListener listener) {
listeners.add(listener);
}
import be.nikiroo.fanfix.Main;
import be.nikiroo.fanfix.bundles.UiConfig;
import be.nikiroo.fanfix.data.MetaData;
-import be.nikiroo.fanfix.reader.LocalReaderBook.BookActionListner;
+import be.nikiroo.fanfix.reader.LocalReaderBook.BookActionListener;
import be.nikiroo.utils.WrapLayout;
class LocalReaderFrame extends JFrame {
books.add(book);
final String luid = meta.getLuid();
- book.addActionListener(new BookActionListner() {
+ book.addActionListener(new BookActionListener() {
public void select(LocalReaderBook book) {
for (LocalReaderBook abook : books) {
abook.setSelected(abook == book);
public void action(LocalReaderBook book) {
try {
- File target = LocalReaderFrame.this.reader
- .getTarget(luid);
+ File target = LocalReaderFrame.this.reader.getTarget(
+ luid, null);
Desktop.getDesktop().browse(target.toURI());
} catch (IOException e) {
Instance.syserr(e);
+ "unresponsive until it is downloaded...",
"Importing from URL", JOptionPane.QUESTION_MESSAGE);
if (url != null && !url.isEmpty()) {
- if (Main.imprt(url) != 0) {
+ if (Main.imprt(url, null) != 0) {
JOptionPane.showMessageDialog(LocalReaderFrame.this,
"Cannot import: " + url, "Imort error",
JOptionPane.ERROR_MESSAGE);
import be.nikiroo.fanfix.data.Story;
import be.nikiroo.utils.IOUtils;
import be.nikiroo.utils.StringUtils;
+import be.nikiroo.utils.ui.Progress;
/**
* This class is the base class used by the other support classes. It can be
*
* @param url
* the story resource
+ * @param pg
+ * the optional progress reporter
*
* @return the {@link Story}
*
* @throws IOException
* in case of I/O error
*/
- public Story process(URL url) throws IOException {
+ public Story process(URL url, Progress pg) throws IOException {
+ if (pg == null) {
+ pg = new Progress();
+ } else {
+ pg.setMinMax(0, 100);
+ }
+
setCurrentReferer(url);
+ pg.setProgress(1);
try {
Story story = processMeta(url, false, true);
+ pg.setProgress(10);
if (story == null) {
+ pg.setProgress(100);
return null;
}
story.setChapters(new ArrayList<Chapter>());
List<Entry<String, URL>> chapters = getChapters(url, getInput());
+ pg.setProgress(20);
+
int i = 1;
if (chapters != null) {
+ Progress pgChaps = new Progress(0, chapters.size());
+ pg.addProgress(pgChaps, 80);
+
for (Entry<String, URL> chap : chapters) {
setCurrentReferer(chap.getValue());
InputStream chapIn = Instance.getCache().open(
chapIn.close();
}
+ pgChaps.setProgress(i);
i++;
}
+ } else {
+ pg.setProgress(100);
}
return story;
import be.nikiroo.fanfix.data.Chapter;
import be.nikiroo.fanfix.data.Paragraph;
import be.nikiroo.fanfix.data.Story;
+import be.nikiroo.utils.ui.Progress;
/**
* Support class for CBZ files (works better with CBZ created with this program,
}
@Override
- public Story process(URL url) throws IOException {
+ public Story process(URL url, Progress pg) throws IOException {
+ if (pg == null) {
+ pg = new Progress();
+ } else {
+ pg.setMinMax(0, 100);
+ }
+
Story story = processMeta(url, false, true);
story.setChapters(new ArrayList<Chapter>());
Chapter chap = new Chapter(1, null);
ZipInputStream zipIn = new ZipInputStream(getInput());
+ pg.setProgress(10);
List<String> images = new ArrayList<String>();
for (ZipEntry entry = zipIn.getNextEntry(); entry != null; entry = zipIn
.getNextEntry()) {
}
}
+ pg.setProgress(80);
+
// ZIP order is not sure
Collections.sort(images);
+ pg.setProgress(90);
for (String uuid : images) {
try {
}
}
+ pg.setProgress(100);
return story;
}
}
import be.nikiroo.fanfix.data.MetaData;
import be.nikiroo.fanfix.data.Story;
import be.nikiroo.utils.StringUtils;
+import be.nikiroo.utils.ui.Progress;
/**
* Support class for <a href="http://e621.net/">e621.net</a> and <a
}
@Override
- public Story process(URL url) throws IOException {
+ public Story process(URL url, Progress pg) throws IOException {
// There is no chapters on e621, just pagination...
- Story story = super.process(url);
+ Story story = super.process(url, pg);
Chapter only = new Chapter(1, null);
for (Chapter chap : story) {
--- /dev/null
+package be.nikiroo.fanfix.test;
+
+import be.nikiroo.utils.test.TestLauncher;
+
+/**
+ * Tests for Fanfix.
+ *
+ * @author niki
+ */
+public class Test extends TestLauncher {
+ public Test(String[] args) {
+ super("Fanfix (empty: all tests were moved to nikiroo-utils...)", args);
+ }
+
+ /**
+ * Main entry point of the program.
+ *
+ * @param args
+ * the arguments passed to the {@link TestLauncher}s.
+ */
+ static public void main(String[] args) {
+ System.exit(new Test(args).launch());
+ }
+}