From b0e88ebd20f8b2950c382694e936da76ac3596b6 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Sun, 2 Jul 2017 01:04:44 +0200 Subject: [PATCH] Network server and Library + nikiroo-utils update --- README.md | 19 ++- changelog.md | 2 + ...es.jar => nikiroo-utils-1.6.1-sources.jar} | Bin 69968 -> 70411 bytes src/be/nikiroo/fanfix/Instance.java | 21 ++++ src/be/nikiroo/fanfix/Library.java | 15 +-- src/be/nikiroo/fanfix/Main.java | 84 ++++++++++---- src/be/nikiroo/fanfix/RemoteLibrary.java | 109 ++++++++++++++++++ .../nikiroo/fanfix/RemoteLibraryServer.java | 51 ++++++++ src/be/nikiroo/fanfix/data/Chapter.java | 7 ++ src/be/nikiroo/fanfix/data/Paragraph.java | 7 ++ src/be/nikiroo/fanfix/reader/BasicReader.java | 79 ++++++++++--- src/be/nikiroo/fanfix/reader/CliReader.java | 4 +- src/be/nikiroo/fanfix/reader/LocalReader.java | 30 +++-- .../fanfix/reader/LocalReaderFrame.java | 2 +- src/be/nikiroo/fanfix/reader/TuiReader.java | 7 +- .../fanfix/reader/TuiReaderApplication.java | 21 ++-- .../fanfix/reader/TuiReaderStoryWindow.java | 27 +++-- 17 files changed, 394 insertions(+), 91 deletions(-) rename libs/{nikiroo-utils-1.6.0-sources.jar => nikiroo-utils-1.6.1-sources.jar} (85%) create mode 100644 src/be/nikiroo/fanfix/RemoteLibrary.java create mode 100644 src/be/nikiroo/fanfix/RemoteLibraryServer.java diff --git a/README.md b/README.md index 826aacb2..d80d3c5c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Fanfix is a small Java program that can download stories from some supported web (If you are interested in the recent changes, please check the [Changelog](changelog.md) -- note that starting from version 1.4.0, the changelog is checked at startup (unless the option is disabled)) +TODO: new screenshots + TUI screenshots + ![Main GUI](screenshots/fanfix-1.3.2.png?raw=true "Main GUI") It will convert from a (supported) URL to an .epub file for stories or a .cbz file for comics (a few other output types are also available, like Plain Text, LaTeX, HTML...). @@ -60,7 +62,9 @@ The following arguments are also allowed: - ```--read [id] ([chapter number])```: read the given story denoted by ID from the library - ```--read-url [URL] ([chapter number])```: convert on the fly and read the story denoted by ID, without saving it - ```--list```: list the stories present in the library and their associated IDs -- ```--set-reader [reader type]```: set the reader type to CLI or LOCAL for this command (must be the first option) +- ```--set-reader [reader type]```: set the reader type to CLI, TUI or GUI for this command +- ```--server [port]```: start a story server on this port +- ```--remote [host] [port]```: contact this server instead of the usual library - ```--help```: display the available options ### Environment variables @@ -85,6 +89,7 @@ There are some unit tests you can run, too: - libs/nikiroo-utils-sources.jar: some shared utility functions I also use elsewhere - [libs/unbescape-sources.jar](https://github.com/unbescape/unbescape): a nice library to escape/unescape a lot of text formats; I only use it for HTML +- [libs/jexer-sources.jar](https://github.com/klamonte/jexer): a small library that offers TUI widgets Nothing else but Java 1.6+. @@ -115,8 +120,16 @@ Currently missing, but either in progress or planned: - [x] Implement it from --set-reader to the actual window - [x] List the stories - [ ] Fix the UI layout - - [ ] Status bar and real menus - - [ ] Open a story in the reader and/or natively + - [x] Status bar + - [ ] Real menus + - [x] Open a story in the reader and/or natively + - [ ] Update the screenshots +- [ ] Network support + - [x] A server that can send the stories + - [x] A network implementation of the Library + - [ ] Write access to the library (?) + - [ ] Access rights (?) + - [ ] More tests, especially with the GUI - [ ] Check if it can work on Android - [x] First checks: it should work, but with changes - [ ] Adapt work on images :( diff --git a/changelog.md b/changelog.md index 89b6b00d..a27f9c8f 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,8 @@ ## Version WIP - New reader type: TUI (a text user interface with windows and menus) +- A server option to offer stories on the network +- A remote library to get said stories from the network ## Version 1.5.3 - FimFiction: Fix tags and chapter handling for some stories diff --git a/libs/nikiroo-utils-1.6.0-sources.jar b/libs/nikiroo-utils-1.6.1-sources.jar similarity index 85% rename from libs/nikiroo-utils-1.6.0-sources.jar rename to libs/nikiroo-utils-1.6.1-sources.jar index 1ec2951b17dc08ac7051355d62f75c9813b28aac..f3b1578e33be4dce4635d0b11b11efd27c51ce98 100644 GIT binary patch delta 8096 zcmYj$Wl&sevo$kF2o^lJ+u#ntg1ftWa3{fG;~s1RgS%Uh!9Bq>c#z;BxCaUL<(&7s z_r3ei?zMV7{Z#L++ErcaTOm?hDH5i-5+V{l0xBvh!dK+2WK1E%zpr{lLn1`q)=lW2 zYZ@3f3J4!K)>Ba<<-!(q4H~dh>(FUepHm{$0ZSw$saK#nze3fq@up%QG<9-C(78J$ z2b>ysKb5lCb0n+luWV-soPO+a&^4`B3io8C>)A4_D6BV5|58{VW}0ZWsJJq)saB4u z=W(Wm7?#PSal{O%hYOd`AL1OmJTRLiUTfQ=$9M(Q!g!q!Zxs9DLy}KL3ot;ZtfQmB zj${q8X;yP*HL$r%wRD+uCesk@YbC3?Q4fJnPENfe(`(a4Ze$W?n23U|tlzl=+{Pjh zY$T|P&beYqn5!WeqZbrRlD-2!>EAT`Srb++e1-Sag&5N^?aDw({ikjz3Y`H11wo-9 zP}&N})Y~&^(oK^eL`#_LnR&tF8!v!aFh;RSneKY^I7b(wCCPJgRs@O zU}BsqR{vOyrkS=QW$UvbyUne*&uKq}d>T))8s3G{_%&o)?_x|1L|i zFD5M(&6DNXH+IsWJm0^!Ps|x;pL@0yRe=GdgzKc{?WcQDc8pUQXpz4RSEny%=a=Lw zO}=~4FGtla>mteQPW$r4j|vEY zL!aU*4vkWant)mN8-EFp;+3JVkG!rd?kF4fm%+HCRmNF%aQpY>c-~g;%&dYt`gr+bwR!FU?-n$TL z;dm@WGksWTATfVrH*pd&b{*|M8Q2YYI(WUm!HLms7rcdbiPm&WEXzmreA#7>*!Qc_ zuivXm^{YATS^U71bx1IJu$otnGp%`a+F91q^a)@o>G={xdhO(Twc4`y$BTfits%SP zSVL=(K}O z#2;(mou5lowq~3)%$>v$jExp21C!D5pgsG0-%tp4%0pBP6P;69e{^*Po>HJutY47! z?b3|SuE0NWSL$TyXwZ*-vX%4q->*z6v3z?eOmXf{P{I)rTXB1m)ep6w{T~h+z0_6E5zin?Jy%iq~t)SOCGOd$t zFlN@BM?TKjZDZo&)2MedHKIaKic^f~msC!sn8J$`8NQpMs)|kBf(fx9i`ZJ~l=H=r zl2gTbQZcc5(@-y*t)rV((Fbo7Y~J*lYD_iq^4rcvN<`Vd@`n~Gp9*FdX}Wc%hg2sv z`Pdv)1GS%w4Ww{?zKq)+x&J_|?uHkJsLim3c29`qJryrDR1u+7%=Jdl+}dB6`obbh zS9^{bLo5i*M!HdnHhX3l`ubG-{#_>OgT|+P7*Rx33yQ`*$EGkYYsa<$8^&(`mtL1% zK{goy$((}fFOa~WZR4})wyt-IRI|}G8|So@2Y_#hfREU$8h7$~IlbEr-~Gx}bRUjV zeTugoTWH_|{MYeAgK8s^@;eu?rZYoFFEvEm^FnN2+$~kDd)n4X#4*D*e%mb8UbHW~ zFFcXjrxJ;X5(O>y8*Q(zpfSk`mHlYu@_0UbNpxIR71WA;xW24-pgQvd`u4HR67;m- zkAPtXiM|g>&?d2AF_)BQD6zAEbNZR@g3)}c`E&cj6Se?CkB1UDaenpW+ajrg^dqgp z6LkjYumJ*^`9T8jQeAqL&&Zeh;F0j59ceZtU$s9=1ObwpuCFcvXe-eQefC_t7oU#E zCT7b)?yD(j?3dC3L3&l*aj=F-s6(_ZHn78z5hq{d*vJx5IkviNL`%{ILs>pib0d4N zdKi#K)4uOQGPp)ae_OxyT|Hu>RJwLK=vMKu^7p-QA49hRo6TFtTgri{+HnwlQm0d}l0O>`%7k3l~!+yx0iiYWFukOgJri~A!rBV`r5!cnP62z&@c#ML>vuHKG!;s& zl9lh_`<+;g=up-VA@yrH4ILadO2x%3vAzlPkWVq_{SMRqET0d2?qtOaTC;#`v2F%5 ztORMRHs<`>Fyo3iYmcDubNOwAQAHDvOtk5AWo5~Qx00XF3XQvd=QmnJ_OV}!NYSot zpQ z4yWaH$=%f<3LtLv7(zwtI`an-l5q{r=w~LkF9L2-XR*_coBa5n2AJbt|{`- z>dXNyVlsgfeNGd4jwi+~j?G?&u>yQ*cp89M_)1+Vy~cN zNrI^_zxk6~>k;F{wD$X!Gr2b$0T27c8coF|NmIqTjCkpg6(&6-NWW+vD6~M#}cm>Ce7rP@&Qr^;v3RvEV!*#Zk#mytL-%2@@aW zLz3}Q3WOJIoazpcaKly{btan%Ee+B>gxCuf4>5eHd!uIKM9+eJkmgC%UCLR*NY(mU zsda0%TT_LSGri9NIh4WzjZT*jH&slbjg8#TPHZyvwUquDktgRAP-kr_P#!~?5?(|0 zZr1Bm#C%U4_0o-|l8A&gp(Nm^d>Bldh)g1_B6GiipfY4k&qPs{l!9Y6sGUOVN zK!A8f7{W`=&~>4hlyg;%QH5H5T$d;&Ozv-gU*~1|BNt1R!-ju$j94Xwy`Z_no+2+_ z?9uw|vX_==nFWnC5RV+REMiG{z&n$>h7@vxp&!BM`nEHOT)iwb!=QoVeJ#HR@k$&0 z=QIT0rrJ;XGU~^YS2~W_ra7C#k_SlJK1n(INh(JDdhfW?Hp|e3c@-p^d41vEUnYEnyrC!mm4$?!E%4Hig6N|_XLkMlJ2c3| zr@-3eEEJv=X!f$m$6pflq+J4Ek?}n|P3ypVB#%}TBa=(64uM!?k85Y30-71N8jT*F2A(h5M5=!Q` z_p(!7`7Lne*$NC#BMgV3c>Hugnlz|9V3g;#2+oYqoUPn#S--;4Ff^(0eKT{8 zUK$Ze^8nsm>%IT|boKai3BRWCW*ke{Zd7{LokhB!27Oy20;fgv7sX&XtE()g-!x;+4Q!|ZF0B8 zmtXlRaZK49ILYn)wQ=^M)Ko&8bgx84JJ`08Wng>dhCY#tqJUga2@Q$qH~sNM{>(*L z2I@BH-g`GI!>7xZ`48v82m&Eh&u+K1{!V9fSL!ub^3^K5o)8!IcMNW5`8v&_W4=(@ zxyJf1T{7U*L-ua?JstiS+19l&4ygJ3JTcFhV}G&V&Ktf!+APoraaE(7PaH_xc;|F8 zlutosmm`*-z&1Vi-V<8|E;jy@89u_9pl)YDA$MM)dt28|p+b|uly2S2< z=TGm5A&^**c2w$>yT~GSc=JQVr3)!OlbHd-z3T73ubNi6?OJ>rApBWQ@R6d^Vl?zC zl-5Yimun7b;o6A<$FvtteLch=zT9S1V_c8`z5r!8&f;BvjN?LG4|OnU!P;JN%=*ao z0CdbEGeut&5hC)}S-g>}iaLG1d%Wve&+UHm7Gpf0gvBB-?K7ssH$59y;eaQX>9y*N z*9qa4GtRa?>mS<;pJi_YFKR;XUe0<#eGn1tqJ&$GY*|Y@-K@^pBBIS4IBBwe6X9ov zjiqy#sPepM!kgEaVW%{GVPedNg~#hJxdaTbX8OPix$bjXcX&*829v#V->nS{tl|=M zRwD%=YH6%YZ7>ZOMTEXDB=VT3K+AiZVmV(T50U1PGt2A!amdbw@tlbt1W2Z-Z z%ES*01lJTRDGddwC<6evsmKD;T%UqZk#d!P8ZP`mB<`Rl2-wx8xG6JLO76yV07u_= zOyd#M*D^yic!K-Giv1{xqtp|7_8d-{+}dFZzHw5Kn=I^@RdTSlUi&L!&Uh?D9{BTN zsva!6J3l11pw_|f{2jYx0!Kx$pmTqlWg>1l3si^pSvr<^eduY|Ge1FZ?gt`>3C3YqKl-KtWO(u#QV#{sw7R&ZA^!{*CJJqx4A|WWlSG{D@HQ*FB z*A_?y?TC7eM>eyhaZ#P^lDvU$EW7ehOW6M*YmrM7=BRQqqR6&i9D>jS2jq%jrB;9q zSoTu5*IQb8io;LUYgjixlyPx+pJ!d=xV*S+r9~z$+2YV`VmgwIpG;8ep0KteBBmP> zX=Xf+Y)Y;D_MAJaQD+ko{UEzERGmh0O!vay?<<#imi=jyl2+9{FNr0; ze#~NbpWOAl=BxG$s@c8FFEeQuu>WvD>q$>*&q)wi=VAts?_og!h7tYl5P3zfXw>8s zLE(JwAxR@2?Hh;!M!wVA0czW0GqY35nLfWTo?9z&2dp(8{~hYoBKe<8s$#Lw#7(5_ zPUJu`PpD{g=+McR#CVJvyN{|h%o;Vt>$$2QD-CsiCyj~ivzhITVNoTcYTh^RQ?@&_ zJn{#Q_g5XBZW9t__^>3O9H>grXAn73;1VsJtO?H?U`d}e(S_U zKoH|@Ed9oZfvAnJ1zlU^2Uc3o8y{G(1EVYS3n^j6&JXEK&18;~iF3L_iwwlCq$P1D z1NP*=ucdeWDYD?tHB3r$etZ`?S8LoMms0Db zliHdQHxkC!$5Gd|G27AOD1_DyPFDBtfvF#5X>futlj48q-6+0?0^#d7MfRn&+B80` zWP3DPV+9HC>Y{sq>5j{O?GZu4Y$I820@F1452v{?yvVhQJNm^AFUNfYQhlK=oC%vB zaM`KL`g6eF=ypOLm{L-qCtM;6;+>lc%(|OBp~>Q8?)2D*pWWh@J>45@Zb!E@wBNOEGdrD}lI3n%}qDt#|||CodoNr|(Z*OXS2F;RF!x zZBQiFI*a)>4CdDLNtceJ`(!o{sU()TGZDGSGD9@^cPh?^)wWnKmdaou@h(LAA@PH+ zD&kPjZs@1x=v0X^#X4PURT?d*M8ovLdStxK^A&fiY}%g;44?PY?8`hEZd*9lBs13rhuPD(_Mv8QK?pB)7~b7qlk4&tb6}NiOsHP| zxWAcId)(v1iozv+ld_5^0Ht^}>GlJ0qO7dIx3%5(wr<)fglk9fO`12|Bfc>@a^jSg zFZ!2SCUJRuPCFG9xQMVUDbv%YD7BKrt|Ap9l^re{Q1q6gOk^|P>rB#*PrpA6Vjy8EGcK5pb5<;GRi-`@BY`gfirA+yZXwi-VuEDmH2q8_!)TzUJ|R zmC7p#k#eN-Y6-->CM_9Ao`ufv;Yy6nH+7LT(fnEp49lLv?>&_e=acInsF&j)ZWmOo z&k_U`Ku&f!q9rnjwPFj5&7nnvi~@7@|6M82iD3MJkG*CoDh&lWLmt)aR)!Z}zB1ab z4lA`^W5b1{47cQ?plztiZ4IY;RctK$_c6nmWHhz~VNl$D^AcwQVjnF&{$SIfi1tjv zbh8D~_0Bi2SKU-Iyl~R&Y-u_uZSFIOOgn6rz>!^1(hi*Dp}-;e{x|Ir3SAqb~*k z(9^VLbLeDpS}{!|+2z$JT!s*k<_-{OnuB3%eKmO5vZrx4FRXas>WStmE8>YrOO{zq zwk^CZR=+=Ad-S-9#V65S=Nv33)3u>Sr>$DH}db_Z|ldH1T&w3-?uy46TfAj&w%a}!y zHp;~uZR<+AKhUj@p~E>ePKPos2tK*8yE&*^w=KhFE(GiS?i-$|A^cbcPB6XH`0gW> zl`fY-vr+=j_{@VI8|-{t4nKJ&;UVEkw?{7|MKYN}d{(LY4siwR4XR*sYy(J{5+(<) z>k7rqX>pf>eYg+_Vd5Mh^!y$9Ibg_-$zUm_5(&+q$|Y8ku+kxqAoJU>m?3F@?u_W$ zr#ty5ypHe#@7L`ruMRjcv!bZ-w|TRW)ZBL+hsh3%qm|zpUO^)XN2-)j#PHdrE_;Y{ zCbEl`25?9EhFNLNRzVvJwSe#HtVd3nxoCHNG9x!9>aKf#Ewy`o6&aYP{(~ppj2~K7 z_ph(wNth-2==PpJzqe$hM9Nw91uZWfk{yZoFEqy?H~l^;_a4nY&gp^gLMZnnlh5%= z>;sloZ0o*tON`38eYO8O+!ry3V@&e8h51};F$7tsUV+w=kGPS3SP^*tCcelB^mwaY zo)P@5&|2OIyrFkG!{xSF_~|8^wVr- zRNmy@UG-*ZWF(F=AQ>oKR&xTM$(_i5r2!isU{^S_e%6tnNML$+ovN+*vH;u^(5M)X zhoNh46VnN^E0aw`&z#PTR$!4;LYD0=^V9bD99WZI2FV?JBNKX}&txfkkMF~rI`k7! z#6~JZVt#1>OTTI@q8pa5nvX;B#4Lj0kS{Q4Dlz@%0b;|ymkm&LdKTg(qNYwt7BE@4 zVa8^5CS0;)Zg9U4U!Uzo3w0 z3SGE&sFw*H06TT3_pY=T8EyFk{6C4vgu3T+UFeuZ6XSST84D8dVs`do^reJKslOCO z3;gl_m_INfYc+b^I#FY)1>b+T< z|>UkwMw%dO{(S^+St5^ahJ zYR7Y}*E?mK^d(+-MBvBM%H#`YCUsVSZKkhdPT-x4Z69qUslchg7rM<$x};p`TW#~q zWZVTcc3Dlqxe%`3t4zZ)F_THb^>#WxYii-&kV(tH_8B>oIKYB3%#eX2%Z+~b&~ukw z!vFRG0;Fl@y;|WeGmD-^A2fP>@JzJACZ|boc@gsT-iX!2D!bAx z?kt?9STBEK_KhIhkr>Qax%XQW(Sv_;-+KPBPmj`ZC)QXUJ;jg!l+P~$O48K&Y{J07 zu5>fWdL^M-LS4;qUyACTX)8DN-hCN0P$28icYtM2mY^4&Wmlg(2F;SH8SA?#@V_z1 zQ(rE!T@jw0*q2_Nzb(i$-m6kk!+eC7m=}UECbR6rTqQ+)-fCJZi|)mg>^S}SD0_6C zt#sE1uS-v|FjyRN3`|J3Vy7C)!(RU&@7&{Tq2`l7mN*#4k9q&T{EDsK zFN}Uzg9Zt&=iNoxi(y@hfrsnw-y5eb2&tVNhi+Gy>8d$IGPz=8Wf8@iMhIZ$M{$zRzSARPF7lhlpkH~M>{eMMu zRUHn2hQA=7?~(i!duI_fGgF{EuT3i)t-F=7rGwqjcx zNDxkI()J4B?(d#IuA?JW`p~5S`c+6$cuJhVKp2u#6qZ9|t}_>fZe6Yn-#F02U1toU zx%R5(E_Ob^x9m2vg7}4w@cIqoM_^{V_ems3Q%Tt=;+EY#1$vJZP<)ZypcZLHB2svq zM+Hqgtr-)6M5`bFHD}7%#}k6l5fC^bjkbG6B>#A)umLbg@SmU=5%dF$_18sJ{MW+y z*TI1uB7>~JCS=fpmqf5$WDpX}2?fLf22uP=bR&UKq1Y5JVI(g=7_b=>5Cy0Pc8&tl z0@Kh#ap;*~E-ygL;A_Tz#L!1Z{Qs|e@mD7aJAVOE082Uj&Edh+P(l3QQ=fn1{Ffj! zSTX9~gG|2vC6515ko*5H0fqQe!Yp3?Re1*e4=D|9jQ*b(4cZfp1H(uAD}coQF9|b2 z1Mz`Vv;Rxfqk(w9N(KKz5Wuj|K_XzIl79&^^uL!%RQ(%QqJv~XL9lalkP>*I8McWD zB7x~+{4I~s@*j|k@wYU8*FS&}_V5p=@BRmn!c;LqV&JIWe`V05UQAdMCWsky4BNm2 zy`>`T*i`)Q5`}=kgz$eKlK%e`H^2f3fmvt%Q@j)F?+Dfx{*!s}3Peoxck5F8?`RPa zsQ+&b1f8{iI!O6}4KqUhJ15syAPO+u0c`peh!9rt>Te6W{{l5|^U1#?lIiaQeM?}h&lCSfu~ delta 7691 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 diff --git a/src/be/nikiroo/fanfix/Instance.java b/src/be/nikiroo/fanfix/Instance.java index 28849b32..42e141e6 100644 --- a/src/be/nikiroo/fanfix/Instance.java +++ b/src/be/nikiroo/fanfix/Instance.java @@ -27,6 +27,7 @@ public class Instance { private static boolean debug; private static File coverDir; private static File readerTmp; + private static File remoteDir; private static String configDir; static { @@ -84,6 +85,7 @@ public class Instance { coverDir = getFile(Config.DEFAULT_COVERS_DIR); File tmp = getFile(Config.CACHE_DIR); readerTmp = getFile(UiConfig.CACHE_DIR_LOCAL_READER); + remoteDir = new File(getFile(Config.LIBRARY_DIR), "remote"); if (checkEnv("NOUTF")) { trans.setUnicode(false); @@ -185,6 +187,25 @@ public class Instance { return readerTmp; } + /** + * Return the directory where to store temporary files for the remote + * {@link Library}. + * + * @param host + * the remote for this host + * + * @return the directory + */ + public static File getRemoteDir(String host) { + remoteDir.mkdirs(); + + if (host != null) { + return new File(remoteDir, host); + } + + return remoteDir; + } + /** * Check if we need to check that a new version of Fanfix is available. * diff --git a/src/be/nikiroo/fanfix/Library.java b/src/be/nikiroo/fanfix/Library.java index 8a3895ad..3868bd3f 100644 --- a/src/be/nikiroo/fanfix/Library.java +++ b/src/be/nikiroo/fanfix/Library.java @@ -38,8 +38,8 @@ import be.nikiroo.utils.Progress; public class Library { protected File baseDir; protected boolean localSpeed; + protected Map stories; - private Map stories; private int lastId; private OutputType text; private OutputType image; @@ -156,17 +156,12 @@ public class Library { * * @return the stories */ - public synchronized List getListByType(String type) { - if (type != null) { - // convert the type to dir name - type = getExpectedDir(type).getName(); - } - + public synchronized List getListBySource(String type) { List list = new ArrayList(); - for (Entry entry : getStories(null).entrySet()) { - String storyType = entry.getValue().getParentFile().getName(); + for (MetaData meta : getStories(null).keySet()) { + String storyType = meta.getSource(); if (type == null || type.equalsIgnoreCase(storyType)) { - list.add(entry.getKey()); + list.add(meta); } } diff --git a/src/be/nikiroo/fanfix/Main.java b/src/be/nikiroo/fanfix/Main.java index 5cd33138..38f58af3 100644 --- a/src/be/nikiroo/fanfix/Main.java +++ b/src/be/nikiroo/fanfix/Main.java @@ -16,7 +16,7 @@ import be.nikiroo.fanfix.supported.BasicSupport; import be.nikiroo.fanfix.supported.BasicSupport.SupportType; import be.nikiroo.utils.Progress; import be.nikiroo.utils.Version; -import be.nikiroo.utils.ui.UIUtils; +import be.nikiroo.utils.serial.Server; /** * Main program entry point. @@ -25,7 +25,7 @@ import be.nikiroo.utils.ui.UIUtils; */ public class Main { private enum MainAction { - IMPORT, EXPORT, CONVERT, READ, READ_URL, LIST, HELP, SET_READER, START, VERSION, + IMPORT, EXPORT, CONVERT, READ, READ_URL, LIST, HELP, SET_READER, START, VERSION, SERVER, REMOTE, } /** @@ -55,6 +55,8 @@ public class Main { *
  • --set-reader [reader type]: set the reader type to CLI, TUI or LOCAL * for this command
  • *
  • --version: get the version of the program
  • + *
  • --server [port]: start a server on this port
  • + *
  • --remote [host] [port]: use a the given remote library
  • * * * @param args @@ -63,11 +65,13 @@ public class Main { public static void main(String[] args) { String urlString = null; String luid = null; - String typeString = null; + String sourceString = null; String chapString = null; String target = null; MainAction action = MainAction.START; Boolean plusInfo = null; + String host = null; + Integer port = null; boolean noMoreActions = false; @@ -102,8 +106,8 @@ public class Main { case EXPORT: if (luid == null) { luid = args[i]; - } else if (typeString == null) { - typeString = args[i]; + } else if (sourceString == null) { + sourceString = args[i]; } else if (target == null) { target = args[i]; } else { @@ -113,8 +117,8 @@ public class Main { case CONVERT: if (urlString == null) { urlString = args[i]; - } else if (typeString == null) { - typeString = args[i]; + } else if (sourceString == null) { + sourceString = args[i]; } else if (target == null) { target = args[i]; } else if (plusInfo == null) { @@ -128,8 +132,8 @@ public class Main { } break; case LIST: - if (typeString == null) { - typeString = args[i]; + if (sourceString == null) { + sourceString = args[i]; } else { exitCode = 255; } @@ -164,6 +168,30 @@ public class Main { break; case VERSION: exitCode = 255; // no arguments for this option + break; + case SERVER: + if (port == null) { + port = Integer.parseInt(args[i]); + } else { + exitCode = 255; + } + break; + case REMOTE: + if (host == null) { + host = args[i]; + } else if (port == null) { + port = Integer.parseInt(args[i]); + try { + BasicReader.setDefaultLibrary(new RemoteLibrary(host, + port)); + } catch (IOException e) { + Instance.syserr(e); + } + action = MainAction.START; + } else { + exitCode = 255; + } + break; } } @@ -215,16 +243,16 @@ public class Main { updates.ok(); // we consider it read break; case EXPORT: - exitCode = export(luid, typeString, target, pg); + exitCode = export(luid, sourceString, target, pg); updates.ok(); // we consider it read break; case CONVERT: - exitCode = convert(urlString, typeString, target, + exitCode = convert(urlString, sourceString, target, plusInfo == null ? false : plusInfo, pg); updates.ok(); // we consider it read break; case LIST: - exitCode = list(typeString); + exitCode = list(sourceString); break; case READ: exitCode = read(luid, chapString, true); @@ -237,6 +265,7 @@ public class Main { exitCode = 0; break; case SET_READER: + exitCode = 255; break; case VERSION: System.out @@ -247,8 +276,23 @@ public class Main { updates.ok(); // we consider it read break; case START: - //BasicReader.setDefaultReaderType(ReaderType.LOCAL); - BasicReader.getReader().start(null); + BasicReader.getReader().browse(null); + break; + case SERVER: + if (port == null) { + exitCode = 255; + break; + } + try { + Server server = new RemoteLibraryServer(port); + server.start(); + System.out.println("Remote server started on: " + port); + } catch (IOException e) { + Instance.syserr(e); + } + return; + case REMOTE: + exitCode = 255; break; } } @@ -320,17 +364,17 @@ public class Main { } /** - * List the stories of the given type from the {@link Library} (unless NULL - * is passed, in which case all stories will be listed). + * List the stories of the given source from the {@link Library} (unless + * NULL is passed, in which case all stories will be listed). * - * @param type - * the type to list the known stories of, or NULL to list all + * @param source + * the source to list the known stories of, or NULL to list all * stories * * @return the exit return code (0 = success) */ - private static int list(String type) { - BasicReader.getReader().start(type); + private static int list(String source) { + BasicReader.getReader().browse(source); return 0; } diff --git a/src/be/nikiroo/fanfix/RemoteLibrary.java b/src/be/nikiroo/fanfix/RemoteLibrary.java new file mode 100644 index 00000000..cc78be90 --- /dev/null +++ b/src/be/nikiroo/fanfix/RemoteLibrary.java @@ -0,0 +1,109 @@ +package be.nikiroo.fanfix; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +import be.nikiroo.fanfix.data.MetaData; +import be.nikiroo.fanfix.data.Story; +import be.nikiroo.fanfix.output.BasicOutput.OutputType; +import be.nikiroo.utils.Progress; +import be.nikiroo.utils.Version; +import be.nikiroo.utils.serial.ConnectActionClient; + +public class RemoteLibrary extends Library { + private String host; + private int port; + + private Library lib; + + public RemoteLibrary(String host, int port) throws IOException { + this.host = host; + this.port = port; + + this.localSpeed = false; + this.baseDir = Instance.getRemoteDir(host); + this.baseDir.mkdirs(); + + this.lib = new Library(baseDir, OutputType.INFO_TEXT, OutputType.CBZ); + } + + @Override + public synchronized Story save(Story story, String luid, Progress pg) + throws IOException { + throw new java.lang.InternalError( + "No write support allowed on remote Libraries"); + } + + @Override + public synchronized boolean delete(String luid) { + throw new java.lang.InternalError( + "No write support allowed on remote Libraries"); + } + + @Override + public synchronized boolean changeType(String luid, String newType) { + throw new java.lang.InternalError( + "No write support allowed on remote Libraries"); + } + + @Override + protected synchronized Map getStories(Progress pg) { + // TODO: progress + if (stories.isEmpty()) { + try { + new ConnectActionClient(host, port, true, null) { + public void action(Version serverVersion) throws Exception { + try { + Object rep = send("GET_METADATA *"); + for (MetaData meta : (MetaData[]) rep) { + stories.put(meta, null); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }.connect(); + } catch (IOException e) { + Instance.syserr(e); + } + } + + return stories; + } + + @Override + public synchronized File getFile(final String luid) { + File file = lib.getFile(luid); + if (file == null) { + final File[] tmp = new File[1]; + try { + new ConnectActionClient(host, port, true, null) { + public void action(Version serverVersion) throws Exception { + try { + Object rep = send("GET_STORY " + luid); + Story story = (Story) rep; + if (story != null) { + lib.save(story, luid, null); + tmp[0] = lib.getFile(luid); + } + } catch (Exception e) { + Instance.syserr(e); + } + } + }.connect(); + } catch (IOException e) { + Instance.syserr(e); + } + + file = tmp[0]; + } + + if (file != null) { + MetaData meta = getInfo(luid); + stories.put(meta, file); + } + + return file; + } +} diff --git a/src/be/nikiroo/fanfix/RemoteLibraryServer.java b/src/be/nikiroo/fanfix/RemoteLibraryServer.java new file mode 100644 index 00000000..9ecbc41e --- /dev/null +++ b/src/be/nikiroo/fanfix/RemoteLibraryServer.java @@ -0,0 +1,51 @@ +package be.nikiroo.fanfix; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +import be.nikiroo.fanfix.data.MetaData; +import be.nikiroo.utils.Version; +import be.nikiroo.utils.serial.ConnectActionServer; +import be.nikiroo.utils.serial.Server; + +public class RemoteLibraryServer extends Server { + + public RemoteLibraryServer(int port) throws IOException { + super(Version.getCurrentVersion(), port, true); + } + + @Override + protected Object onRequest(ConnectActionServer action, + Version clientVersion, Object data) throws Exception { + String command = null; + String args = null; + if (data instanceof String) { + command = (String) data; + int pos = command.indexOf(" "); + if (pos >= 0) { + args = command.substring(pos + 1); + command = command.substring(0, pos); + } + } + + System.out.println(String.format("COMMAND: [%s], ARGS: [%s]", command, + args)); + + if (command != null) { + if (command.equals("GET_METADATA")) { + if (args != null && args.equals("*")) { + Map stories = Instance.getLibrary() + .getStories(null); + return stories.keySet().toArray(new MetaData[] {}); + } + } else if (command.equals("GET_STORY")) { + if (args != null) { + return Instance.getLibrary().getStory(args, null); + } + } + } + + return null; + } +} diff --git a/src/be/nikiroo/fanfix/data/Chapter.java b/src/be/nikiroo/fanfix/data/Chapter.java index 7d0a51b3..d9f285f9 100644 --- a/src/be/nikiroo/fanfix/data/Chapter.java +++ b/src/be/nikiroo/fanfix/data/Chapter.java @@ -16,6 +16,13 @@ public class Chapter implements Iterable { private List empty = new ArrayList(); private long words; + /** + * Empty constructor, not to use. + */ + private Chapter() { + // for serialisation purposes + } + /** * Create a new {@link Chapter} with the given information. * diff --git a/src/be/nikiroo/fanfix/data/Paragraph.java b/src/be/nikiroo/fanfix/data/Paragraph.java index e4c94d45..8a9130bf 100644 --- a/src/be/nikiroo/fanfix/data/Paragraph.java +++ b/src/be/nikiroo/fanfix/data/Paragraph.java @@ -30,6 +30,13 @@ public class Paragraph { private String content; private long words; + /** + * Empty constructor, not to use. + */ + private Paragraph() { + // for serialisation purposes + } + /** * Create a new {@link Paragraph} with the given image. * diff --git a/src/be/nikiroo/fanfix/reader/BasicReader.java b/src/be/nikiroo/fanfix/reader/BasicReader.java index 7b9c98c2..05e3e3df 100644 --- a/src/be/nikiroo/fanfix/reader/BasicReader.java +++ b/src/be/nikiroo/fanfix/reader/BasicReader.java @@ -14,7 +14,6 @@ import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.supported.BasicSupport; import be.nikiroo.utils.Progress; -import be.nikiroo.utils.ui.UIUtils; import be.nikiroo.utils.serial.SerialUtils; /** @@ -32,22 +31,28 @@ public abstract class BasicReader { GUI, /** A text (UTF-8) reader with menu and text windows */ TUI, - + ; - + public String getTypeName() { String pkg = "be.nikiroo.fanfix.reader."; switch (this) { - case CLI: return pkg + "CliReader"; - case TUI: return pkg + "TuiReader"; - case GUI: return pkg + "LocalReader"; + case CLI: + return pkg + "CliReader"; + case TUI: + return pkg + "TuiReader"; + case GUI: + return pkg + "LocalReader"; } - + return null; } } + private static Library defaultLibrary = Instance.getLibrary(); private static ReaderType defaultType = ReaderType.GUI; + + private Library lib; private Story story; private ReaderType type; @@ -95,9 +100,34 @@ public abstract class BasicReader { return story; } + /** + * The {@link Library} to load the stories from (by default, takes the + * default {@link Library}). + * + * @return the {@link Library} + */ + public Library getLibrary() { + if (lib == null) { + lib = defaultLibrary; + } + + return lib; + } + + /** + * Change the {@link Library} that will be managed by this + * {@link BasicReader}. + * + * @param lib + * the new {@link Library} + */ + public void setLibrary(Library lib) { + this.lib = lib; + } + /** * Create a new {@link BasicReader} for a {@link Story} in the - * {@link Library} . + * {@link Library}. * * @param luid * the {@link Story} ID @@ -108,7 +138,7 @@ public abstract class BasicReader { * in case of I/O error */ public void setStory(String luid, Progress pg) throws IOException { - story = Instance.getLibrary().getStory(luid, pg); + story = lib.getStory(luid, pg); if (story == null) { throw new IOException("Cannot retrieve story from library: " + luid); } @@ -162,14 +192,17 @@ public abstract class BasicReader { public abstract void read(int chapter) throws IOException; /** - * Start the reader in browse mode for the given type (or pass NULL for all - * types). + * Start the reader in browse mode for the given source (or pass NULL for + * all sources). * - * @param type + * @param library + * the library to browse + * + * @param source * the type of {@link Story} to take into account, or NULL for * all */ - public abstract void start(String type); + public abstract void browse(String source); /** * Return a new {@link BasicReader} ready for use if one is configured. @@ -181,8 +214,8 @@ public abstract class BasicReader { public static BasicReader getReader() { try { if (defaultType != null) { - return ((BasicReader)SerialUtils.createObject - (defaultType.getTypeName())).setType(defaultType); + return ((BasicReader) SerialUtils.createObject(defaultType + .getTypeName())).setType(defaultType); } } catch (Exception e) { Instance.syserr(new Exception("Cannot create a reader of type: " @@ -213,6 +246,16 @@ public abstract class BasicReader { BasicReader.defaultType = defaultType; } + /** + * Change the default {@link Library} to open with the {@link BasicReader}s. + * + * @param lib + * the new {@link Library} + */ + public static void setDefaultLibrary(Library lib) { + BasicReader.defaultLibrary = lib; + } + /** * Return an {@link URL} from this {@link String}, be it a file path or an * actual {@link URL}. @@ -242,9 +285,9 @@ public abstract class BasicReader { } // open with external player the related file - public static void open(String luid) throws IOException { - MetaData meta = Instance.getLibrary().getInfo(luid); - File target = Instance.getLibrary().getFile(luid); + public static void open(Library lib, String luid) throws IOException { + MetaData meta = lib.getInfo(luid); + File target = lib.getFile(luid); open(meta, target); } diff --git a/src/be/nikiroo/fanfix/reader/CliReader.java b/src/be/nikiroo/fanfix/reader/CliReader.java index a57de34a..7ac69e85 100644 --- a/src/be/nikiroo/fanfix/reader/CliReader.java +++ b/src/be/nikiroo/fanfix/reader/CliReader.java @@ -78,9 +78,9 @@ class CliReader extends BasicReader { } @Override - public void start(String type) { + public void browse(String source) { List stories; - stories = Instance.getLibrary().getListByType(type); + stories = getLibrary().getListBySource(source); for (MetaData story : stories) { String author = ""; diff --git a/src/be/nikiroo/fanfix/reader/LocalReader.java b/src/be/nikiroo/fanfix/reader/LocalReader.java index 2b92e721..6c615031 100644 --- a/src/be/nikiroo/fanfix/reader/LocalReader.java +++ b/src/be/nikiroo/fanfix/reader/LocalReader.java @@ -20,11 +20,19 @@ import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.output.BasicOutput.OutputType; import be.nikiroo.utils.Progress; import be.nikiroo.utils.Version; +import be.nikiroo.utils.ui.UIUtils; class LocalReader extends BasicReader { - private Library lib; + static private boolean nativeLookLoaded; + + private Library localLibrary; public LocalReader() throws IOException { + if (!nativeLookLoaded) { + UIUtils.setLookAndFeel(); + nativeLookLoaded = true; + } + File dir = Instance.getReaderDir(); dir.mkdirs(); if (!dir.exists()) { @@ -58,7 +66,7 @@ class LocalReader extends BasicReader { key, value), e); } - lib = new Library(dir, text, images); + localLibrary = new Library(dir, text, images); } @Override @@ -99,7 +107,7 @@ class LocalReader extends BasicReader { try { Story story = Instance.getLibrary().getStory(luid, pgGetStory); if (story != null) { - story = lib.save(story, luid, pgSave); + story = localLibrary.save(story, luid, pgSave); } else { throw new IOException("Cannot find story in Library: " + luid); } @@ -120,11 +128,11 @@ class LocalReader extends BasicReader { * @return TRUE if it is */ public boolean isCached(String luid) { - return lib.getInfo(luid) != null; + return localLibrary.getInfo(luid) != null; } @Override - public void start(String type) { + public void browse(String type) { // TODO: improve presentation of update message final VersionCheck updates = VersionCheck.check(); StringBuilder builder = new StringBuilder(); @@ -189,28 +197,28 @@ class LocalReader extends BasicReader { // delete from local reader library void clearLocalReaderCache(String luid) { - lib.delete(luid); + localLibrary.delete(luid); } // delete from main library void delete(String luid) { - lib.delete(luid); + localLibrary.delete(luid); Instance.getLibrary().delete(luid); } // open the given book void open(String luid, Progress pg) throws IOException { - File file = lib.getFile(luid); + File file = localLibrary.getFile(luid); if (file == null) { imprt(luid, pg); - file = lib.getFile(luid); + file = localLibrary.getFile(luid); } - open(Instance.getLibrary().getInfo(luid), file); + open(getLibrary().getInfo(luid), file); } void changeType(String luid, String newType) { - lib.changeType(luid, newType); + localLibrary.changeType(luid, newType); Instance.getLibrary().changeType(luid, newType); } } diff --git a/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java b/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java index 03df7fbd..edd1fb60 100644 --- a/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java +++ b/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java @@ -220,7 +220,7 @@ class LocalReaderFrame extends JFrame { */ private void refreshBooks() { for (LocalReaderGroup group : booksByType.keySet()) { - List stories = Instance.getLibrary().getListByType( + List stories = Instance.getLibrary().getListBySource( booksByType.get(group)); group.refreshBooks(stories, words); } diff --git a/src/be/nikiroo/fanfix/reader/TuiReader.java b/src/be/nikiroo/fanfix/reader/TuiReader.java index d18436f6..d00bbc29 100644 --- a/src/be/nikiroo/fanfix/reader/TuiReader.java +++ b/src/be/nikiroo/fanfix/reader/TuiReader.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.util.List; import be.nikiroo.fanfix.Instance; +import be.nikiroo.fanfix.Library; import be.nikiroo.fanfix.data.MetaData; class TuiReader extends BasicReader { @@ -13,7 +14,7 @@ class TuiReader extends BasicReader { throw new IOException("No story to read"); } - open(getStory().getMeta().getLuid()); + open(getLibrary(), getStory().getMeta().getLuid()); } @Override @@ -23,8 +24,8 @@ class TuiReader extends BasicReader { } @Override - public void start(String type) { - List stories = Instance.getLibrary().getListByType(type); + public void browse(String source) { + List stories = getLibrary().getListBySource(source); try { TuiReaderApplication app = new TuiReaderApplication(stories, this); new Thread(app).start(); diff --git a/src/be/nikiroo/fanfix/reader/TuiReaderApplication.java b/src/be/nikiroo/fanfix/reader/TuiReaderApplication.java index 94ecfd52..9a534983 100644 --- a/src/be/nikiroo/fanfix/reader/TuiReaderApplication.java +++ b/src/be/nikiroo/fanfix/reader/TuiReaderApplication.java @@ -5,7 +5,6 @@ import java.util.List; import jexer.TApplication; import jexer.TMessageBox; -import be.nikiroo.fanfix.Instance; import be.nikiroo.fanfix.data.MetaData; public class TuiReaderApplication extends TApplication { @@ -55,21 +54,15 @@ public class TuiReaderApplication extends TApplication { public void open(MetaData meta) { // TODO: open in editor + external option - if (true) { - if (!meta.isImageDocument()) { - new TuiReaderStoryWindow(this, meta); - } else { + if (!meta.isImageDocument()) { + new TuiReaderStoryWindow(this, reader.getLibrary(), meta); + } else { + try { + BasicReader.open(reader.getLibrary(), meta.getLuid()); + } catch (IOException e) { messageBox("Error when trying to open the story", - "Images document not yet supported.", - TMessageBox.Type.OK); + e.getMessage(), TMessageBox.Type.OK); } - return; - } - try { - reader.open(meta.getLuid()); - } catch (IOException e) { - messageBox("Error when trying to open the story", e.getMessage(), - TMessageBox.Type.OK); } } } diff --git a/src/be/nikiroo/fanfix/reader/TuiReaderStoryWindow.java b/src/be/nikiroo/fanfix/reader/TuiReaderStoryWindow.java index 7eb592fd..0d47cde4 100644 --- a/src/be/nikiroo/fanfix/reader/TuiReaderStoryWindow.java +++ b/src/be/nikiroo/fanfix/reader/TuiReaderStoryWindow.java @@ -13,12 +13,14 @@ import jexer.TText; import jexer.TWindow; import jexer.event.TResizeEvent; import be.nikiroo.fanfix.Instance; +import be.nikiroo.fanfix.Library; import be.nikiroo.fanfix.data.Chapter; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Paragraph; import be.nikiroo.fanfix.data.Story; public class TuiReaderStoryWindow extends TWindow { + private Library lib; private MetaData meta; private Story story; private TText textField; @@ -26,12 +28,15 @@ public class TuiReaderStoryWindow extends TWindow { private List navigationButtons; private TLabel chapterName; - public TuiReaderStoryWindow(TApplication app, MetaData meta) { - this(app, meta, 0); + public TuiReaderStoryWindow(TApplication app, Library lib, MetaData meta) { + this(app, lib, meta, 0); } - public TuiReaderStoryWindow(TApplication app, MetaData meta, int chapter) { + public TuiReaderStoryWindow(TApplication app, Library lib, MetaData meta, + int chapter) { super(app, desc(meta), 0, 0, 60, 18, CENTERED | RESIZABLE); + + this.lib = lib; this.meta = meta; // TODO: show all meta info before? @@ -46,7 +51,10 @@ public class TuiReaderStoryWindow extends TWindow { // -3 because 0-based and 2 for borders int row = getHeight() - 3; - navigationButtons.add(addButton(" ", 0, row, null)); // for bg colour when << button is pressed + navigationButtons.add(addButton(" ", 0, row, null)); // for bg colour + // when << + // button is + // pressed navigationButtons.add(addButton("<< ", 0, row, new TAction() { public void DO() { setChapter(0); @@ -67,7 +75,7 @@ public class TuiReaderStoryWindow extends TWindow { setChapter(getStory().getChapters().size()); } })); - + navigationButtons.get(0).setEnabled(false); navigationButtons.get(1).setEnabled(false); navigationButtons.get(2).setEnabled(false); @@ -113,14 +121,14 @@ public class TuiReaderStoryWindow extends TWindow { if (chapter != this.chapter) { this.chapter = chapter; - + int max = getStory().getChapters().size(); navigationButtons.get(0).setEnabled(chapter > 0); navigationButtons.get(1).setEnabled(chapter > 0); navigationButtons.get(2).setEnabled(chapter > 0); navigationButtons.get(3).setEnabled(chapter < max); navigationButtons.get(4).setEnabled(chapter < max); - + Chapter chap; String name; if (chapter == 0) { @@ -128,7 +136,8 @@ public class TuiReaderStoryWindow extends TWindow { name = String.format(" %s", chap.getName()); } else { chap = getStory().getChapters().get(chapter - 1); - name = String.format(" %d/%d: %s", chapter, max, chap.getName()); + name = String + .format(" %d/%d: %s", chapter, max, chap.getName()); } while (name.length() < getWidth() - chapterName.getX()) { @@ -157,7 +166,7 @@ public class TuiReaderStoryWindow extends TWindow { private Story getStory() { if (story == null) { // TODO: progress bar? - story = Instance.getLibrary().getStory(meta.getLuid(), null); + story = lib.getStory(meta.getLuid(), null); } return story; } -- 2.27.0