allox subkeys, step 1 (keys not active)
[nikiroo-utils.git] / src / be / nikiroo / fanfix / Main.java
CommitLineData
08fe2e33
NR
1package be.nikiroo.fanfix;
2
3import java.io.File;
4import java.io.IOException;
5import java.net.MalformedURLException;
6import java.net.URL;
91b82a5c 7import java.util.ArrayList;
f569d249 8import java.util.List;
08fe2e33 9
fb25273c 10import be.nikiroo.fanfix.bundles.Config;
08fe2e33
NR
11import be.nikiroo.fanfix.bundles.StringId;
12import be.nikiroo.fanfix.data.Chapter;
f569d249 13import be.nikiroo.fanfix.data.MetaData;
08fe2e33 14import be.nikiroo.fanfix.data.Story;
ff05b828
NR
15import be.nikiroo.fanfix.library.BasicLibrary;
16import be.nikiroo.fanfix.library.CacheLibrary;
e42573a0
NR
17import be.nikiroo.fanfix.library.LocalLibrary;
18import be.nikiroo.fanfix.library.RemoteLibrary;
19import be.nikiroo.fanfix.library.RemoteLibraryServer;
08fe2e33
NR
20import be.nikiroo.fanfix.output.BasicOutput;
21import be.nikiroo.fanfix.output.BasicOutput.OutputType;
3727aae2 22import be.nikiroo.fanfix.reader.BasicReader;
e42573a0
NR
23import be.nikiroo.fanfix.reader.Reader;
24import be.nikiroo.fanfix.reader.Reader.ReaderType;
91b82a5c 25import be.nikiroo.fanfix.searchable.BasicSearchable;
08fe2e33 26import be.nikiroo.fanfix.supported.BasicSupport;
0ffa4754 27import be.nikiroo.fanfix.supported.SupportType;
3b2b638f 28import be.nikiroo.utils.Progress;
39c3c689 29import be.nikiroo.utils.Version;
62c63b07 30import be.nikiroo.utils.serial.server.ServerObject;
08fe2e33
NR
31
32/**
33 * Main program entry point.
34 *
35 * @author niki
36 */
37public class Main {
d0114000 38 private enum MainAction {
8141d7ac 39 IMPORT, EXPORT, CONVERT, READ, READ_URL, LIST, HELP, SET_READER, START, VERSION, SERVER, STOP_SERVER, REMOTE, SET_SOURCE, SET_TITLE, SET_AUTHOR, SEARCH, SEARCH_TAG
d0114000
NR
40 }
41
08fe2e33
NR
42 /**
43 * Main program entry point.
44 * <p>
45 * Known environment variables:
46 * <ul>
d0114000 47 * <li>NOUTF: if set to 1 or 'true', the program will prefer non-unicode
08fe2e33
NR
48 * {@link String}s when possible</li>
49 * <li>CONFIG_DIR: a path where to look for the <tt>.properties</tt> files
edd46289
NR
50 * before taking the usual ones; they will also be saved/updated into this
51 * path when the program starts</li>
d0114000
NR
52 * <li>DEBUG: if set to 1 or 'true', the program will override the DEBUG_ERR
53 * configuration value with 'true'</li>
54 * </ul>
55 * <p>
56 * <ul>
57 * <li>--import [URL]: import into library</li>
58 * <li>--export [id] [output_type] [target]: export story to target</li>
59 * <li>--convert [URL] [output_type] [target] (+info): convert URL into
60 * target</li>
61 * <li>--read [id] ([chapter number]): read the given story from the library
62 * </li>
333f0e7b 63 * <li>--read-url [URL] ([chapter number]): convert on the fly and read the
d0114000 64 * story, without saving it</li>
8b153400
NR
65 * <li>--search: list the supported websites (where)</li>
66 * <li>--search [where] [keywords] (page [page]) (item [item]): search on
67 * the supported website and display the given results page of stories it
68 * found, or the story details if asked</li>
8141d7ac
NR
69 * <li>--search-tag [where]: list all the tags supported by this website</li>
70 * <li>--search-tag [index 1]... (page [page]) (item [item]): search for the
71 * given stories or subtags, tag by tag, and display information about a
72 * specific page of results or about a specific item if requested</li>
333f0e7b 73 * <li>--list ([type]): list the stories present in the library</li>
e10b51a2
NR
74 * <li>--set-source [id] [new source]: change the source of the given story</li>
75 * <li>--set-title [id] [new title]: change the title of the given story</li>
76 * <li>--set-author [id] [new author]: change the author of the given story</li>
c1873e56
NR
77 * <li>--set-reader [reader type]: set the reader type to CLI, TUI or LOCAL
78 * for this command</li>
39c3c689 79 * <li>--version: get the version of the program</li>
fb25273c
NR
80 * <li>--server: start the server mode (see config file for parameters)</li>
81 * <li>--stop-server: stop the running server on this port if any</li>
2070ced5 82 * <li>--remote [key] [host] [port]: use a the given remote library</li>
08fe2e33
NR
83 * </ul>
84 *
85 * @param args
d0114000 86 * see method description
08fe2e33
NR
87 */
88 public static void main(String[] args) {
d0114000
NR
89 String urlString = null;
90 String luid = null;
b0e88ebd 91 String sourceString = null;
e10b51a2
NR
92 String titleString = null;
93 String authorString = null;
d0114000
NR
94 String chapString = null;
95 String target = null;
2070ced5 96 String key = null;
333f0e7b 97 MainAction action = MainAction.START;
d0114000 98 Boolean plusInfo = null;
b0e88ebd
NR
99 String host = null;
100 Integer port = null;
91b82a5c
NR
101 SupportType searchOn = null;
102 String search = null;
8b153400 103 List<Integer> tags = new ArrayList<Integer>();
91b82a5c
NR
104 Integer page = null;
105 Integer item = null;
73ce17ef 106
d0114000
NR
107 boolean noMoreActions = false;
108
109 int exitCode = 0;
110 for (int i = 0; exitCode == 0 && i < args.length; i++) {
111 // Action (--) handling:
112 if (!noMoreActions && args[i].startsWith("--")) {
113 if (args[i].equals("--")) {
114 noMoreActions = true;
115 } else {
116 try {
117 action = MainAction.valueOf(args[i].substring(2)
118 .toUpperCase().replace("-", "_"));
119 } catch (Exception e) {
62c63b07
NR
120 Instance.getTraceHandler().error(
121 new IllegalArgumentException("Unknown action: "
122 + args[i], e));
d0114000
NR
123 exitCode = 255;
124 }
125 }
08fe2e33 126
d0114000
NR
127 continue;
128 }
129
130 switch (action) {
131 case IMPORT:
132 if (urlString == null) {
133 urlString = args[i];
134 } else {
135 exitCode = 255;
136 }
137 break;
138 case EXPORT:
139 if (luid == null) {
140 luid = args[i];
b0e88ebd
NR
141 } else if (sourceString == null) {
142 sourceString = args[i];
d0114000
NR
143 } else if (target == null) {
144 target = args[i];
145 } else {
146 exitCode = 255;
147 }
148 break;
149 case CONVERT:
150 if (urlString == null) {
151 urlString = args[i];
b0e88ebd
NR
152 } else if (sourceString == null) {
153 sourceString = args[i];
d0114000
NR
154 } else if (target == null) {
155 target = args[i];
156 } else if (plusInfo == null) {
157 if ("+info".equals(args[i])) {
158 plusInfo = true;
159 } else {
160 exitCode = 255;
161 }
162 } else {
163 exitCode = 255;
08fe2e33 164 }
d0114000
NR
165 break;
166 case LIST:
b0e88ebd
NR
167 if (sourceString == null) {
168 sourceString = args[i];
d0114000
NR
169 } else {
170 exitCode = 255;
08fe2e33 171 }
d0114000 172 break;
e10b51a2
NR
173 case SET_SOURCE:
174 if (luid == null) {
175 luid = args[i];
176 } else if (sourceString == null) {
177 sourceString = args[i];
178 } else {
179 exitCode = 255;
180 }
181 break;
182 case SET_TITLE:
183 if (luid == null) {
184 luid = args[i];
185 } else if (sourceString == null) {
186 titleString = args[i];
187 } else {
188 exitCode = 255;
189 }
190 break;
191 case SET_AUTHOR:
192 if (luid == null) {
193 luid = args[i];
194 } else if (sourceString == null) {
195 authorString = args[i];
196 } else {
197 exitCode = 255;
198 }
199 break;
d0114000
NR
200 case READ:
201 if (luid == null) {
202 luid = args[i];
203 } else if (chapString == null) {
204 chapString = args[i];
205 } else {
206 exitCode = 255;
08fe2e33 207 }
d0114000
NR
208 break;
209 case READ_URL:
210 if (urlString == null) {
211 urlString = args[i];
212 } else if (chapString == null) {
213 chapString = args[i];
214 } else {
215 exitCode = 255;
08fe2e33 216 }
d0114000 217 break;
91b82a5c
NR
218 case SEARCH:
219 if (searchOn == null) {
220 searchOn = SupportType.valueOfAllOkUC(args[i]);
8b153400 221
91b82a5c
NR
222 if (searchOn == null) {
223 Instance.getTraceHandler().error(
224 "Website not known: <" + args[i] + ">");
b31a0db0
NR
225 exitCode = 41;
226 break;
91b82a5c 227 }
8b153400 228
91b82a5c
NR
229 if (BasicSearchable.getSearchable(searchOn) == null) {
230 Instance.getTraceHandler().error(
231 "Website not supported: " + searchOn);
b31a0db0
NR
232 exitCode = 42;
233 break;
91b82a5c
NR
234 }
235 } else if (search == null) {
236 search = args[i];
8b153400 237 } else if (page != null && page == -1) {
91b82a5c
NR
238 try {
239 page = Integer.parseInt(args[i]);
8b153400
NR
240 } catch (Exception e) {
241 page = -2;
91b82a5c 242 }
8b153400 243 } else if (item != null && item == -1) {
91b82a5c
NR
244 try {
245 item = Integer.parseInt(args[i]);
8b153400
NR
246 } catch (Exception e) {
247 item = -2;
248 }
249 } else if (page == null || item == null) {
250 if (page == null && "page".equals(args[i])) {
251 page = -1;
252 } else if (item == null && "item".equals(args[i])) {
253 item = -1;
254 } else {
91b82a5c
NR
255 exitCode = 255;
256 }
257 } else {
258 exitCode = 255;
259 }
260 break;
8141d7ac 261 case SEARCH_TAG:
91b82a5c
NR
262 if (searchOn == null) {
263 searchOn = SupportType.valueOfAllOkUC(args[i]);
8b153400 264
91b82a5c
NR
265 if (searchOn == null) {
266 Instance.getTraceHandler().error(
267 "Website not known: <" + args[i] + ">");
268 exitCode = 255;
269 }
8b153400 270
91b82a5c
NR
271 if (BasicSearchable.getSearchable(searchOn) == null) {
272 Instance.getTraceHandler().error(
273 "Website not supported: " + searchOn);
274 exitCode = 255;
275 }
8b153400
NR
276 } else if (page == null && item == null) {
277 if ("page".equals(args[i])) {
278 page = -1;
279 } else if ("item".equals(args[i])) {
280 item = -1;
281 } else {
282 try {
283 int index = Integer.parseInt(args[i]);
284 tags.add(index);
285 } catch (NumberFormatException e) {
286 Instance.getTraceHandler().error(
287 "Invalid tag index: " + args[i]);
288 exitCode = 255;
289 }
290 }
291 } else if (page != null && page == -1) {
292 try {
293 page = Integer.parseInt(args[i]);
294 } catch (Exception e) {
295 page = -2;
296 }
297 } else if (item != null && item == -1) {
298 try {
299 item = Integer.parseInt(args[i]);
300 } catch (Exception e) {
301 item = -2;
302 }
303 } else if (page == null || item == null) {
304 if (page == null && "page".equals(args[i])) {
305 page = -1;
306 } else if (item == null && "item".equals(args[i])) {
307 item = -1;
308 } else {
309 exitCode = 255;
310 }
91b82a5c 311 } else {
8b153400 312 exitCode = 255;
91b82a5c
NR
313 }
314 break;
d0114000
NR
315 case HELP:
316 exitCode = 255;
317 break;
318 case SET_READER:
7de079f1 319 exitCode = setReaderType(args[i]);
c1873e56 320 action = MainAction.START;
d0114000 321 break;
333f0e7b
NR
322 case START:
323 exitCode = 255; // not supposed to be selected by user
324 break;
39c3c689
NR
325 case VERSION:
326 exitCode = 255; // no arguments for this option
b0e88ebd
NR
327 break;
328 case SERVER:
fb25273c
NR
329 exitCode = 255; // no arguments for this option
330 break;
5e848e6a 331 case STOP_SERVER:
fb25273c 332 exitCode = 255; // no arguments for this option
b0e88ebd
NR
333 break;
334 case REMOTE:
2070ced5
NR
335 if (key == null) {
336 key = args[i];
337 } else if (host == null) {
b0e88ebd
NR
338 host = args[i];
339 } else if (port == null) {
340 port = Integer.parseInt(args[i]);
ff05b828 341
2070ced5 342 BasicLibrary lib = new RemoteLibrary(key, host, port);
5895a958 343 lib = new CacheLibrary(Instance.getRemoteDir(host), lib);
ff05b828
NR
344
345 BasicReader.setDefaultLibrary(lib);
5e848e6a 346
b0e88ebd
NR
347 action = MainAction.START;
348 } else {
349 exitCode = 255;
350 }
351 break;
d0114000
NR
352 }
353 }
354
92fb0719
NR
355 final Progress mainProgress = new Progress(0, 80);
356 mainProgress.addProgressListener(new Progress.ProgressListener() {
357 private int current = mainProgress.getMin();
358
211f7ddb 359 @Override
92fb0719
NR
360 public void progress(Progress progress, String name) {
361 int diff = progress.getProgress() - current;
362 current += diff;
363
1822d603
NR
364 if (diff <= 0)
365 return;
366
92fb0719
NR
367 StringBuilder builder = new StringBuilder();
368 for (int i = 0; i < diff; i++) {
369 builder.append('.');
370 }
371
372 System.err.print(builder.toString());
373
374 if (progress.isDone()) {
375 System.err.println("");
376 }
377 }
378 });
379 Progress pg = new Progress();
380 mainProgress.addProgress(pg, mainProgress.getMax());
381
b42117f1
NR
382 VersionCheck updates = VersionCheck.check();
383 if (updates.isNewVersionAvailable()) {
384 // Sent to syserr so not to cause problem if one tries to capture a
385 // story content in text mode
386 System.err
387 .println("A new version of the program is available at https://github.com/nikiroo/fanfix/releases");
388 System.err.println("");
389 for (Version v : updates.getNewer()) {
390 System.err.println("\tVersion " + v);
391 System.err.println("\t-------------");
392 System.err.println("");
91b82a5c
NR
393 for (String it : updates.getChanges().get(v)) {
394 System.err.println("\t- " + it);
b42117f1
NR
395 }
396 System.err.println("");
397 }
398 }
399
b31a0db0 400 if (exitCode == 0) {
d0114000
NR
401 switch (action) {
402 case IMPORT:
92fb0719 403 exitCode = imprt(urlString, pg);
b42117f1 404 updates.ok(); // we consider it read
d0114000
NR
405 break;
406 case EXPORT:
b0e88ebd 407 exitCode = export(luid, sourceString, target, pg);
b42117f1 408 updates.ok(); // we consider it read
d0114000
NR
409 break;
410 case CONVERT:
b0e88ebd 411 exitCode = convert(urlString, sourceString, target,
92fb0719 412 plusInfo == null ? false : plusInfo, pg);
b42117f1 413 updates.ok(); // we consider it read
d0114000
NR
414 break;
415 case LIST:
99ccbdf6 416 if (BasicReader.getReader() == null) {
62c63b07
NR
417 Instance.getTraceHandler()
418 .error(new Exception(
419 "No reader type has been configured"));
99ccbdf6
NR
420 exitCode = 10;
421 break;
422 }
b0e88ebd 423 exitCode = list(sourceString);
d0114000 424 break;
e10b51a2
NR
425 case SET_SOURCE:
426 try {
427 Instance.getLibrary().changeSource(luid, sourceString, pg);
428 } catch (IOException e1) {
429 Instance.getTraceHandler().error(e1);
430 exitCode = 21;
431 }
432 break;
433 case SET_TITLE:
434 try {
435 Instance.getLibrary().changeTitle(luid, titleString, pg);
436 } catch (IOException e1) {
437 Instance.getTraceHandler().error(e1);
438 exitCode = 22;
439 }
440 break;
441 case SET_AUTHOR:
442 try {
443 Instance.getLibrary().changeAuthor(luid, authorString, pg);
444 } catch (IOException e1) {
445 Instance.getTraceHandler().error(e1);
446 exitCode = 23;
447 }
448 break;
d0114000 449 case READ:
99ccbdf6 450 if (BasicReader.getReader() == null) {
62c63b07
NR
451 Instance.getTraceHandler()
452 .error(new Exception(
453 "No reader type has been configured"));
99ccbdf6
NR
454 exitCode = 10;
455 break;
456 }
d0114000
NR
457 exitCode = read(luid, chapString, true);
458 break;
459 case READ_URL:
99ccbdf6 460 if (BasicReader.getReader() == null) {
62c63b07
NR
461 Instance.getTraceHandler()
462 .error(new Exception(
463 "No reader type has been configured"));
99ccbdf6
NR
464 exitCode = 10;
465 break;
466 }
d0114000 467 exitCode = read(urlString, chapString, false);
91b82a5c
NR
468 break;
469 case SEARCH:
8b153400
NR
470 page = page == null ? 1 : page;
471 if (page < 0) {
472 Instance.getTraceHandler().error("Incorrect page number");
91b82a5c
NR
473 exitCode = 255;
474 break;
475 }
8b153400
NR
476
477 item = item == null ? 0 : item;
478 if (item < 0) {
479 Instance.getTraceHandler().error("Incorrect item number");
480 exitCode = 255;
481 break;
91b82a5c 482 }
8b153400 483
91b82a5c
NR
484 if (BasicReader.getReader() == null) {
485 Instance.getTraceHandler()
486 .error(new Exception(
487 "No reader type has been configured"));
488 exitCode = 10;
489 break;
490 }
8b153400 491
b31a0db0
NR
492 try {
493 if (searchOn == null) {
494 BasicReader.getReader().search(true);
495 } else if (search != null) {
496
8b153400 497 BasicReader.getReader().search(searchOn, search, page,
f76de465 498 item, true);
b31a0db0
NR
499 } else {
500 exitCode = 255;
8b153400 501 }
b31a0db0
NR
502 } catch (IOException e1) {
503 Instance.getTraceHandler().error(e1);
504 exitCode = 20;
91b82a5c 505 }
8b153400 506
91b82a5c 507 break;
8141d7ac 508 case SEARCH_TAG:
91b82a5c
NR
509 if (searchOn == null) {
510 exitCode = 255;
511 break;
512 }
91b82a5c 513
8b153400
NR
514 page = page == null ? 1 : page;
515 if (page < 0) {
516 Instance.getTraceHandler().error("Incorrect page number");
517 exitCode = 255;
518 break;
91b82a5c 519 }
8b153400
NR
520
521 item = item == null ? 0 : item;
522 if (item < 0) {
523 Instance.getTraceHandler().error("Incorrect item number");
524 exitCode = 255;
525 break;
526 }
527
91b82a5c
NR
528 if (BasicReader.getReader() == null) {
529 Instance.getTraceHandler()
530 .error(new Exception(
531 "No reader type has been configured"));
532 exitCode = 10;
533 break;
534 }
8b153400 535
91b82a5c 536 try {
8b153400 537 BasicReader.getReader().searchTag(searchOn, page, item,
f76de465 538 true, tags.toArray(new Integer[] {}));
91b82a5c
NR
539 } catch (IOException e1) {
540 Instance.getTraceHandler().error(e1);
541 }
8b153400 542
d0114000
NR
543 break;
544 case HELP:
545 syntax(true);
546 exitCode = 0;
547 break;
548 case SET_READER:
b0e88ebd 549 exitCode = 255;
d0114000 550 break;
39c3c689
NR
551 case VERSION:
552 System.out
553 .println(String.format("Fanfix version %s"
9fe3f177
NR
554 + "%nhttps://github.com/nikiroo/fanfix/"
555 + "%n\tWritten by Nikiroo",
39c3c689 556 Version.getCurrentVersion()));
b42117f1 557 updates.ok(); // we consider it read
39c3c689 558 break;
333f0e7b 559 case START:
99ccbdf6 560 if (BasicReader.getReader() == null) {
62c63b07
NR
561 Instance.getTraceHandler()
562 .error(new Exception(
563 "No reader type has been configured"));
99ccbdf6
NR
564 exitCode = 10;
565 break;
566 }
b0e88ebd
NR
567 BasicReader.getReader().browse(null);
568 break;
569 case SERVER:
fb25273c
NR
570 key = Instance.getConfig().getString(Config.SERVER_KEY);
571 port = Instance.getConfig().getInteger(Config.SERVER_PORT);
b0e88ebd 572 if (port == null) {
fb25273c
NR
573 System.err.println("No port configured in the config file");
574 exitCode = 15;
b0e88ebd
NR
575 break;
576 }
577 try {
62c63b07 578 ServerObject server = new RemoteLibraryServer(key, port);
22b2b942 579 server.setTraceHandler(Instance.getTraceHandler());
edf79e5e 580 server.run();
b0e88ebd 581 } catch (IOException e) {
62c63b07 582 Instance.getTraceHandler().error(e);
b0e88ebd
NR
583 }
584 return;
5e848e6a 585 case STOP_SERVER:
fb25273c
NR
586 key = Instance.getConfig().getString(Config.SERVER_KEY);
587 port = Instance.getConfig().getInteger(Config.SERVER_PORT);
5e848e6a 588 if (port == null) {
fb25273c
NR
589 System.err.println("No port configured in the config file");
590 exitCode = 15;
5e848e6a
NR
591 break;
592 }
593
468b960b 594 new RemoteLibrary(key, host, port).exit();
5e848e6a 595 break;
b0e88ebd 596 case REMOTE:
99ccbdf6 597 exitCode = 255; // should not be reachable (REMOTE -> START)
333f0e7b 598 break;
08fe2e33
NR
599 }
600 }
601
350bc060
NR
602 try {
603 Instance.getTempFiles().close();
604 } catch (IOException e) {
605 Instance.getTraceHandler()
606 .error(new IOException(
607 "Cannot dispose of the temporary files", e));
2aac79c7
NR
608 }
609
08fe2e33 610 if (exitCode == 255) {
d0114000 611 syntax(false);
08fe2e33
NR
612 }
613
31e28683 614 System.exit(exitCode);
08fe2e33
NR
615 }
616
08fe2e33 617 /**
68e2c6d2 618 * Import the given resource into the {@link LocalLibrary}.
08fe2e33 619 *
d0114000 620 * @param urlString
08fe2e33 621 * the resource to import
92fb0719
NR
622 * @param pg
623 * the optional progress reporter
08fe2e33
NR
624 *
625 * @return the exit return code (0 = success)
626 */
92fb0719 627 public static int imprt(String urlString, Progress pg) {
08fe2e33 628 try {
3b2b638f
NR
629 Story story = Instance.getLibrary().imprt(
630 BasicReader.getUrl(urlString), pg);
08fe2e33
NR
631 System.out.println(story.getMeta().getLuid() + ": \""
632 + story.getMeta().getTitle() + "\" imported.");
633 } catch (IOException e) {
62c63b07 634 Instance.getTraceHandler().error(e);
08fe2e33
NR
635 return 1;
636 }
637
638 return 0;
639 }
640
641 /**
68e2c6d2
NR
642 * Export the {@link Story} from the {@link LocalLibrary} to the given
643 * target.
08fe2e33 644 *
73ce17ef 645 * @param luid
08fe2e33
NR
646 * the story LUID
647 * @param typeString
648 * the {@link OutputType} to use
649 * @param target
650 * the target
92fb0719
NR
651 * @param pg
652 * the optional progress reporter
08fe2e33
NR
653 *
654 * @return the exit return code (0 = success)
655 */
92fb0719
NR
656 public static int export(String luid, String typeString, String target,
657 Progress pg) {
e604986c 658 OutputType type = OutputType.valueOfNullOkUC(typeString, null);
08fe2e33 659 if (type == null) {
62c63b07
NR
660 Instance.getTraceHandler().error(
661 new Exception(trans(StringId.OUTPUT_DESC, typeString)));
08fe2e33
NR
662 return 1;
663 }
664
665 try {
92fb0719 666 Instance.getLibrary().export(luid, type, target, pg);
08fe2e33 667 } catch (IOException e) {
62c63b07 668 Instance.getTraceHandler().error(e);
08fe2e33
NR
669 return 4;
670 }
671
672 return 0;
673 }
674
675 /**
68e2c6d2
NR
676 * List the stories of the given source from the {@link LocalLibrary}
677 * (unless NULL is passed, in which case all stories will be listed).
08fe2e33 678 *
b0e88ebd
NR
679 * @param source
680 * the source to list the known stories of, or NULL to list all
333f0e7b 681 * stories
08fe2e33
NR
682 *
683 * @return the exit return code (0 = success)
684 */
b0e88ebd 685 private static int list(String source) {
f569d249
NR
686 List<MetaData> stories;
687 stories = BasicReader.getReader().getLibrary().getListBySource(source);
688
689 for (MetaData story : stories) {
690 String author = "";
691 if (story.getAuthor() != null && !story.getAuthor().isEmpty()) {
692 author = " (" + story.getAuthor() + ")";
693 }
694
695 System.out.println(story.getLuid() + ": " + story.getTitle()
696 + author);
697 }
08fe2e33
NR
698 return 0;
699 }
700
701 /**
350bc060 702 * Start the current reader for this {@link Story}.
08fe2e33
NR
703 *
704 * @param story
68e2c6d2
NR
705 * the LUID of the {@link Story} in the {@link LocalLibrary}
706 * <b>or</b> the {@link Story} {@link URL}
d0114000 707 * @param chapString
08fe2e33
NR
708 * which {@link Chapter} to read (starting at 1), or NULL to get
709 * the {@link Story} description
710 * @param library
711 * TRUE if the source is the {@link Story} LUID, FALSE if it is a
712 * {@link URL}
713 *
714 * @return the exit return code (0 = success)
715 */
d0114000 716 private static int read(String story, String chapString, boolean library) {
08fe2e33 717 try {
e42573a0 718 Reader reader = BasicReader.getReader();
08fe2e33 719 if (library) {
bc2ea776 720 reader.setMeta(story);
08fe2e33 721 } else {
bc2ea776 722 reader.setMeta(BasicReader.getUrl(story), null);
08fe2e33
NR
723 }
724
d0114000
NR
725 if (chapString != null) {
726 try {
bc2ea776 727 reader.setChapter(Integer.parseInt(chapString));
350bc060 728 reader.read(true);
d0114000 729 } catch (NumberFormatException e) {
62c63b07
NR
730 Instance.getTraceHandler().error(
731 new IOException("Chapter number cannot be parsed: "
732 + chapString, e));
d0114000
NR
733 return 2;
734 }
08fe2e33 735 } else {
350bc060 736 reader.read(true);
08fe2e33
NR
737 }
738 } catch (IOException e) {
62c63b07 739 Instance.getTraceHandler().error(e);
08fe2e33
NR
740 return 1;
741 }
742
743 return 0;
744 }
745
746 /**
747 * Convert the {@link Story} into another format.
748 *
d0114000 749 * @param urlString
08fe2e33
NR
750 * the source {@link Story} to convert
751 * @param typeString
752 * the {@link OutputType} to convert to
d0114000 753 * @param target
08fe2e33
NR
754 * the target file
755 * @param infoCover
756 * TRUE to also export the cover and info file, even if the given
757 * {@link OutputType} does not usually save them
92fb0719
NR
758 * @param pg
759 * the optional progress reporter
08fe2e33
NR
760 *
761 * @return the exit return code (0 = success)
762 */
f7460e4c 763 public static int convert(String urlString, String typeString,
92fb0719 764 String target, boolean infoCover, Progress pg) {
08fe2e33
NR
765 int exitCode = 0;
766
826e4569 767 Instance.getTraceHandler().trace("Convert: " + urlString);
d0114000 768 String sourceName = urlString;
08fe2e33 769 try {
3b2b638f 770 URL source = BasicReader.getUrl(urlString);
08fe2e33
NR
771 sourceName = source.toString();
772 if (source.toString().startsWith("file://")) {
773 sourceName = sourceName.substring("file://".length());
774 }
775
e604986c 776 OutputType type = OutputType.valueOfAllOkUC(typeString, null);
08fe2e33 777 if (type == null) {
62c63b07
NR
778 Instance.getTraceHandler().error(
779 new IOException(trans(StringId.ERR_BAD_OUTPUT_TYPE,
780 typeString)));
08fe2e33
NR
781
782 exitCode = 2;
783 } else {
784 try {
785 BasicSupport support = BasicSupport.getSupport(source);
333f0e7b 786
08fe2e33 787 if (support != null) {
350bc060
NR
788 Instance.getTraceHandler().trace(
789 "Support found: " + support.getClass());
bee7dffe
NR
790 Progress pgIn = new Progress();
791 Progress pgOut = new Progress();
792 if (pg != null) {
793 pg.setMax(2);
794 pg.addProgress(pgIn, 1);
795 pg.addProgress(pgOut, 1);
796 }
08fe2e33 797
0ffa4754 798 Story story = support.process(pgIn);
08fe2e33 799 try {
d0114000 800 target = new File(target).getAbsolutePath();
925298fd
NR
801 BasicOutput.getOutput(type, infoCover, infoCover)
802 .process(story, target, pgOut);
08fe2e33 803 } catch (IOException e) {
62c63b07
NR
804 Instance.getTraceHandler().error(
805 new IOException(trans(StringId.ERR_SAVING,
806 target), e));
08fe2e33
NR
807 exitCode = 5;
808 }
809 } else {
62c63b07
NR
810 Instance.getTraceHandler().error(
811 new IOException(trans(
812 StringId.ERR_NOT_SUPPORTED, source)));
08fe2e33
NR
813
814 exitCode = 4;
815 }
816 } catch (IOException e) {
62c63b07
NR
817 Instance.getTraceHandler().error(
818 new IOException(trans(StringId.ERR_LOADING,
819 sourceName), e));
08fe2e33
NR
820 exitCode = 3;
821 }
822 }
823 } catch (MalformedURLException e) {
62c63b07
NR
824 Instance.getTraceHandler()
825 .error(new IOException(trans(StringId.ERR_BAD_URL,
826 sourceName), e));
08fe2e33
NR
827 exitCode = 1;
828 }
829
830 return exitCode;
831 }
832
833 /**
834 * Simple shortcut method to call {link Instance#getTrans()#getString()}.
835 *
836 * @param id
837 * the ID to translate
838 *
839 * @return the translated result
840 */
841 private static String trans(StringId id, Object... params) {
842 return Instance.getTrans().getString(id, params);
843 }
844
845 /**
d0114000
NR
846 * Display the correct syntax of the program to the user to stdout, or an
847 * error message if the syntax used was wrong on stderr.
848 *
849 * @param showHelp
850 * TRUE to show the syntax help, FALSE to show "syntax error"
08fe2e33 851 */
d0114000
NR
852 private static void syntax(boolean showHelp) {
853 if (showHelp) {
854 StringBuilder builder = new StringBuilder();
855 for (SupportType type : SupportType.values()) {
856 builder.append(trans(StringId.ERR_SYNTAX_TYPE, type.toString(),
857 type.getDesc()));
858 builder.append('\n');
859 }
08fe2e33 860
d0114000
NR
861 String typesIn = builder.toString();
862 builder.setLength(0);
08fe2e33 863
d0114000
NR
864 for (OutputType type : OutputType.values()) {
865 builder.append(trans(StringId.ERR_SYNTAX_TYPE, type.toString(),
4d205683 866 type.getDesc(true)));
d0114000
NR
867 builder.append('\n');
868 }
08fe2e33 869
d0114000 870 String typesOut = builder.toString();
08fe2e33 871
d0114000
NR
872 System.out.println(trans(StringId.HELP_SYNTAX, typesIn, typesOut));
873 } else {
874 System.err.println(trans(StringId.ERR_SYNTAX));
875 }
876 }
877
878 /**
879 * Set the default reader type for this session only (it can be changed in
880 * the configuration file, too, but this value will override it).
881 *
882 * @param readerTypeString
883 * the type
884 */
885 private static int setReaderType(String readerTypeString) {
886 try {
7de079f1
NR
887 ReaderType readerType = ReaderType.valueOf(readerTypeString
888 .toUpperCase());
d0114000
NR
889 BasicReader.setDefaultReaderType(readerType);
890 return 0;
891 } catch (IllegalArgumentException e) {
62c63b07
NR
892 Instance.getTraceHandler().error(
893 new IOException("Unknown reader type: " + readerTypeString,
894 e));
d0114000
NR
895 return 1;
896 }
08fe2e33
NR
897 }
898}