fix TUI
[nikiroo-utils.git] / src / be / nikiroo / fanfix / Instance.java
CommitLineData
08fe2e33
NR
1package be.nikiroo.fanfix;
2
3import java.io.File;
4import java.io.IOException;
b42117f1 5import java.util.Date;
08fe2e33
NR
6
7import be.nikiroo.fanfix.bundles.Config;
8import be.nikiroo.fanfix.bundles.ConfigBundle;
99ccbdf6 9import be.nikiroo.fanfix.bundles.StringId;
08fe2e33 10import be.nikiroo.fanfix.bundles.StringIdBundle;
5bc9573b 11import be.nikiroo.fanfix.bundles.StringIdGuiBundle;
b4dc6ab5
NR
12import be.nikiroo.fanfix.bundles.UiConfig;
13import be.nikiroo.fanfix.bundles.UiConfigBundle;
e42573a0 14import be.nikiroo.fanfix.library.BasicLibrary;
5895a958 15import be.nikiroo.fanfix.library.CacheLibrary;
e42573a0 16import be.nikiroo.fanfix.library.LocalLibrary;
e023483b 17import be.nikiroo.fanfix.library.RemoteLibrary;
581d42c0 18import be.nikiroo.utils.Cache;
b42117f1 19import be.nikiroo.utils.IOUtils;
12443642 20import be.nikiroo.utils.Image;
dddfac1d 21import be.nikiroo.utils.Proxy;
2aac79c7 22import be.nikiroo.utils.TempFiles;
581d42c0 23import be.nikiroo.utils.TraceHandler;
08fe2e33
NR
24import be.nikiroo.utils.resources.Bundles;
25
26/**
27 * Global state for the program (services and singletons).
28 *
29 * @author niki
30 */
31public class Instance {
32 private static ConfigBundle config;
b4dc6ab5 33 private static UiConfigBundle uiconfig;
08fe2e33 34 private static StringIdBundle trans;
f1fb834c 35 private static DataLoader cache;
5bc9573b 36 private static StringIdGuiBundle transGui;
e023483b 37 private static BasicLibrary lib;
08fe2e33 38 private static File coverDir;
3727aae2 39 private static File readerTmp;
b0e88ebd 40 private static File remoteDir;
b42117f1 41 private static String configDir;
581d42c0 42 private static TraceHandler tracer;
2aac79c7 43 private static TempFiles tempFiles;
a8209dd0 44
ee9b7083
NR
45 private static boolean init;
46
47 /**
48 * Initialise the instance -- if already initialised, nothing will happen.
49 * <p>
6a211e41
NR
50 * Before calling this method, you may call
51 * {@link Bundles#setDirectory(String)} if wanted.
ee9b7083
NR
52 */
53 static public void init() {
6a211e41
NR
54 init(false);
55 }
56
57 /**
58 * Initialise the instance -- if already initialised, nothing will happen
59 * unless you pass TRUE to <tt>force</tt>.
60 * <p>
61 * Before calling this method, you may call
62 * {@link Bundles#setDirectory(String)} if wanted.
63 * <p>
64 * Note: forcing the initialisation can be dangerous, so make sure to only
65 * make it under controlled circumstances -- for instance, at the start of
66 * the program, you could call {@link Instance#init()}, change some settings
67 * because you want to force those settings (it will also forbid users to
68 * change them!) and then call {@link Instance#init(boolean)} with
69 * <tt>force</tt> set to TRUE.
70 *
71 * @param force
72 * force the initialisation even if already initialised
73 */
74 static public void init(boolean force) {
75 if (init && !force) {
ee9b7083
NR
76 return;
77 }
78
79 init = true;
80
62c63b07 81 // Before we can configure it:
a3c35586
NR
82 Boolean debug = checkEnv("DEBUG");
83 boolean trace = debug != null && debug;
84 tracer = new TraceHandler(true, trace, trace);
62c63b07 85
9f705f1a
NR
86 // config dir:
87 configDir = getConfigDir();
b42117f1
NR
88 if (!new File(configDir).exists()) {
89 new File(configDir).mkdirs();
fe999aa4
NR
90 }
91
9f705f1a 92 // Most of the rest is dependent upon this:
38febea9 93 createConfigs(configDir, false);
a3c35586 94
4ff0b1a9 95 // Proxy support
dddfac1d 96 Proxy.use(Instance.getConfig().getString(Config.USE_PROXY));
2206ef66 97
9f705f1a 98 // update tracer:
a3c35586
NR
99 if (debug == null) {
100 debug = Instance.getConfig().getBoolean(Config.DEBUG_ERR, false);
101 trace = Instance.getConfig().getBoolean(Config.DEBUG_TRACE, false);
d0114000
NR
102 }
103
62c63b07 104 tracer = new TraceHandler(true, debug, trace);
581d42c0 105
9f705f1a
NR
106 // default Library
107 remoteDir = new File(configDir, "remote");
108 lib = createDefaultLibrary(remoteDir);
778d8d85 109
12443642
NR
110 // create cache and TMP
111 Image.setTemporaryFilesRoot(new File(configDir, "tmp.images"));
9f705f1a 112 File tmp = getFile(Config.CACHE_DIR);
68e370a4 113 if (tmp == null) {
9f705f1a 114 // Could have used: System.getProperty("java.io.tmpdir")
68e370a4
NR
115 tmp = new File(configDir, "tmp");
116 }
ae78e517 117 String ua = config.getString(Config.USER_AGENT);
08fe2e33 118 try {
08fe2e33
NR
119 int hours = config.getInteger(Config.CACHE_MAX_TIME_CHANGING, -1);
120 int hoursLarge = config
121 .getInteger(Config.CACHE_MAX_TIME_STABLE, -1);
f1fb834c 122 cache = new DataLoader(tmp, ua, hours, hoursLarge);
08fe2e33 123 } catch (IOException e) {
62c63b07 124 tracer.error(new IOException(
08fe2e33 125 "Cannot create cache (will continue without cache)", e));
ae78e517 126 cache = new DataLoader(ua);
08fe2e33 127 }
ae78e517
NR
128
129 cache.setTraceHandler(tracer);
9f705f1a
NR
130
131 // readerTmp / coverDir
132 readerTmp = getFile(UiConfig.CACHE_DIR_LOCAL_READER);
133 if (readerTmp == null) {
134 readerTmp = new File(configDir, "tmp-reader");
135 }
136
137 coverDir = getFile(Config.DEFAULT_COVERS_DIR);
138 if (coverDir != null && !coverDir.exists()) {
139 tracer.error(new IOException(
140 "The 'default covers' directory does not exists: "
141 + coverDir));
142 coverDir = null;
143 }
2aac79c7
NR
144
145 try {
146 tempFiles = new TempFiles("fanfix");
147 } catch (IOException e) {
148 tracer.error(new IOException("Cannot create temporary directory", e));
149 }
08fe2e33
NR
150 }
151
581d42c0
NR
152 /**
153 * The traces handler for this {@link Cache}.
62c63b07
NR
154 * <p>
155 * It is never NULL.
581d42c0 156 *
62c63b07 157 * @return the traces handler (never NULL)
581d42c0
NR
158 */
159 public static TraceHandler getTraceHandler() {
160 return tracer;
161 }
162
163 /**
164 * The traces handler for this {@link Cache}.
165 *
166 * @param tracer
167 * the new traces handler or NULL
168 */
169 public static void setTraceHandler(TraceHandler tracer) {
62c63b07
NR
170 if (tracer == null) {
171 tracer = new TraceHandler(false, false, false);
172 }
173
581d42c0 174 Instance.tracer = tracer;
ae78e517 175 cache.setTraceHandler(tracer);
581d42c0
NR
176 }
177
08fe2e33
NR
178 /**
179 * Get the (unique) configuration service for the program.
180 *
181 * @return the configuration service
182 */
183 public static ConfigBundle getConfig() {
184 return config;
185 }
186
b4dc6ab5
NR
187 /**
188 * Get the (unique) UI configuration service for the program.
189 *
190 * @return the configuration service
191 */
192 public static UiConfigBundle getUiConfig() {
193 return uiconfig;
194 }
195
ae78e517
NR
196 /**
197 * Reset the configuration.
198 *
199 * @param resetTrans
200 * also reset the translation files
201 */
202 public static void resetConfig(boolean resetTrans) {
203 String dir = Bundles.getDirectory();
204 Bundles.setDirectory(null);
205 try {
206 try {
207 ConfigBundle config = new ConfigBundle();
208 config.updateFile(configDir);
209 } catch (IOException e) {
210 tracer.error(e);
211 }
212 try {
213 UiConfigBundle uiconfig = new UiConfigBundle();
214 uiconfig.updateFile(configDir);
215 } catch (IOException e) {
216 tracer.error(e);
217 }
218
219 if (resetTrans) {
220 try {
221 StringIdBundle trans = new StringIdBundle(null);
222 trans.updateFile(configDir);
223 } catch (IOException e) {
224 tracer.error(e);
225 }
226 }
227 } finally {
228 Bundles.setDirectory(dir);
229 }
230 }
231
08fe2e33 232 /**
f1fb834c 233 * Get the (unique) {@link DataLoader} for the program.
08fe2e33 234 *
f1fb834c 235 * @return the {@link DataLoader}
08fe2e33 236 */
f1fb834c 237 public static DataLoader getCache() {
08fe2e33
NR
238 return cache;
239 }
240
241 /**
242 * Get the (unique) {link StringIdBundle} for the program.
5bc9573b
NR
243 * <p>
244 * This is used for the translations of the core parts of Fanfix.
39c3c689 245 *
08fe2e33
NR
246 * @return the {link StringIdBundle}
247 */
248 public static StringIdBundle getTrans() {
249 return trans;
250 }
251
5bc9573b
NR
252 /**
253 * Get the (unique) {link StringIdGuiBundle} for the program.
254 * <p>
255 * This is used for the translations of the GUI parts of Fanfix.
256 *
257 * @return the {link StringIdGuiBundle}
258 */
259 public static StringIdGuiBundle getTransGui() {
260 return transGui;
261 }
262
08fe2e33 263 /**
68e2c6d2 264 * Get the (unique) {@link LocalLibrary} for the program.
08fe2e33 265 *
68e2c6d2 266 * @return the {@link LocalLibrary}
08fe2e33 267 */
68e2c6d2 268 public static BasicLibrary getLibrary() {
778d8d85
NR
269 if (lib == null) {
270 throw new NullPointerException("We don't have a library to return");
271 }
272
08fe2e33
NR
273 return lib;
274 }
275
276 /**
277 * Return the directory where to look for default cover pages.
278 *
279 * @return the default covers directory
280 */
281 public static File getCoverDir() {
282 return coverDir;
283 }
284
3727aae2
NR
285 /**
286 * Return the directory where to store temporary files for the local reader.
287 *
288 * @return the directory
289 */
290 public static File getReaderDir() {
291 return readerTmp;
292 }
293
b0e88ebd
NR
294 /**
295 * Return the directory where to store temporary files for the remote
68e2c6d2 296 * {@link LocalLibrary}.
b0e88ebd
NR
297 *
298 * @param host
299 * the remote for this host
300 *
301 * @return the directory
302 */
303 public static File getRemoteDir(String host) {
9f705f1a
NR
304 return getRemoteDir(remoteDir, host);
305 }
306
307 /**
308 * Return the directory where to store temporary files for the remote
309 * {@link LocalLibrary}.
310 *
311 * @param remoteDir
312 * the base remote directory
313 * @param host
314 * the remote for this host
315 *
316 * @return the directory
317 */
318 private static File getRemoteDir(File remoteDir, String host) {
b0e88ebd
NR
319 remoteDir.mkdirs();
320
321 if (host != null) {
322 return new File(remoteDir, host);
323 }
324
325 return remoteDir;
326 }
327
b42117f1
NR
328 /**
329 * Check if we need to check that a new version of Fanfix is available.
330 *
331 * @return TRUE if we need to
332 */
333 public static boolean isVersionCheckNeeded() {
334 try {
a3641e4b
NR
335 long wait = config.getInteger(Config.UPDATE_INTERVAL, 1) * 24 * 60
336 * 60 * 1000;
b42117f1
NR
337 if (wait >= 0) {
338 String lastUpString = IOUtils.readSmallFile(new File(configDir,
339 "LAST_UPDATE"));
340 long delay = new Date().getTime()
341 - Long.parseLong(lastUpString);
342 if (delay > wait) {
343 return true;
344 }
345 } else {
346 return false;
347 }
348 } catch (Exception e) {
349 // No file or bad file:
350 return true;
351 }
352
353 return false;
354 }
355
356 /**
357 * Notify that we checked for a new version of Fanfix.
358 */
359 public static void setVersionChecked() {
360 try {
361 IOUtils.writeSmallFile(new File(configDir), "LAST_UPDATE",
362 Long.toString(new Date().getTime()));
363 } catch (IOException e) {
581d42c0 364 tracer.error(e);
08fe2e33
NR
365 }
366 }
367
2aac79c7
NR
368 /**
369 * The facility to use temporary files in this program.
370 * <p>
371 * <b>MUST</b> be closed at end of program.
372 *
373 * @return the facility
374 */
375 public static TempFiles getTempFiles() {
376 return tempFiles;
377 }
378
9f705f1a
NR
379 /**
380 * The configuration directory (will check, in order of preference,
381 * {@link Bundles#getDirectory()}, the system properties, the environment
382 * and then defaults to $HOME/.fanfix).
383 *
384 * @return the config directory
385 */
386 private static String getConfigDir() {
387 String configDir = Bundles.getDirectory();
388
389 if (configDir == null) {
390 configDir = System.getProperty("CONFIG_DIR");
391 }
392
393 if (configDir == null) {
394 configDir = System.getenv("CONFIG_DIR");
395 }
396
397 if (configDir == null) {
398 configDir = new File(getHome(), ".fanfix").getPath();
399 }
400
401 return configDir;
402 }
403
404 /**
405 * Create the config variables ({@link Instance#config},
5bc9573b
NR
406 * {@link Instance#uiconfig}, {@link Instance#trans} and
407 * {@link Instance#transGui}).
9f705f1a
NR
408 *
409 * @param configDir
410 * the directory where to find the configuration files
411 * @param refresh
38febea9 412 * TRUE to reset the configuration files from the default
9f705f1a
NR
413 * included ones
414 */
415 private static void createConfigs(String configDir, boolean refresh) {
416 if (!refresh) {
417 Bundles.setDirectory(configDir);
418 }
419
420 try {
421 config = new ConfigBundle();
422 config.updateFile(configDir);
423 } catch (IOException e) {
424 tracer.error(e);
425 }
426
427 try {
428 uiconfig = new UiConfigBundle();
429 uiconfig.updateFile(configDir);
430 } catch (IOException e) {
431 tracer.error(e);
432 }
433
434 // No updateFile for this one! (we do not want the user to have custom
435 // translations that won't accept updates from newer versions)
436 trans = new StringIdBundle(getLang());
5bc9573b 437 transGui = new StringIdGuiBundle(getLang());
9f705f1a
NR
438
439 // Fix an old bug (we used to store custom translation files by
440 // default):
441 if (trans.getString(StringId.INPUT_DESC_CBZ) == null) {
442 trans.deleteFile(configDir);
443 }
444
a3c35586
NR
445 Boolean noutf = checkEnv("NOUTF");
446 if (noutf != null && noutf) {
9f705f1a 447 trans.setUnicode(false);
5bc9573b 448 transGui.setUnicode(false);
9f705f1a
NR
449 }
450
451 Bundles.setDirectory(configDir);
452 }
453
454 /**
455 * Create the default library as specified by the config.
456 *
457 * @param remoteDir
458 * the base remote directory if needed
459 *
460 * @return the default {@link BasicLibrary}
461 */
462 private static BasicLibrary createDefaultLibrary(File remoteDir) {
463 BasicLibrary lib = null;
464
465 String remoteLib = config.getString(Config.DEFAULT_LIBRARY);
466 if (remoteLib == null || remoteLib.trim().isEmpty()) {
467 String libDir = System.getProperty("fanfix.libdir");
468 if (libDir == null || libDir.isEmpty()) {
38febea9 469 libDir = config.getString(Config.LIBRARY_DIR);
9f705f1a
NR
470 }
471 try {
472 lib = new LocalLibrary(getFile(libDir));
473 } catch (Exception e) {
474 tracer.error(new IOException(
475 "Cannot create library for directory: "
476 + getFile(libDir), e));
477 }
478 } else {
fb25273c 479 Exception ex = null;
9f705f1a
NR
480 int pos = remoteLib.lastIndexOf(":");
481 if (pos >= 0) {
482 String port = remoteLib.substring(pos + 1).trim();
483 remoteLib = remoteLib.substring(0, pos);
484 pos = remoteLib.lastIndexOf(":");
485 if (pos >= 0) {
486 String host = remoteLib.substring(pos + 1).trim();
487 String key = remoteLib.substring(0, pos).trim();
488
489 try {
490 tracer.trace("Selecting remote library " + host + ":"
491 + port);
492 lib = new RemoteLibrary(key, host,
493 Integer.parseInt(port));
494 lib = new CacheLibrary(getRemoteDir(remoteDir, host),
495 lib);
496
497 } catch (Exception e) {
fb25273c 498 ex = e;
9f705f1a
NR
499 }
500 }
501 }
502
503 if (lib == null) {
504 tracer.error(new IOException(
fb25273c 505 "Cannot create remote library for: " + remoteLib, ex));
9f705f1a
NR
506 }
507 }
508
509 return lib;
510 }
511
08fe2e33
NR
512 /**
513 * Return a path, but support the special $HOME variable.
514 *
515 * @return the path
516 */
517 private static File getFile(Config id) {
b4dc6ab5
NR
518 return getFile(config.getString(id));
519 }
520
521 /**
522 * Return a path, but support the special $HOME variable.
523 *
524 * @return the path
525 */
526 private static File getFile(UiConfig id) {
527 return getFile(uiconfig.getString(id));
528 }
529
530 /**
531 * Return a path, but support the special $HOME variable.
532 *
533 * @return the path
534 */
535 private static File getFile(String path) {
08fe2e33 536 File file = null;
08fe2e33
NR
537 if (path != null && !path.isEmpty()) {
538 path = path.replace('/', File.separatorChar);
539 if (path.contains("$HOME")) {
ae78e517 540 path = path.replace("$HOME", getHome());
08fe2e33
NR
541 }
542
543 file = new File(path);
544 }
545
546 return file;
547 }
548
ae78e517
NR
549 /**
550 * Return the home directory from the system properties.
551 *
552 * @return the home
553 */
554 private static String getHome() {
b4f9071c
NR
555 String home = System.getProperty("fanfix.home");
556 if (home != null && new File(home).isFile()) {
557 home = null;
558 }
559
560 if (home == null || home.trim().isEmpty()) {
561 home = System.getProperty("user.home");
562 if (!new File(home).isDirectory()) {
563 home = null;
564 }
565 }
566
ae78e517
NR
567 if (home == null || home.trim().isEmpty()) {
568 home = System.getProperty("java.io.tmpdir");
b4f9071c
NR
569 if (!new File(home).isDirectory()) {
570 home = null;
571 }
ae78e517
NR
572 }
573
574 if (home == null) {
575 home = "";
576 }
577
578 return home;
579 }
580
08fe2e33
NR
581 /**
582 * The language to use for the application (NULL = default system language).
583 *
584 * @return the language
585 */
586 private static String getLang() {
587 String lang = config.getString(Config.LANG);
588
f83510cf 589 if (lang == null || lang.isEmpty()) {
fee80815
NR
590 if (System.getenv("LANG") != null
591 && !System.getenv("LANG").isEmpty()) {
592 lang = System.getenv("LANG");
593 }
08fe2e33
NR
594 }
595
596 if (lang != null && lang.isEmpty()) {
597 lang = null;
598 }
599
600 return lang;
601 }
d0114000
NR
602
603 /**
604 * Check that the given environment variable is "enabled".
605 *
606 * @param key
607 * the variable to check
608 *
609 * @return TRUE if it is
610 */
a3c35586 611 private static Boolean checkEnv(String key) {
d0114000
NR
612 String value = System.getenv(key);
613 if (value != null) {
614 value = value.trim().toLowerCase();
615 if ("yes".equals(value) || "true".equals(value)
616 || "on".equals(value) || "1".equals(value)
617 || "y".equals(value)) {
618 return true;
619 }
a3c35586
NR
620
621 return false;
d0114000
NR
622 }
623
a3c35586 624 return null;
d0114000 625 }
08fe2e33 626}