Commit | Line | Data |
---|---|---|
08fe2e33 NR |
1 | package be.nikiroo.fanfix; |
2 | ||
3 | import java.io.File; | |
4 | import java.io.IOException; | |
5 | import java.net.MalformedURLException; | |
6 | import java.net.URL; | |
91b82a5c | 7 | import java.util.ArrayList; |
f569d249 | 8 | import java.util.List; |
08fe2e33 | 9 | |
0bb51c9c NR |
10 | import javax.net.ssl.SSLException; |
11 | ||
fb25273c | 12 | import be.nikiroo.fanfix.bundles.Config; |
69f4b58e | 13 | import be.nikiroo.fanfix.bundles.ConfigBundle; |
08fe2e33 | 14 | import be.nikiroo.fanfix.bundles.StringId; |
69f4b58e | 15 | import be.nikiroo.fanfix.bundles.StringIdBundle; |
08fe2e33 | 16 | import be.nikiroo.fanfix.data.Chapter; |
b6b65795 | 17 | import be.nikiroo.fanfix.data.MetaData; |
08fe2e33 | 18 | import be.nikiroo.fanfix.data.Story; |
ff05b828 NR |
19 | import be.nikiroo.fanfix.library.BasicLibrary; |
20 | import be.nikiroo.fanfix.library.CacheLibrary; | |
e42573a0 NR |
21 | import be.nikiroo.fanfix.library.LocalLibrary; |
22 | import be.nikiroo.fanfix.library.RemoteLibrary; | |
23 | import be.nikiroo.fanfix.library.RemoteLibraryServer; | |
08fe2e33 NR |
24 | import be.nikiroo.fanfix.output.BasicOutput; |
25 | import be.nikiroo.fanfix.output.BasicOutput.OutputType; | |
3727aae2 | 26 | import be.nikiroo.fanfix.reader.BasicReader; |
e42573a0 NR |
27 | import be.nikiroo.fanfix.reader.Reader; |
28 | import be.nikiroo.fanfix.reader.Reader.ReaderType; | |
91b82a5c | 29 | import be.nikiroo.fanfix.searchable.BasicSearchable; |
08fe2e33 | 30 | import be.nikiroo.fanfix.supported.BasicSupport; |
0ffa4754 | 31 | import be.nikiroo.fanfix.supported.SupportType; |
3b2b638f | 32 | import be.nikiroo.utils.Progress; |
39c3c689 | 33 | import be.nikiroo.utils.Version; |
69f4b58e NR |
34 | import be.nikiroo.utils.resources.Bundles; |
35 | import be.nikiroo.utils.resources.TransBundle; | |
62c63b07 | 36 | import be.nikiroo.utils.serial.server.ServerObject; |
08fe2e33 NR |
37 | |
38 | /** | |
39 | * Main program entry point. | |
40 | * | |
41 | * @author niki | |
42 | */ | |
43 | public 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: |
fb25273c NR |
600 | key = Instance.getConfig().getString(Config.SERVER_KEY); |
601 | port = Instance.getConfig().getInteger(Config.SERVER_PORT); | |
5e848e6a | 602 | if (port == null) { |
fb25273c NR |
603 | System.err.println("No port configured in the config file"); |
604 | exitCode = 15; | |
5e848e6a NR |
605 | break; |
606 | } | |
0bb51c9c NR |
607 | try { |
608 | new RemoteLibrary(key, host, port).exit(); | |
609 | } catch (SSLException e) { | |
610 | Instance.getTraceHandler().error( | |
611 | "Bad access key for remote library"); | |
612 | exitCode = 43; | |
613 | } catch (IOException e) { | |
614 | Instance.getTraceHandler().error(e); | |
615 | exitCode = 44; | |
616 | } | |
5e848e6a | 617 | |
5e848e6a | 618 | break; |
b0e88ebd | 619 | case REMOTE: |
99ccbdf6 | 620 | exitCode = 255; // should not be reachable (REMOTE -> START) |
333f0e7b | 621 | break; |
08fe2e33 NR |
622 | } |
623 | } | |
624 | ||
350bc060 NR |
625 | try { |
626 | Instance.getTempFiles().close(); | |
627 | } catch (IOException e) { | |
628 | Instance.getTraceHandler() | |
629 | .error(new IOException( | |
630 | "Cannot dispose of the temporary files", e)); | |
2aac79c7 NR |
631 | } |
632 | ||
08fe2e33 | 633 | if (exitCode == 255) { |
d0114000 | 634 | syntax(false); |
08fe2e33 NR |
635 | } |
636 | ||
31e28683 | 637 | System.exit(exitCode); |
08fe2e33 NR |
638 | } |
639 | ||
08fe2e33 | 640 | /** |
68e2c6d2 | 641 | * Import the given resource into the {@link LocalLibrary}. |
08fe2e33 | 642 | * |
d0114000 | 643 | * @param urlString |
08fe2e33 | 644 | * the resource to import |
92fb0719 NR |
645 | * @param pg |
646 | * the optional progress reporter | |
08fe2e33 NR |
647 | * |
648 | * @return the exit return code (0 = success) | |
649 | */ | |
92fb0719 | 650 | public static int imprt(String urlString, Progress pg) { |
08fe2e33 | 651 | try { |
b6b65795 | 652 | MetaData meta = Instance.getLibrary().imprt( |
3b2b638f | 653 | BasicReader.getUrl(urlString), pg); |
b6b65795 NR |
654 | System.out.println(meta.getLuid() + ": \"" + meta.getTitle() |
655 | + "\" imported."); | |
08fe2e33 | 656 | } catch (IOException e) { |
62c63b07 | 657 | Instance.getTraceHandler().error(e); |
08fe2e33 NR |
658 | return 1; |
659 | } | |
660 | ||
661 | return 0; | |
662 | } | |
663 | ||
664 | /** | |
68e2c6d2 NR |
665 | * Export the {@link Story} from the {@link LocalLibrary} to the given |
666 | * target. | |
08fe2e33 | 667 | * |
73ce17ef | 668 | * @param luid |
08fe2e33 NR |
669 | * the story LUID |
670 | * @param typeString | |
671 | * the {@link OutputType} to use | |
672 | * @param target | |
673 | * the target | |
92fb0719 NR |
674 | * @param pg |
675 | * the optional progress reporter | |
08fe2e33 NR |
676 | * |
677 | * @return the exit return code (0 = success) | |
678 | */ | |
92fb0719 NR |
679 | public static int export(String luid, String typeString, String target, |
680 | Progress pg) { | |
e604986c | 681 | OutputType type = OutputType.valueOfNullOkUC(typeString, null); |
08fe2e33 | 682 | if (type == null) { |
62c63b07 NR |
683 | Instance.getTraceHandler().error( |
684 | new Exception(trans(StringId.OUTPUT_DESC, typeString))); | |
08fe2e33 NR |
685 | return 1; |
686 | } | |
687 | ||
688 | try { | |
92fb0719 | 689 | Instance.getLibrary().export(luid, type, target, pg); |
08fe2e33 | 690 | } catch (IOException e) { |
62c63b07 | 691 | Instance.getTraceHandler().error(e); |
08fe2e33 NR |
692 | return 4; |
693 | } | |
694 | ||
695 | return 0; | |
696 | } | |
697 | ||
698 | /** | |
68e2c6d2 NR |
699 | * List the stories of the given source from the {@link LocalLibrary} |
700 | * (unless NULL is passed, in which case all stories will be listed). | |
08fe2e33 | 701 | * |
b0e88ebd NR |
702 | * @param source |
703 | * the source to list the known stories of, or NULL to list all | |
333f0e7b | 704 | * stories |
08fe2e33 NR |
705 | * |
706 | * @return the exit return code (0 = success) | |
707 | */ | |
b0e88ebd | 708 | private static int list(String source) { |
0bb51c9c NR |
709 | BasicReader.setDefaultReaderType(ReaderType.CLI); |
710 | try { | |
711 | BasicReader.getReader().browse(source); | |
712 | } catch (IOException e) { | |
713 | Instance.getTraceHandler().error(e); | |
714 | return 66; | |
f569d249 | 715 | } |
0bb51c9c | 716 | |
08fe2e33 NR |
717 | return 0; |
718 | } | |
719 | ||
720 | /** | |
350bc060 | 721 | * Start the current reader for this {@link Story}. |
08fe2e33 NR |
722 | * |
723 | * @param story | |
68e2c6d2 NR |
724 | * the LUID of the {@link Story} in the {@link LocalLibrary} |
725 | * <b>or</b> the {@link Story} {@link URL} | |
d0114000 | 726 | * @param chapString |
08fe2e33 NR |
727 | * which {@link Chapter} to read (starting at 1), or NULL to get |
728 | * the {@link Story} description | |
729 | * @param library | |
730 | * TRUE if the source is the {@link Story} LUID, FALSE if it is a | |
731 | * {@link URL} | |
732 | * | |
733 | * @return the exit return code (0 = success) | |
734 | */ | |
d0114000 | 735 | private static int read(String story, String chapString, boolean library) { |
08fe2e33 | 736 | try { |
e42573a0 | 737 | Reader reader = BasicReader.getReader(); |
08fe2e33 | 738 | if (library) { |
bc2ea776 | 739 | reader.setMeta(story); |
08fe2e33 | 740 | } else { |
bc2ea776 | 741 | reader.setMeta(BasicReader.getUrl(story), null); |
08fe2e33 NR |
742 | } |
743 | ||
d0114000 NR |
744 | if (chapString != null) { |
745 | try { | |
bc2ea776 | 746 | reader.setChapter(Integer.parseInt(chapString)); |
350bc060 | 747 | reader.read(true); |
d0114000 | 748 | } catch (NumberFormatException e) { |
62c63b07 NR |
749 | Instance.getTraceHandler().error( |
750 | new IOException("Chapter number cannot be parsed: " | |
751 | + chapString, e)); | |
d0114000 NR |
752 | return 2; |
753 | } | |
08fe2e33 | 754 | } else { |
350bc060 | 755 | reader.read(true); |
08fe2e33 NR |
756 | } |
757 | } catch (IOException e) { | |
62c63b07 | 758 | Instance.getTraceHandler().error(e); |
08fe2e33 NR |
759 | return 1; |
760 | } | |
761 | ||
762 | return 0; | |
763 | } | |
764 | ||
765 | /** | |
766 | * Convert the {@link Story} into another format. | |
767 | * | |
d0114000 | 768 | * @param urlString |
08fe2e33 NR |
769 | * the source {@link Story} to convert |
770 | * @param typeString | |
771 | * the {@link OutputType} to convert to | |
d0114000 | 772 | * @param target |
08fe2e33 NR |
773 | * the target file |
774 | * @param infoCover | |
775 | * TRUE to also export the cover and info file, even if the given | |
776 | * {@link OutputType} does not usually save them | |
92fb0719 NR |
777 | * @param pg |
778 | * the optional progress reporter | |
08fe2e33 NR |
779 | * |
780 | * @return the exit return code (0 = success) | |
781 | */ | |
f7460e4c | 782 | public static int convert(String urlString, String typeString, |
92fb0719 | 783 | String target, boolean infoCover, Progress pg) { |
08fe2e33 NR |
784 | int exitCode = 0; |
785 | ||
826e4569 | 786 | Instance.getTraceHandler().trace("Convert: " + urlString); |
d0114000 | 787 | String sourceName = urlString; |
08fe2e33 | 788 | try { |
3b2b638f | 789 | URL source = BasicReader.getUrl(urlString); |
08fe2e33 NR |
790 | sourceName = source.toString(); |
791 | if (source.toString().startsWith("file://")) { | |
792 | sourceName = sourceName.substring("file://".length()); | |
793 | } | |
794 | ||
e604986c | 795 | OutputType type = OutputType.valueOfAllOkUC(typeString, null); |
08fe2e33 | 796 | if (type == null) { |
62c63b07 NR |
797 | Instance.getTraceHandler().error( |
798 | new IOException(trans(StringId.ERR_BAD_OUTPUT_TYPE, | |
799 | typeString))); | |
08fe2e33 NR |
800 | |
801 | exitCode = 2; | |
802 | } else { | |
803 | try { | |
804 | BasicSupport support = BasicSupport.getSupport(source); | |
333f0e7b | 805 | |
08fe2e33 | 806 | if (support != null) { |
350bc060 NR |
807 | Instance.getTraceHandler().trace( |
808 | "Support found: " + support.getClass()); | |
bee7dffe NR |
809 | Progress pgIn = new Progress(); |
810 | Progress pgOut = new Progress(); | |
811 | if (pg != null) { | |
812 | pg.setMax(2); | |
813 | pg.addProgress(pgIn, 1); | |
814 | pg.addProgress(pgOut, 1); | |
815 | } | |
08fe2e33 | 816 | |
0ffa4754 | 817 | Story story = support.process(pgIn); |
08fe2e33 | 818 | try { |
d0114000 | 819 | target = new File(target).getAbsolutePath(); |
925298fd NR |
820 | BasicOutput.getOutput(type, infoCover, infoCover) |
821 | .process(story, target, pgOut); | |
08fe2e33 | 822 | } catch (IOException e) { |
62c63b07 NR |
823 | Instance.getTraceHandler().error( |
824 | new IOException(trans(StringId.ERR_SAVING, | |
825 | target), e)); | |
08fe2e33 NR |
826 | exitCode = 5; |
827 | } | |
828 | } else { | |
62c63b07 NR |
829 | Instance.getTraceHandler().error( |
830 | new IOException(trans( | |
831 | StringId.ERR_NOT_SUPPORTED, source))); | |
08fe2e33 NR |
832 | |
833 | exitCode = 4; | |
834 | } | |
835 | } catch (IOException e) { | |
62c63b07 NR |
836 | Instance.getTraceHandler().error( |
837 | new IOException(trans(StringId.ERR_LOADING, | |
838 | sourceName), e)); | |
08fe2e33 NR |
839 | exitCode = 3; |
840 | } | |
841 | } | |
842 | } catch (MalformedURLException e) { | |
62c63b07 NR |
843 | Instance.getTraceHandler() |
844 | .error(new IOException(trans(StringId.ERR_BAD_URL, | |
845 | sourceName), e)); | |
08fe2e33 NR |
846 | exitCode = 1; |
847 | } | |
848 | ||
849 | return exitCode; | |
850 | } | |
851 | ||
852 | /** | |
853 | * Simple shortcut method to call {link Instance#getTrans()#getString()}. | |
854 | * | |
855 | * @param id | |
856 | * the ID to translate | |
857 | * | |
858 | * @return the translated result | |
859 | */ | |
860 | private static String trans(StringId id, Object... params) { | |
861 | return Instance.getTrans().getString(id, params); | |
862 | } | |
863 | ||
864 | /** | |
d0114000 NR |
865 | * Display the correct syntax of the program to the user to stdout, or an |
866 | * error message if the syntax used was wrong on stderr. | |
867 | * | |
868 | * @param showHelp | |
869 | * TRUE to show the syntax help, FALSE to show "syntax error" | |
08fe2e33 | 870 | */ |
d0114000 NR |
871 | private static void syntax(boolean showHelp) { |
872 | if (showHelp) { | |
873 | StringBuilder builder = new StringBuilder(); | |
874 | for (SupportType type : SupportType.values()) { | |
875 | builder.append(trans(StringId.ERR_SYNTAX_TYPE, type.toString(), | |
876 | type.getDesc())); | |
877 | builder.append('\n'); | |
878 | } | |
08fe2e33 | 879 | |
d0114000 NR |
880 | String typesIn = builder.toString(); |
881 | builder.setLength(0); | |
08fe2e33 | 882 | |
d0114000 NR |
883 | for (OutputType type : OutputType.values()) { |
884 | builder.append(trans(StringId.ERR_SYNTAX_TYPE, type.toString(), | |
4d205683 | 885 | type.getDesc(true))); |
d0114000 NR |
886 | builder.append('\n'); |
887 | } | |
08fe2e33 | 888 | |
d0114000 | 889 | String typesOut = builder.toString(); |
08fe2e33 | 890 | |
d0114000 NR |
891 | System.out.println(trans(StringId.HELP_SYNTAX, typesIn, typesOut)); |
892 | } else { | |
893 | System.err.println(trans(StringId.ERR_SYNTAX)); | |
894 | } | |
895 | } | |
896 | ||
897 | /** | |
898 | * Set the default reader type for this session only (it can be changed in | |
899 | * the configuration file, too, but this value will override it). | |
900 | * | |
901 | * @param readerTypeString | |
902 | * the type | |
903 | */ | |
904 | private static int setReaderType(String readerTypeString) { | |
905 | try { | |
7de079f1 NR |
906 | ReaderType readerType = ReaderType.valueOf(readerTypeString |
907 | .toUpperCase()); | |
d0114000 NR |
908 | BasicReader.setDefaultReaderType(readerType); |
909 | return 0; | |
910 | } catch (IllegalArgumentException e) { | |
62c63b07 NR |
911 | Instance.getTraceHandler().error( |
912 | new IOException("Unknown reader type: " + readerTypeString, | |
913 | e)); | |
d0114000 NR |
914 | return 1; |
915 | } | |
08fe2e33 NR |
916 | } |
917 | } |