cover: allow custom author covers
[fanfix.git] / src / be / nikiroo / fanfix / library / RemoteLibrary.java
CommitLineData
e42573a0 1package be.nikiroo.fanfix.library;
b0e88ebd
NR
2
3import java.io.File;
4import java.io.IOException;
edf79e5e 5import java.net.URL;
e6249b0f 6import java.net.UnknownHostException;
68e2c6d2
NR
7import java.util.ArrayList;
8import java.util.List;
b0e88ebd 9
e42573a0 10import be.nikiroo.fanfix.Instance;
b0e88ebd
NR
11import be.nikiroo.fanfix.data.MetaData;
12import be.nikiroo.fanfix.data.Story;
16a81ef7 13import be.nikiroo.utils.Image;
b0e88ebd 14import be.nikiroo.utils.Progress;
416c54f8 15import be.nikiroo.utils.StringUtils;
b0e88ebd 16import be.nikiroo.utils.Version;
62c63b07 17import be.nikiroo.utils.serial.server.ConnectActionClientObject;
b0e88ebd 18
68e2c6d2
NR
19/**
20 * This {@link BasicLibrary} will access a remote server to list the available
a85e8077 21 * stories, and download the ones you try to load to the local directory
68e2c6d2
NR
22 * specified in the configuration.
23 *
24 * @author niki
25 */
26public class RemoteLibrary extends BasicLibrary {
b0e88ebd
NR
27 private String host;
28 private int port;
416c54f8 29 private final String md5;
68e2c6d2
NR
30
31 /**
32 * Create a {@link RemoteLibrary} linked to the given server.
33 *
2070ced5
NR
34 * @param key
35 * the key that will allow us to exchange information with the
36 * server
68e2c6d2
NR
37 * @param host
38 * the host to contact or NULL for localhost
39 * @param port
40 * the port to contact it on
41 */
2070ced5 42 public RemoteLibrary(String key, String host, int port) {
416c54f8 43 this.md5 = StringUtils.getMd5Hash(key);
b0e88ebd
NR
44 this.host = host;
45 this.port = port;
b0e88ebd
NR
46 }
47
99ccbdf6
NR
48 @Override
49 public String getLibraryName() {
50 return host + ":" + port;
51 }
52
e6249b0f
NR
53 @Override
54 public Status getStatus() {
55 final Status[] result = new Status[1];
56
57 result[0] = Status.INVALID;
58
59 ConnectActionClientObject action = null;
60 try {
61 action = new ConnectActionClientObject(host, port, true) {
62 @Override
63 public void action(Version serverVersion) throws Exception {
416c54f8 64 Object rep = send(new Object[] { md5, "PING" });
e6249b0f
NR
65 if ("PONG".equals(rep)) {
66 result[0] = Status.READY;
67 } else {
68 result[0] = Status.UNAUTORIZED;
69 }
70 }
71
72 @Override
73 protected void onError(Exception e) {
74 result[0] = Status.UNAVAILABLE;
75 }
76 };
77
78 } catch (UnknownHostException e) {
79 result[0] = Status.INVALID;
80 } catch (IllegalArgumentException e) {
81 result[0] = Status.INVALID;
82 } catch (Exception e) {
83 result[0] = Status.UNAVAILABLE;
84 }
85
86 if (action != null) {
87 try {
88 action.connect();
89 } catch (Exception e) {
90 result[0] = Status.UNAVAILABLE;
91 }
92 }
93
94 return result[0];
95 }
96
b0e88ebd 97 @Override
16a81ef7
NR
98 public Image getCover(final String luid) {
99 final Image[] result = new Image[1];
b0e88ebd 100
b9ce9cad
NR
101 try {
102 new ConnectActionClientObject(host, port, true) {
103 @Override
104 public void action(Version serverVersion) throws Exception {
416c54f8 105 Object rep = send(new Object[] { md5, "GET_COVER", luid });
16a81ef7 106 result[0] = (Image) rep;
b9ce9cad 107 }
b0e88ebd 108
b9ce9cad
NR
109 @Override
110 protected void onError(Exception e) {
111 Instance.getTraceHandler().error(e);
112 }
113 }.connect();
114 } catch (Exception e) {
115 Instance.getTraceHandler().error(e);
116 }
b0e88ebd 117
b9ce9cad 118 return result[0];
085a2f9a
NR
119 }
120
121 @Override
e1de8087 122 public Image getCustomSourceCover(final String source) {
3989dfc5
NR
123 return getCustomCover(source, "SOURCE");
124 }
125
126 @Override
127 public Image getCustomAuthorCover(final String author) {
128 return getCustomCover(author, "AUTHOR");
129 }
130
131 // type: "SOURCE" or "AUTHOR"
132 private Image getCustomCover(final String source, final String type) {
16a81ef7 133 final Image[] result = new Image[1];
b9ce9cad
NR
134
135 try {
136 new ConnectActionClientObject(host, port, true) {
137 @Override
138 public void action(Version serverVersion) throws Exception {
3989dfc5
NR
139 Object rep = send(new Object[] { md5, "GET_CUSTOM_COVER",
140 type, source });
16a81ef7 141 result[0] = (Image) rep;
b9ce9cad
NR
142 }
143
144 @Override
145 protected void onError(Exception e) {
146 Instance.getTraceHandler().error(e);
147 }
148 }.connect();
149 } catch (Exception e) {
150 Instance.getTraceHandler().error(e);
151 }
152
153 return result[0];
b0e88ebd 154 }
68e2c6d2
NR
155
156 @Override
ff05b828 157 public synchronized Story getStory(final String luid, Progress pg) {
b9ce9cad
NR
158 final Progress pgF = pg;
159 final Story[] result = new Story[1];
68e2c6d2 160
b9ce9cad
NR
161 try {
162 new ConnectActionClientObject(host, port, true) {
163 @Override
164 public void action(Version serverVersion) throws Exception {
165 Progress pg = pgF;
166 if (pg == null) {
167 pg = new Progress();
168 }
169
416c54f8 170 Object rep = send(new Object[] { md5, "GET_STORY", luid });
b9ce9cad
NR
171
172 MetaData meta = null;
173 if (rep instanceof MetaData) {
174 meta = (MetaData) rep;
175 if (meta.getWords() <= Integer.MAX_VALUE) {
176 pg.setMinMax(0, (int) meta.getWords());
177 }
178 }
179
180 List<Object> list = new ArrayList<Object>();
181 for (Object obj = send(null); obj != null; obj = send(null)) {
182 list.add(obj);
183 pg.add(1);
184 }
185
186 result[0] = RemoteLibraryServer.rebuildStory(list);
187 pg.done();
188 }
189
190 @Override
191 protected void onError(Exception e) {
192 Instance.getTraceHandler().error(e);
193 }
194 }.connect();
195 } catch (Exception e) {
196 Instance.getTraceHandler().error(e);
197 }
198
199 return result[0];
68e2c6d2
NR
200 }
201
202 @Override
b9ce9cad
NR
203 public synchronized Story save(final Story story, final String luid,
204 Progress pg) throws IOException {
0fa0fe95
NR
205 final String[] luidSaved = new String[1];
206 Progress pgSave = new Progress();
207 Progress pgRefresh = new Progress();
208 if (pg == null) {
209 pg = new Progress();
210 }
211
212 pg.setMinMax(0, 10);
213 pg.addProgress(pgSave, 9);
214 pg.addProgress(pgRefresh, 1);
215
216 final Progress pgF = pgSave;
b9ce9cad
NR
217
218 new ConnectActionClientObject(host, port, true) {
219 @Override
220 public void action(Version serverVersion) throws Exception {
221 Progress pg = pgF;
b9ce9cad
NR
222 if (story.getMeta().getWords() <= Integer.MAX_VALUE) {
223 pg.setMinMax(0, (int) story.getMeta().getWords());
224 }
225
416c54f8 226 send(new Object[] { md5, "SAVE_STORY", luid });
b9ce9cad
NR
227
228 List<Object> list = RemoteLibraryServer.breakStory(story);
229 for (Object obj : list) {
230 send(obj);
231 pg.add(1);
232 }
233
edf79e5e 234 luidSaved[0] = (String) send(null);
0fa0fe95 235
b9ce9cad
NR
236 pg.done();
237 }
238
239 @Override
240 protected void onError(Exception e) {
241 Instance.getTraceHandler().error(e);
242 }
243 }.connect();
085a2f9a
NR
244
245 // because the meta changed:
edf79e5e 246 MetaData meta = getInfo(luidSaved[0]);
efa3c511
NR
247 if (story.getMeta().getClass() != null) {
248 // If already available locally:
249 meta.setCover(story.getMeta().getCover());
250 } else {
251 // If required:
252 meta.setCover(getCover(meta.getLuid()));
253 }
edf79e5e 254 story.setMeta(meta);
0fa0fe95
NR
255
256 pg.done();
085a2f9a
NR
257
258 return story;
68e2c6d2
NR
259 }
260
261 @Override
b9ce9cad
NR
262 public synchronized void delete(final String luid) throws IOException {
263 new ConnectActionClientObject(host, port, true) {
264 @Override
265 public void action(Version serverVersion) throws Exception {
416c54f8 266 send(new Object[] { md5, "DELETE_STORY", luid });
b9ce9cad
NR
267 }
268
269 @Override
270 protected void onError(Exception e) {
271 Instance.getTraceHandler().error(e);
272 }
273 }.connect();
68e2c6d2
NR
274 }
275
276 @Override
b9ce9cad 277 public void setSourceCover(final String source, final String luid) {
3989dfc5
NR
278 setCover(source, luid, "SOURCE");
279 }
280
281 @Override
282 public void setAuthorCover(final String author, final String luid) {
283 setCover(author, luid, "AUTHOR");
284 }
285
286 // type = "SOURCE" | "AUTHOR"
287 private void setCover(final String value, final String luid,
288 final String type) {
b9ce9cad
NR
289 try {
290 new ConnectActionClientObject(host, port, true) {
291 @Override
292 public void action(Version serverVersion) throws Exception {
3989dfc5 293 send(new Object[] { md5, "SET_COVER", type, value, luid });
b9ce9cad
NR
294 }
295
296 @Override
297 protected void onError(Exception e) {
298 Instance.getTraceHandler().error(e);
299 }
300 }.connect();
301 } catch (IOException e) {
302 Instance.getTraceHandler().error(e);
edf79e5e
NR
303 }
304 }
305
306 @Override
307 // Could work (more slowly) without it
308 public Story imprt(final URL url, Progress pg) throws IOException {
00f6344a
NR
309 // Import the file locally if it is actually a file
310 if (url == null || url.getProtocol().equalsIgnoreCase("file")) {
311 return super.imprt(url, pg);
312 }
313
314 // Import it remotely if it is an URL
315
edf79e5e
NR
316 if (pg == null) {
317 pg = new Progress();
318 }
319
320 pg.setMinMax(0, 2);
321 Progress pgImprt = new Progress();
322 Progress pgGet = new Progress();
323 pg.addProgress(pgImprt, 1);
324 pg.addProgress(pgGet, 1);
325
326 final Progress pgF = pgImprt;
327 final String[] luid = new String[1];
328
329 try {
330 new ConnectActionClientObject(host, port, true) {
331 @Override
332 public void action(Version serverVersion) throws Exception {
333 Progress pg = pgF;
334
335 Object rep = send(new Object[] { md5, "IMPORT",
336 url.toString() });
337
338 while (true) {
339 if (!RemoteLibraryServer.updateProgress(pg, rep)) {
340 break;
341 }
342
343 rep = send(null);
344 }
345
346 pg.done();
347 luid[0] = (String) rep;
348 }
349
350 @Override
351 protected void onError(Exception e) {
352 Instance.getTraceHandler().error(e);
353 }
354 }.connect();
355 } catch (IOException e) {
356 Instance.getTraceHandler().error(e);
357 }
358
359 if (luid[0] == null) {
360 throw new IOException("Remote failure");
361 }
362
363 Story story = getStory(luid[0], pgGet);
364 pgGet.done();
365
366 pg.done();
367 return story;
368 }
369
370 @Override
371 // Could work (more slowly) without it
c8d48938
NR
372 protected synchronized void changeSTA(final String luid,
373 final String newSource, final String newTitle,
374 final String newAuthor, Progress pg) throws IOException {
edf79e5e
NR
375 final Progress pgF = pg == null ? new Progress() : pg;
376
377 try {
378 new ConnectActionClientObject(host, port, true) {
379 @Override
380 public void action(Version serverVersion) throws Exception {
381 Progress pg = pgF;
382
c8d48938
NR
383 Object rep = send(new Object[] { md5, "CHANGE_STA", luid,
384 newSource, newTitle, newAuthor });
edf79e5e
NR
385 while (true) {
386 if (!RemoteLibraryServer.updateProgress(pg, rep)) {
387 break;
388 }
389
390 rep = send(null);
391 }
392 }
393
394 @Override
395 protected void onError(Exception e) {
396 Instance.getTraceHandler().error(e);
397 }
398 }.connect();
399 } catch (IOException e) {
400 Instance.getTraceHandler().error(e);
b9ce9cad 401 }
68e2c6d2
NR
402 }
403
ff05b828 404 @Override
2249988a 405 public synchronized File getFile(final String luid, Progress pg) {
ff05b828
NR
406 throw new java.lang.InternalError(
407 "Operation not supportorted on remote Libraries");
408 }
409
468b960b
NR
410 /**
411 * Stop the server.
412 */
413 public void exit() {
414 try {
415 new ConnectActionClientObject(host, port, true) {
416 @Override
417 public void action(Version serverVersion) throws Exception {
418 send(new Object[] { md5, "EXIT" });
419 }
420
421 @Override
422 protected void onError(Exception e) {
423 Instance.getTraceHandler().error(e);
424 }
425 }.connect();
426 } catch (IOException e) {
427 Instance.getTraceHandler().error(e);
428 }
429 }
430
e272f05f
NR
431 @Override
432 public synchronized MetaData getInfo(String luid) {
433 List<MetaData> metas = getMetasList(luid, null);
434 if (!metas.isEmpty()) {
435 return metas.get(0);
436 }
437
438 return null;
439 }
440
14b57448 441 @Override
b9ce9cad 442 protected List<MetaData> getMetas(Progress pg) {
e272f05f
NR
443 return getMetasList("*", pg);
444 }
445
446 @Override
efa3c511
NR
447 protected void updateInfo(MetaData meta) {
448 // Will be taken care of directly server side
449 }
450
451 @Override
c8d48938 452 protected void invalidateInfo(String luid) {
efa3c511 453 // Will be taken care of directly server side
e272f05f
NR
454 }
455
456 // The following methods are only used by Save and Delete in BasicLibrary:
457
458 @Override
459 protected int getNextId() {
460 throw new java.lang.InternalError("Should not have been called");
461 }
462
463 @Override
464 protected void doDelete(String luid) throws IOException {
465 throw new java.lang.InternalError("Should not have been called");
466 }
467
468 @Override
469 protected Story doSave(Story story, Progress pg) throws IOException {
470 throw new java.lang.InternalError("Should not have been called");
471 }
472
473 //
474
475 /**
476 * Return the meta of the given story or a list of all known metas if the
477 * luid is "*".
9f51d8ab
NR
478 * <p>
479 * Will not get the covers.
e272f05f
NR
480 *
481 * @param luid
482 * the luid of the story or *
483 * @param pg
484 * the optional progress
485 *
486 *
487 * @return the metas
488 */
489 private List<MetaData> getMetasList(final String luid, Progress pg) {
b9ce9cad
NR
490 final Progress pgF = pg;
491 final List<MetaData> metas = new ArrayList<MetaData>();
74a40dfb 492
ff05b828 493 try {
62c63b07 494 new ConnectActionClientObject(host, port, true) {
ff05b828
NR
495 @Override
496 public void action(Version serverVersion) throws Exception {
b9ce9cad
NR
497 Progress pg = pgF;
498 if (pg == null) {
499 pg = new Progress();
851dd538 500 }
74a40dfb 501
e272f05f 502 Object rep = send(new Object[] { md5, "GET_METADATA", luid });
74a40dfb 503
b9ce9cad
NR
504 while (true) {
505 if (!RemoteLibraryServer.updateProgress(pg, rep)) {
506 break;
507 }
74a40dfb 508
b9ce9cad 509 rep = send(null);
ff05b828 510 }
851dd538 511
e272f05f
NR
512 if (rep instanceof MetaData[]) {
513 for (MetaData meta : (MetaData[]) rep) {
514 metas.add(meta);
515 }
516 } else if (rep != null) {
517 metas.add((MetaData) rep);
b9ce9cad 518 }
851dd538
NR
519 }
520
521 @Override
522 protected void onError(Exception e) {
523 Instance.getTraceHandler().error(e);
ff05b828
NR
524 }
525 }.connect();
ff05b828 526 } catch (Exception e) {
62c63b07 527 Instance.getTraceHandler().error(e);
ff05b828 528 }
b9ce9cad
NR
529
530 return metas;
531 }
b0e88ebd 532}