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