1 package be
.nikiroo
.fanfix
;
4 import java
.io
.IOException
;
5 import java
.net
.MalformedURLException
;
8 import be
.nikiroo
.fanfix
.bundles
.StringId
;
9 import be
.nikiroo
.fanfix
.data
.Chapter
;
10 import be
.nikiroo
.fanfix
.data
.Story
;
11 import be
.nikiroo
.fanfix
.output
.BasicOutput
;
12 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
13 import be
.nikiroo
.fanfix
.reader
.BasicReader
;
14 import be
.nikiroo
.fanfix
.reader
.BasicReader
.ReaderType
;
15 import be
.nikiroo
.fanfix
.supported
.BasicSupport
;
16 import be
.nikiroo
.fanfix
.supported
.BasicSupport
.SupportType
;
17 import be
.nikiroo
.utils
.Progress
;
18 import be
.nikiroo
.utils
.Version
;
19 import be
.nikiroo
.utils
.ui
.UIUtils
;
22 * Main program entry point.
27 private enum MainAction
{
28 IMPORT
, EXPORT
, CONVERT
, READ
, READ_URL
, LIST
, HELP
, SET_READER
, START
, VERSION
,
32 * Main program entry point.
34 * Known environment variables:
36 * <li>NOUTF: if set to 1 or 'true', the program will prefer non-unicode
37 * {@link String}s when possible</li>
38 * <li>CONFIG_DIR: a path where to look for the <tt>.properties</tt> files
39 * before taking the usual ones; they will also be saved/updated into this
40 * path when the program starts</li>
41 * <li>DEBUG: if set to 1 or 'true', the program will override the DEBUG_ERR
42 * configuration value with 'true'</li>
46 * <li>--import [URL]: import into library</li>
47 * <li>--export [id] [output_type] [target]: export story to target</li>
48 * <li>--convert [URL] [output_type] [target] (+info): convert URL into
50 * <li>--read [id] ([chapter number]): read the given story from the library
52 * <li>--read-url [URL] ([chapter number]): convert on the fly and read the
53 * story, without saving it</li>
54 * <li>--list ([type]): list the stories present in the library</li>
55 * <li>--set-reader [reader type]: set the reader type to CLI or LOCAL for
57 * <li>--version: get the version of the program</li>
61 * see method description
63 public static void main(String
[] args
) {
64 String urlString
= null;
66 String typeString
= null;
67 String chapString
= null;
69 MainAction action
= MainAction
.START
;
70 Boolean plusInfo
= null;
72 boolean noMoreActions
= false;
75 for (int i
= 0; exitCode
== 0 && i
< args
.length
; i
++) {
76 // Action (--) handling:
77 if (!noMoreActions
&& args
[i
].startsWith("--")) {
78 if (args
[i
].equals("--")) {
82 action
= MainAction
.valueOf(args
[i
].substring(2)
83 .toUpperCase().replace("-", "_"));
84 } catch (Exception e
) {
85 Instance
.syserr(new IllegalArgumentException(
86 "Unknown action: " + args
[i
], e
));
96 if (urlString
== null) {
105 } else if (typeString
== null) {
106 typeString
= args
[i
];
107 } else if (target
== null) {
114 if (urlString
== null) {
116 } else if (typeString
== null) {
117 typeString
= args
[i
];
118 } else if (target
== null) {
120 } else if (plusInfo
== null) {
121 if ("+info".equals(args
[i
])) {
131 if (typeString
== null) {
132 typeString
= args
[i
];
140 } else if (chapString
== null) {
141 chapString
= args
[i
];
147 if (urlString
== null) {
149 } else if (chapString
== null) {
150 chapString
= args
[i
];
159 exitCode
= setReaderType(args
[i
]);
162 exitCode
= 255; // not supposed to be selected by user
165 exitCode
= 255; // no arguments for this option
169 final Progress mainProgress
= new Progress(0, 80);
170 mainProgress
.addProgressListener(new Progress
.ProgressListener() {
171 private int current
= mainProgress
.getMin();
173 public void progress(Progress progress
, String name
) {
174 int diff
= progress
.getProgress() - current
;
177 StringBuilder builder
= new StringBuilder();
178 for (int i
= 0; i
< diff
; i
++) {
182 System
.err
.print(builder
.toString());
184 if (progress
.isDone()) {
185 System
.err
.println("");
189 Progress pg
= new Progress();
190 mainProgress
.addProgress(pg
, mainProgress
.getMax());
192 if (exitCode
!= 255) {
195 exitCode
= imprt(urlString
, pg
);
198 exitCode
= export(luid
, typeString
, target
, pg
);
201 exitCode
= convert(urlString
, typeString
, target
,
202 plusInfo
== null ?
false : plusInfo
, pg
);
205 exitCode
= list(typeString
);
208 exitCode
= read(luid
, chapString
, true);
211 exitCode
= read(urlString
, chapString
, false);
221 .println(String
.format("Fanfix version %s"
222 + "\nhttps://github.com/nikiroo/fanfix/"
223 + "\n\tWritten by Nikiroo",
224 Version
.getCurrentVersion()));
227 UIUtils
.setLookAndFeel();
228 BasicReader
.setDefaultReaderType(ReaderType
.LOCAL
);
229 BasicReader
.getReader().start(null);
234 if (exitCode
== 255) {
239 System
.exit(exitCode
);
244 * Import the given resource into the {@link Library}.
247 * the resource to import
249 * the optional progress reporter
251 * @return the exit return code (0 = success)
253 public static int imprt(String urlString
, Progress pg
) {
255 Story story
= Instance
.getLibrary().imprt(
256 BasicReader
.getUrl(urlString
), pg
);
257 System
.out
.println(story
.getMeta().getLuid() + ": \""
258 + story
.getMeta().getTitle() + "\" imported.");
259 } catch (IOException e
) {
268 * Export the {@link Story} from the {@link Library} to the given target.
273 * the {@link OutputType} to use
277 * the optional progress reporter
279 * @return the exit return code (0 = success)
281 public static int export(String luid
, String typeString
, String target
,
283 OutputType type
= OutputType
.valueOfNullOkUC(typeString
);
285 Instance
.syserr(new Exception(trans(StringId
.OUTPUT_DESC
,
291 Instance
.getLibrary().export(luid
, type
, target
, pg
);
292 } catch (IOException e
) {
301 * List the stories of the given type from the {@link Library} (unless NULL
302 * is passed, in which case all stories will be listed).
305 * the type to list the known stories of, or NULL to list all
308 * @return the exit return code (0 = success)
310 private static int list(String type
) {
311 BasicReader
.getReader().start(type
);
316 * Start the CLI reader for this {@link Story}.
319 * the LUID of the {@link Story} in the {@link Library} <b>or</b>
320 * the {@link Story} {@link URL}
322 * which {@link Chapter} to read (starting at 1), or NULL to get
323 * the {@link Story} description
325 * TRUE if the source is the {@link Story} LUID, FALSE if it is a
328 * @return the exit return code (0 = success)
330 private static int read(String story
, String chapString
, boolean library
) {
332 BasicReader reader
= BasicReader
.getReader();
334 reader
.setStory(story
, null);
336 reader
.setStory(BasicReader
.getUrl(story
), null);
339 if (chapString
!= null) {
341 reader
.read(Integer
.parseInt(chapString
));
342 } catch (NumberFormatException e
) {
343 Instance
.syserr(new IOException(
344 "Chapter number cannot be parsed: " + chapString
, e
));
350 } catch (IOException e
) {
359 * Convert the {@link Story} into another format.
362 * the source {@link Story} to convert
364 * the {@link OutputType} to convert to
368 * TRUE to also export the cover and info file, even if the given
369 * {@link OutputType} does not usually save them
371 * the optional progress reporter
373 * @return the exit return code (0 = success)
375 private static int convert(String urlString
, String typeString
,
376 String target
, boolean infoCover
, Progress pg
) {
379 String sourceName
= urlString
;
381 URL source
= BasicReader
.getUrl(urlString
);
382 sourceName
= source
.toString();
383 if (source
.toString().startsWith("file://")) {
384 sourceName
= sourceName
.substring("file://".length());
387 OutputType type
= OutputType
.valueOfAllOkUC(typeString
);
389 Instance
.syserr(new IOException(trans(
390 StringId
.ERR_BAD_OUTPUT_TYPE
, typeString
)));
395 BasicSupport support
= BasicSupport
.getSupport(source
);
397 if (support
!= null) {
398 Progress pgIn
= new Progress();
399 Progress pgOut
= new Progress();
402 pg
.addProgress(pgIn
, 1);
403 pg
.addProgress(pgOut
, 1);
406 Story story
= support
.process(source
, pgIn
);
408 target
= new File(target
).getAbsolutePath();
409 BasicOutput
.getOutput(type
, infoCover
).process(
410 story
, target
, pgOut
);
411 } catch (IOException e
) {
412 Instance
.syserr(new IOException(trans(
413 StringId
.ERR_SAVING
, target
), e
));
417 Instance
.syserr(new IOException(trans(
418 StringId
.ERR_NOT_SUPPORTED
, source
)));
422 } catch (IOException e
) {
423 Instance
.syserr(new IOException(trans(StringId
.ERR_LOADING
,
428 } catch (MalformedURLException e
) {
429 Instance
.syserr(new IOException(trans(StringId
.ERR_BAD_URL
,
438 * Simple shortcut method to call {link Instance#getTrans()#getString()}.
441 * the ID to translate
443 * @return the translated result
445 private static String
trans(StringId id
, Object
... params
) {
446 return Instance
.getTrans().getString(id
, params
);
450 * Display the correct syntax of the program to the user to stdout, or an
451 * error message if the syntax used was wrong on stderr.
454 * TRUE to show the syntax help, FALSE to show "syntax error"
456 private static void syntax(boolean showHelp
) {
458 StringBuilder builder
= new StringBuilder();
459 for (SupportType type
: SupportType
.values()) {
460 builder
.append(trans(StringId
.ERR_SYNTAX_TYPE
, type
.toString(),
462 builder
.append('\n');
465 String typesIn
= builder
.toString();
466 builder
.setLength(0);
468 for (OutputType type
: OutputType
.values()) {
469 builder
.append(trans(StringId
.ERR_SYNTAX_TYPE
, type
.toString(),
470 type
.getDesc(true)));
471 builder
.append('\n');
474 String typesOut
= builder
.toString();
476 System
.out
.println(trans(StringId
.HELP_SYNTAX
, typesIn
, typesOut
));
478 System
.err
.println(trans(StringId
.ERR_SYNTAX
));
483 * Set the default reader type for this session only (it can be changed in
484 * the configuration file, too, but this value will override it).
486 * @param readerTypeString
489 private static int setReaderType(String readerTypeString
) {
491 ReaderType readerType
= ReaderType
.valueOf(readerTypeString
493 BasicReader
.setDefaultReaderType(readerType
);
495 } catch (IllegalArgumentException e
) {
496 Instance
.syserr(new IOException("Unknown reader type: "
497 + readerTypeString
, e
));