From 2dcd37c26cd15afb964f52a998027f65287bac12 Mon Sep 17 00:00:00 2001 From: niuhuan Date: Mon, 18 Apr 2022 10:55:08 +0800 Subject: [PATCH] Comic info screen's "Recommendation" --- .github/workflows/Release.yml | 58 +- go/.gitignore | 4 - go/assets/icon.png | Bin 33686 -> 0 bytes go/cmd/init.go | 58 -- go/cmd/main.go | 70 -- go/cmd/options.go | 88 -- go/go.mod | 21 - go/go.sum | 127 --- go/hover.yaml | 9 - go/mobile/lib/.keep | 0 go/mobile/mobile.go | 22 - .../Contents/Info.plist.tmpl | 38 - go/packaging/linux-appimage/AppRun.tmpl | 3 - .../{{.packageName}}.desktop.tmpl | 9 - go/pikapika/client.go | 617 -------------- go/pikapika/common.go | 76 -- go/pikapika/config/common.go | 29 - go/pikapika/database/comic_center/center.go | 583 -------------- go/pikapika/database/comic_center/entities.go | 107 --- go/pikapika/database/network_cache/cache.go | 99 --- go/pikapika/database/properties/properties.go | 127 --- go/pikapika/download.go | 498 ------------ go/pikapika/export.go | 510 ------------ go/pikapika/game.go | 40 - go/pikapika/image.go | 130 --- go/pikapika/import.go | 196 ----- go/pikapika/modles.go | 33 - go/pikapika/network.go | 24 - go/pikapika/pikapika.go | 755 ------------------ go/pikapika/utils/const.go | 16 - go/pikapika/utils/file.go | 42 - go/pikapika/utils/mutex.go | 21 - go/pikapika/utils/time.go | 8 - lib/basic/Method.dart | 2 +- lib/screens/ComicInfoScreen.dart | 3 + lib/screens/components/Recommendation.dart | 1 - 36 files changed, 60 insertions(+), 4364 deletions(-) delete mode 100644 go/.gitignore delete mode 100644 go/assets/icon.png delete mode 100644 go/cmd/init.go delete mode 100644 go/cmd/main.go delete mode 100644 go/cmd/options.go delete mode 100644 go/go.mod delete mode 100644 go/go.sum delete mode 100644 go/hover.yaml delete mode 100644 go/mobile/lib/.keep delete mode 100644 go/mobile/mobile.go delete mode 100644 go/packaging/darwin-bundle/{{.applicationName}} {{.version}}.app/Contents/Info.plist.tmpl delete mode 100644 go/packaging/linux-appimage/AppRun.tmpl delete mode 100644 go/packaging/linux-appimage/{{.packageName}}.desktop.tmpl delete mode 100644 go/pikapika/client.go delete mode 100644 go/pikapika/common.go delete mode 100644 go/pikapika/config/common.go delete mode 100644 go/pikapika/database/comic_center/center.go delete mode 100644 go/pikapika/database/comic_center/entities.go delete mode 100644 go/pikapika/database/network_cache/cache.go delete mode 100644 go/pikapika/database/properties/properties.go delete mode 100644 go/pikapika/download.go delete mode 100644 go/pikapika/export.go delete mode 100644 go/pikapika/game.go delete mode 100644 go/pikapika/image.go delete mode 100644 go/pikapika/import.go delete mode 100644 go/pikapika/modles.go delete mode 100644 go/pikapika/network.go delete mode 100644 go/pikapika/pikapika.go delete mode 100644 go/pikapika/utils/const.go delete mode 100644 go/pikapika/utils/file.go delete mode 100644 go/pikapika/utils/mutex.go delete mode 100644 go/pikapika/utils/time.go diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index d80afae..75ae267 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -28,11 +28,18 @@ jobs: with: repository: ${{ github.event.inputs.repo }} ref: 'master' - - name: Checkout submodules - run: git submodule update --init --recursive - uses: actions/setup-go@v2 with: go-version: ${{ env.go_version }} + - name: Cache go modules + uses: actions/cache@v3 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- - name: Check release run: | cd ci @@ -75,6 +82,39 @@ jobs: with: go-version: ${{ env.go_version }} + - name: Cache go modules (Linux/Android) + if: matrix.config.target == 'android-arm32' || matrix.config.target == 'android-arm64' || matrix.config.target == 'android-x86_64' || matrix.config.target == 'linux' + uses: actions/cache@v3 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Cache go modules (macOS/ios) + if: matrix.config.target == 'macos' || matrix.config.target == 'ios' + uses: actions/cache@v3 + with: + path: | + ~/Library/Caches/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Cache go modules (Windows) + if: matrix.config.target == 'windows' + uses: actions/cache@v3 + with: + path: | + ~\AppData\Local\go-build + ~\go\pkg\mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - id: check_asset name: Check asset run: | @@ -88,6 +128,20 @@ jobs: channel: ${{ env.flutter_channel }} flutter-version: ${{ env.flutter_version }} + - name: Check core + uses: actions/checkout@v3 + with: + repository: 'niuhuan/pikapika-go-core' + token: ${{ secrets.GH_TOKEN }} + path: 'go' + + - name: Cache Flutter dependencies (Linux/Android) + if: steps.check_asset.outputs.skip_build != 'true' && ( matrix.config.target == 'android-arm32' || matrix.config.target == 'android-arm64' || matrix.config.target == 'android-x86_64' || matrix.config.target == 'linux' ) + uses: actions/cache@v3 + with: + path: /opt/hostedtoolcache/flutter + key: ${{ runner.os }}-flutter + - name: Setup java (Android) if: steps.check_asset.outputs.skip_build != 'true' && ( matrix.config.target == 'android-arm32' || matrix.config.target == 'android-arm64' || matrix.config.target == 'android-x86_64' ) uses: actions/setup-java@v3 diff --git a/go/.gitignore b/go/.gitignore deleted file mode 100644 index 596bcc6..0000000 --- a/go/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -build -.last_goflutter_check -.last_go-flutter_check -.last_go-flutter_check diff --git a/go/assets/icon.png b/go/assets/icon.png deleted file mode 100644 index f14bdd15fa6e4e49b922ae7f252988a2065be705..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33686 zcmX6@byQW&+npP9OLs~O2uOE#O1FTNq?F{PkycuyOGO$Nlpg<$ipiHprbxKOg%om1ONe)<)okc&irdd z3!v)py5Roh(XjgdMd=;U^}bL7Rk!;;q59RSYGR}JkF_IGrZ}P({P$bWaq(Mg&ljX0 zF+rgC=m@yf$-@4S%ggh-{r=sMC_lvoY78mS+`(%gG}ph|7Pv3E-)n2T_*ifEuTdo`@N(S1;x_brUm)N~ zU{{XoBJcTi59;;ir1AWtpz+=8o0qwoKNZ97uIq8o)-vKz*G1wY*8Jk4NWzsmF;|NS zin6R6#C_JQfL`r$E6)h z*m0tg>=9;BN7I&di11+{WICVm&M2p=Ei)m^eCw~c3!A8j*`o;`p&#GpEvc_|G@k_A z?_S|X?MJn@Y;k`NKAgyHJ^IaI@c}J0T`nnF{Z(RimT-MPw_w0!!RK=y6SLJ|k)`y@ z0WGD0oZ%`cse_I7iH(iJ-gCKMpEG;kQ!>?@K;k)f|1zlbT=wda(`u`{eEeOG-l`iD z;|W6mx(fYr*oJDzidV{zOt-&X@xkGJ%A>DW4MW^*2A5{XkL#3vMv%lOCw0;oxh;2P zE`84;lzvnC7$SUW9d!KytyOpadhQX<>@MS^Ugp)^cbl-oMHk=G@k)#;EtH6lQfMiH}*V2{`0=it8e8zjaEEQ?viUZVP5=>_jI&+%&Uxlhz7)VkfF7xWkOCF zOTNo=1guLZ@7N#dT^SaTCi6!8_(ZUNO|m0NiHYods>kGbigoaXn z94aYxKYVRI_h3*$d^?~yYc;+&mx>UNE#7_j#^!AZ+CARA-m}l zC3Cz$mdNGxAx1cZJ62e$yPJw^z3?o|8qpO_GD!V4wa}q)Xb6t`qQx6ICX_5!zJW$egE zxay~zyE&F+K7}(UD!QH!LZS(I^VwnX-%stqu~6I=rLx#rs^GJ2iyHnFTLSbKqjvsV z`4+#=6|M39Wvhso(#1-S4Jhs##98)DW6_9;zCL;1wCt<)`Rc%gh&VId&(f10E28Ha z4i{@~--WGDf^Sh;{fyiP+G(3JcGEI$41&<9A7>|p0sl-$EU2Q{Z{}@AhTV)h^Nq=t z?fwdh{aydUc>T3D7Wb=QCM@ESytrJkODD?Tezgmmg?+ELiLI@Z$K{jl=dG^M!y!}r zE(??b0aiTT_dB|5iaPe4%Urm}K2*-3;RF%|z7<`ujMu-TiBhDrN^sCOrJ`{IYMZaK z3D4#ic5WAf#Uewdeh3JD-;ZUzYWP)Wy4nRN`8s zh3dshqI2C1vFR~)dtQb&(X8D!v)9;{SH4H@#A{Nl*G>OPPybVDM{ddY7-w(EOwV0O ze}bZZ46b-JC%)^Qo!MTz9DMV)(n9O2EWU2|po+^PNy@!A)vm+HyC4A+v_e?foMN=b zeI^1@f?>#q&Pm57ag93bT=+VhlY?%^NYSj?Jc4Lq z-@HEZ)0TK$U@V5M%Q;ezM1w8Voh#Zjedb)1T^U1X*qtDmQFXt;M;^Lb%hT>=p1~c% z8r#PZPKWMc6R2^xp(^<_P&OL<$C%}}x`d1xS~c5`oNFt%{X9KA1d;&@Iwsn3Mk>!4 zph`F|o2d%$SXyf@CA%p)FkPQXzg_&$Cq^Jcn5bmjddoJ4m~!)XoUGs5>wdZ z-3D!h5v;mAMZdiM8ehAMiz={RuTMlB8I8dF~;(pD;J+wR~f<` z7)PF0PwH(g=kX76t}k&1kjogX#}6}KK_S>YW0L77LO;#beP53T$2xQzQ=GQ8w$sh~ zjD#@LQfKuaGyZV1APOu+DXu*YaoFcnE`F=IV9`pq5ODHuhK}-7)6A-20d-9> z=T9O#0nYOjJ6%fr0tgpf;)!VYe8SD@&qefM#MH_7cZ-7{hZl=)TY0thj9le^D!HA-E>NBg?@onJhbY>HCqSv4w4{9Ndo)(r>Z)Sh^~d`wv}R zx>Ca}NLmg4j0cRw5t$&YX^{fS7pJ$e15wYz+?e6#eC9gWLR$Sz^XA8sM@Bil2Oy}@%&#W!1co-&t zAI@Ha*|KZBPkwXKe!q&+{Tla2z2T1P+`ahwH`@}g18l1`J%h9-pm1afn(3nt)IrA+ zt`=HilTxp~II~_3wGW*TCQcDqXf0cBkjkfwb1irNWPBA#SNvz+hxR)g7ln>PmqU~V zqXUbG(f`lH56A5NWTIlRL_5*Ws6i;z|4nHW1qN_Wgu5GFz1bzbKh4Zw_#rP*)2F*m#dUze>HED?lSo^_DHN9L zrs|$$rq*A1cXusmJ{lY0_J-SJIqFhkwpH<=e2FlP%A2>3L|UkYKi8tS9rSYtZl#h= zZ2o27*4Psz?ps#A-VhH;IkS5_m&N$!))hrL16P8wzIVUilhV7D&(Eb%3sSCBW_ zg;fUF_0LQ3NBW$0Qzab+HN~`Cb;cOfA96d6$!}Y9wd>hdPs$O<>!leNW@WV$gnW&|RnFf)V=pLDp{}Go|4}Sa@m>y?jA5 z!*7pA&!wV+aX$;-h+VIfnqNfewWK3`f4*(yuSs#sIJe&9HaFFl)8_6hKQP2&y`AC& zrdzXR6*NPshDWmR18l;N`}OB$C>av&3Ze^y;h2W(Jli?EE@!cmljUFhQB?$dgRMkv z*NC^%WnVVzlW>1wEf6yz0x*^eJYP#{{aZnc`zy)H52uik6T&m1Sc>0S0Y9+VV^}q_ zG5aoFzAMafUO|4l^XM*xnhoP$u~5+v$_8u84XaCQDWMeol)Sw~;}9W6mY!&o<_eXg z-yZ+y%w;XuWW~(86E0d{Xk3Z4xLLu|f5py|=KYEzo|XP%F3(0H+5>(T*t5tG@vLVn z^DYP;1W6xWRHu%A`Fsyc{56KpAMXMGrBg7I7q@`F3;`M*;Uyteb-uPvCx z$Xf3=nEdXsPy5a12P%tRMWYJ73jY*tTM&JuMxQi@YW&v!L*Jc2@Ei))#kUQSE6q|G zQH7@`J?awB2l_IefdoI zUTpaVoD3d}YKb@{>lTGVU`K`@>UB*KLlb-bs5E>E~&z|0hzc4VneGcIztlD7vB zIGv#;L84%}wvi%u8@=3qCxu{>@jd)UH1vf~%mx1XHdG5L#j9XJFb&nS*b$|zLKT<~Y#mX4JmE&Oj~ zK*wtM>bRl7pj1@zdvd^Q#^x7^PBpo7GSmHVOE%mc3Hr4wT8b?%(S(YoUSM`h-ruA$ zT0Pd>FbvgVwhfnKlOxglZNq$2t)`g9$|OUt%R|veyd2zleZW_vBriF|BCmZpP%5&( z)6H0tTUzn--Ub>5Y~QQ$uroE9Q|qL6pA?S`7wOQYnA5R0Ki_s&kPntqz1@NXD=5$p zpWmof3i!VAHf5qEEqeQZ1|ZH z<0AeNDjPyF@v*7nDHB&6;y!e-v3=wkt)(vrFqPNxbD z=#cXAxy7Jwdt1O*uZbyP!dD|Nag*ZYZS`5}>6 zH)AXvn2s~4#(w@(n2_9%V~>*Y4w3xMN9)bNNyC{X5=~lPrXl7*rkRygwVCg_%2BQ& zTW5nd3JE{sBgJXH@NY**8BUOOL{C@z5%Q_9E;SGl6A=};f(uZt&(irL5Lu*0cHLWQFEiUa|l*Fo3>2OgCiZCh;y3K_ks9 zi6QFGr*IRDs^bu&2tMb>+G$`q<%KMmha5uT!m5UrJrB-lQ%;@6K$mBCFG?3 zvt)S{dY(@<^{a#`%J^=;jlrk{amjZtqD8%sJ@O?8JcD%O!k73+C2AC}L5 zt`M3Ac4${{?Er&h(_TG=y;b%ALi;9J)>a2fnSG(~VSkNxeFQ^|!&S2#pZo!)(8}`W z+oVnc4GfsqJBX--*f|19Ei5S#B=FkL^8zwSG_w0qX@+`LsVorNL1!BW5j|kCP6XK? z*5LRjyG5NyW=#B1#mV}0)3w86Q^xHtCq$DTYhsgr+n9?%Z6Eg=J!AL3n|Ao58yw9G zmc1-K{_Whhx}x58dZO2MIV4;`aB|_`_|$+rZD8K*ZQf)#bPH#P&e)1jjl)^ip*#@o z>>0;g4*qFO%8l&j$S8=78u9a@^l-yOi@zGiZ~eZ})}<#x`Iu=bt=>iC(G8JOg4wt& z^hnfm1xg=sLgqQ0~XMP=g7`MT(V3z{?6gzk*6OQ zs!;6C6H3olkDFe)ttE_3UbMwf1g&*21{L^7X)u?rd6-v>{yhF3;OJ>8F*o+p$GrWI zB&+x@J^9^L7eF@T{skf+q*L$v_#Ky|n+SRc0V?4#kFh)n3|(dVjXT{d3g|FTF8g2!jPJDze8QL(CbN**j_4)R9L5? z_Xw;f{}mo^VZ{Mck{zJm!!_;lAf)x{>C2sfB59`-h6JL>MzE?VBGKic8;Idp>`v?L zERLe`KOZG4)D6`dj%`qN6q-9<(GKD;9^;lpw@F^#qzC68vtZ&x61Q`~P_)-qXw@M4 zAJ9kD;Z#&XQiun7e)45iuCRn-Dl63=H!~(79A`@Kj)?g&y#CpuZ7ijYykC z9cD*77Y|34CkdQf*}k`mHm_26ilYt5p)_H{e>~U6ui2=iM0*gKRysx>`92w3(vzLe zw(8msc3{mR`yI_kCi+LW$S+l#_*Qk^a3imS{KyS(31lMPa+g=5m*6=w*t5uS!nl6f zP!(jb39)1*0_fu7e}Z+s+Svo?nOYFQ(R)S?fTvEw^OdtQpH~r4vD+2Ok8qi+qeDc?q3*S7JIFX z|BI&sx+M<8vjgLDIT=0Vqs7WQ>E`SeWmUDiFC)|}j7M!PUIAWexIX<;QTj3>la|yP zqI5CZ2)eKkF!;^{8(u~RRD25)bLL+Kn;MdT%HpRxdlJh%Ah*A7vn&adT8Kdbf4es6 zgd7W2#X@g%+b}5=*u;IDE@OtCSIjX-ug^TmQ7#&kOzwZK7C~A~0Wo8zhz?Bw8_Td9 zY^ab*LUU=gX*)fGzuq=(7LiWV)m+z{p00)8@8Da zpb$VEq+0ZTARF3mZ0C0=rwCK7MU9l`JGv!RBQ-ftz!EkYyY)pPv3;N3dRe2@ZeGK- zgvB1^AOn<>rry8wJ-v{m!zW$}Ly^aM1OUU7FE1Z=_^AR?xoYU#!SL^w3F`W{A@pz5 z9iRG)4`nUz;T1jc)-DPC8t2TG|O5`#zVXN0hjaRo78owfHD44xNESg67&ICZHe zvcp!8^_UhWOna14lNov`L#Klz{+n)T3BXmT<{oAW z)xr2H-mNr+M+;L_7+g2hCMBa$9T0ml-Y&@N)eM@%Ie`1FS>}zGnN1v4Brx8wKry z>4Rh-1^-hY=Q z4Syo^Gf{`U)8V|2GGL#_j)2)EK=32^V(UIcodsX6rw3n-jfSW%C`+bUwXJ^6fEa#e z5$lOQEXmaZ2wH%AvkOD?`2t34{ksi>~Q8!)4I-~LZ2(uzxCw`Pbo*5a%8 z!88oiU)`2AVzrn#lsl8{9x1~Rf4Q)Rdf1REC{kPl8dKfEvR@=q?EOk z?0I}eG3IzwlYG`8>`GV|962abxsO0`sRW^dm@qH1q!X>2>kNGOVFVOI{YR^@Cw z^2uetr%hzNQ4vb#}F%gfpv_#`f}Lkas#CDAV3^sZTOOfeEz7W$zk&{ zlAP#nKXaq*eDAn4PRu?vP-yo+<*{{0)4w!>FX0H0d3qp&0vZ8WIfVf=6C2%8kFBBT zfTBVZiIY!Zb|W(#iKi#ON{b2qi4PLx$rc{*{gpULQ|EDZ*JSE~O#iF5D#+ifD6>d);{^|ts=o36oe`i%J z{^f{GJTh3VY2-5>p3`=+aA^>tH;jo7!CO7NrW=CtqNy=+9Y9;sk7f$5>-q?I|e42^izlQc}zBh94> zBlr+n#UartqMj*?U%7lQN5>}ru??#0+Hg~_ZS9O%yL#WrVB7Ke4zjrYNDgcs{qeO% z&6QYT14s_VhEOze^DnRIt%`ZJiQk*kMgyvEiNKis%f*Qo>Rd?tL|;A!cA$N{16?^E zUOo;^NvA|;y6e*`}af#qowz!LdrQSQsDuSX}iG&kj<|Ig|94|U1 zjf}fS5a)ql+roZ^k{|Voa(W6vndYyeiYp_ z=2JcqJTHCXk~Da=!PCQR^>~m76ebOB1lVI2HWHTepIJBWKmzj?B0nbg@@zW(5RQmz z{DgiZ-glDmVIE%yk{WgfPJnsdT`@`kGOG$F<@oAAU#>yc0QT;dA~enHG3 zG&r3tN-uukMTP-Mric3D>xG#d2-oeoriTSG_I-504SC=FDJtT-9t0skR{WG(zR({q zl8Su$@g!a?)TKm>H&c!8mkvSq&P5Iee!%@FRdzl{PlP_%?)DRzspx3!?!#7>x}CjJ zk!NckLK)|#&A498el^dU+$&OPuv#Kz59Yif^l?~3w8%R^c7Mmgf+!omi^ zNp>(m@f8eVCm>DRRyB-D3cuY{#th+Bk0jCiB?o$EA%N`88a**V6f-hfA?En&HrIz0 zaigzfVY$*>>CMHp5?3AMJnWysqQSDqixx)bCO26-eB`HL`UH@LaTQ=GeQCLO` zwCgVOyhWkk7C$fJxzywnTiQF<58GhNuuto5%Z+Ea+jME;jADExor5Aj&MeE#RxXZ{ z!+Bbf&kGkM-s4`d%NVIc+!onBEW}RI05b7Z*}H@{chO+5betL!_8CCqepXqiVGZni z=KCaL37ZicIN^O<8KCeVHLjvpXZ)0kiQtNL+wy6<8lUX=)?P#aXbcr^?U{LUd*}F! zSjPAi&qD#SGbX{79t=2=KLiQ{W`<@X+v@t6BjcKiLUd~DM0`5Auqix9$UH>0UMn%F zrx9@e7HXZ1#}#bY`4Qu8ytAgS=r_op1-XvY#qUXbFIm$r1p({clyYVaGe7yJBk>Xk7$gBW;T|Z+) zoHYCyq0U#*tZoLn;iyCmZ-G~ostdPw?R)UaJHB`T25Mb?;-&O+O&p^k)!Ya&F*!~3 zOTy^nu%1})5WdXoej<5ApI!)He_fuQ^S!{X0vp7MjjNJwF1E52ZLA=7ZZX{+ql;s-2TAP zi<0zr>~$R0Gza&*ORcHJMM98l8(MzjbGZ_p%6YOuNvLqi`lodb>$}xSt^03aAkc6n zLvHSV$C=x0w|xv9$c8kX@sW9y1JH}KEDb=knG64k!45~qA9@UE%v$)c5NAeUQ-eiC zRssEFhULBAYo6wLnmTF4YckV=m}RbgkJ5M&D^H-{?Rij)ovf}Lvh3YdQnfln0=mkx zZ#cnMFD%3*KAEQ)v%3DfwUQ9LR`QI_trvdZc<*^sVO442XW`z(Zb5Ett~mdmR@ut~ z%aO;aMQzp3%tkj*z6+6&ZEB-vg00;u9iZrmf^oI^b3e?G9@@#)(08&2Q;mutV#T5=<$k$_$GsApl zOX1p)qK;WgalQ2BDa^pwfl@(KTzC2v*ml9xVWXlOr=fM>DF}(zWS^Ypa8)FL&PS4 z10@WgIpbI?33w1P?Swq`r=mY-M8FDl-13}IWXx;O# zzCBy9F3CCB=JL53VoSrlEFd6d2RxeiImsdDKnc?4iH;Cozyv}N;Pz5k@#;fmi$jAN zZ)h#_VRw^EP$!s;82cC$@HS%oT&P=k8hPe#S3~45BB==hUOmKGRiAP;uwV!p&jKRy zspl;Y2U7;^C&iMVvN69mjxM*1Lf-IZ@3rYa>?f&=w$^P|KKu77U%r?|tONJXn3pGu1%NsZe8iA`M*JBGr* z`n8Y@ZKy^1v++(v%>la4%uv`w=#x{w;xOlRz9_RNfDQ_-o)p?h-W|7JYwae7(Hbk> zdQxZ(J+UT+(FKn~!DuW7r~P#IJCS55zgbafC&mh;CpHmC5mEjX?2_DX=s^F_Tb%`l zi&4}e7Hs4O8{;U<#q&X&h`{s5IQI2% zbnE_NTfQa@`VLap^Tv0*4;*-sImO$zD9OF|o<*M!%++RaCr-2AN9EoR6Pn&srdhz( zGV9T(aV6!%<$#zI_(b6zYx(2-fO$DMz*nfY&f2w4_4@Qeo-Xc1%`;43i#d)uiCJkh z*za!)$-7@>yj#*>F}5OBo1IJkMJBWpZn4K+2C;ec+4>R2Z7yCxNB6z=We?)owpq>J zrm8ArrF$fOX--1FaJ-kFY^|;|G7$BJiwPX`G6LREpH2rX1PL6;q7Q&8kYXZ-F-|nN zQ~}YyDRMJk8I&VJ1gRShDpLs>78?K1F4(K@8>6t5K;7^CIpG7N&$e+libS&sm0BI} z%VUQjK)?_HG5a!GA#w=g$Kl$&gF9jjkT=Tw;W_!$i2f6-kcYeD=8pR8q{Noa*-M?N zFvv)5JH0%kU&}}A?hVEC1ye=yc$Nkc8puq4HW@|F5W(aNoDB~3BAN5b1yOX z3cFoB_cT;YZsCdXGHWUv7xt7P`!mqN8(rV@IE-MU3Pu0eD_*+;4wGVN!UK)DhDF4{ zEw}w4Q>gHite*~)oW3&fy^nzx@XON=^2Du0z6*3uMcg*rIx9{*w+`p^2@LTq3oBq z8~fSMWyaYs7in)$kb#Ls);;%r44MaHK#XI9j~JWP@r>VoLh2-r5&9cE!XnJv5MU~Z zm)BoL0y1qVJUzA<|6nbxN}9V^Aku%{zSh(bE$t_P7;u1QFH}T~9WGdBop=z>Xu#L= zq~Hu(OvNn7fJO;XfL&k52j+Zn!R9x;zJfVTwqXKr0|AF+6kt+7c!7Y1ta;55IjDS7 z=%E11p7b{gxDZ1FQe;kRnkL`iQU+ePuOU+RVTywgU}V#|e#q|_==fQGgN~Xi zE&Wxk$@+sW3mR+tztIHTkp3x04KUhHzg^0d1LhP^IsldqiNL`SHNd3@*a7y}wNONs zBYa77_L~}HszQMliw*GzNXG)Y#*8hEbYLluoX@G#!vK@mtvedep!Rb7+8Mc^q`a+~n{DAyJyXtdqZ#0MSSj`h1FG8Jd2eU=k1`7!YW z22WyxycS#*fOwk#v3$T^e2`~1uWSW&s& zM>z<P3xn=Obw_$qHI=v9o?p)&@lUp zw<>IiI_xb75WLWgQYRAekUb)RxDl?_iUR1QAgRJ&-l~x!gteTqpwy8wFQ>5}b+1{{ zdF|Y_p;YjxUuRU*fC8YPO0>vP5RiaKrETf4Sn2`+|>@ydRXGY9VAE>Am9%d_JI+AluYt;ca?YyU$#w=BiM38VK@VJZaRvn$E0v%&76 zlzrvfc+HEK9F8u2k4|h)wrFbIrWb>6vv$(H(=JU2oCkmGJosq@0hv;Gt>`d>pNPit zj1yk})kjK55hek`1t1clv0f`s6BlMkc=4g!;)yc7Cn0-QjbP5o7;${O6u%a*cfl;` zHz<d84xlPi*XWx{dxqDlT5|;7Wb#;^_)$DmgUMwC*6hV zfBk-F2)p`7&}#t!Rr(Mq-apo-pR<&Q?(+hnZ!Bq4TtpRgn$iTn<($W^YPh)PeG^!& zL(^d#OMI0{lt--t%pizz7=mc&q2_Da(0zx<{T7Ec)vY*he~yLU?CS};-uZe0n7%9Q zD)Ug_neZ&r4j>?Z?2qowgI}Om+?99l!}+nU*NI$OzrqntC3kYV-~tBbp6%$m0wi>X z1|S{-Bm^uKNX}c{aeJxQ>b)`}TB)MuJuR|p5E;Jqepffhm#u#K1OuR(%u++ZPZ226 z4Phk!g1IfH>Gvci za&VivDsa2_4dji34NOe}y;ZtLPfGRpHY_4@SK-y#EESMUbD_@ z6eBi%u%jTOy*R7ye(leteP^udqTgdQ^{kzN&}G69s%5Ca9==J}b*2n2?RPWC`qe?u zz0W%?PB_XSaQU0mBE} zLIX@SdV0+hXc`vk((Tp>Z;QIv-#=e&{Wn<`tTwfN@O*DdfimXStC{(|yJYl1=n?7o zvn-j@Kd9gb6hHwe{#ZY(xaId6AVi&?;-LU0q;LsWIU1>S+I9Q(_49?qGJ_tf_e$w{ zM4x{~HEvo;B1KW6E*M3riFa6}e5{3TqX-b1^)HxvZQszhU3`s!eq%T#DR2@0jg8H@ zT93iBElB$qa}dpN>dI7?sw(C;KoowIKruMCqF0Gm>xkyxDly7^MGf$cSJGvh=XI67 zsb`!4q2O%Ex(<~GQ2L{2MU?duUAgB9DN&qsjW_l4T~rDENrH36geYYB^^uB?$WW~b zl>_zKk5(5p8>nZPNT==SF^WYG)SyL{s^i?LC84b{Ka)G`Wc8;8-pmUUg-9#U9H?xH zu-)c!H_t3LwZ~xshef~mQ=Vs_YGvBUnB1q+`rxc;?79~|vs9eV6UETqq3{2QAjpyr zaZHH4SmXM#(NqAJiX1rQRaDILdF#xee6bi$uDXZ8vy}5Uq!1r!sX(i-|MjS)$~!oL zmD*zcn{vAt@A8+U>2(dF5fpM$y6;-P9bd}3?90*sQqL0`v_QaL09u%McT*}}!*V>; z7#W9rT2SKdP}_`2?^Eb~htWz-etR2SA(_H)n@jlu3ld0Bz&37N%RCLRj0QtoD99H4 z4FIsUCV^#hn8abTr?K2SfB#$ionuwzEpN6{2nQFP+3?aaAmjL9wjaPlp+vAJK zmjvL_b(hn`Q8$S+s+D(@nLg!%yJ?_yfpJWr&{G-$B> zMmSy6iU-)h&Q{nEBkJgqwL_I;ekQ@nSmfY);_pB z53dNEa4+iS|2T&N3#z;Ss*!^px2t21>Ej#rtTke}-6)*l{;d0^{ooQJ^@qsPVg7RD z?~1O-=zRZI-n%7lMZZx_M8tg;+1)kUh-xM;irWMMfaXvG=3U+1x99!bA(id*Qx#I& z@o#KtehOAoAao;xP$2kPpY)jnUlW6&JWX@Vtf@}|5&%z3@YH?r!`-xKMf2Pj+Q-E*-QUw5< z_VD5O$#tA-3Kj9PP}Qr`1?pGBtwK}JFs*}UOo9^AcdE=Zd)INlHrOOgzM}YD(!@ zA0@f$lgac}v1LPk!|_gC+L7#nB0~(G8nVYVEU-o|guE+bW=hW5q#i21t+{!2&&cQZG8tAba8PJQ zqHTjcN+QISxmb$=TuQWWLfBfH=TikJ(6r&S+c2*OsP-HES~W?;;H9@%LLmSKN=`nu7_Z{yzBRexjM{G`(H z=9+Dz?s2v{8!!5zCj3_&P`FyEx{Gzu^mMTAw{lC$GdKdj9O8Dr zCNKZgxeTkE@1%71@4G%a0=O)zWnNb(Hby1R)Q{@(CHn~6=y$NltBW`M3+#h%mn9c! z?hP*y-)ip($pq0}g?=}b z5BiIv2qLaOtS0Dd${H40)xPXiyfCyZ%E^#1$|&Zz~`|Md2F0voV5Gz)}O-g`U+-^KQtDj|#f^ZRj z=I5K|oGpFyI_W*R78);+$kOE(@z*x*G$xsJBEXOb{ZMHS_kqzq#gKLG)R2?toaOV= zVjRn)%^@M@Niof#EgCYkE>_xJL`bY3yZ4)#P#sxgazUNN0@9wIvrji%76)xl#T&Zc z31fGnDGpyp;s6WXmoM{s!;rSS#aL(Tt4sI9z6p#L z8wIRzggJ`q(!rns2R0b^aQX`@mGZp6Ui`ut=5T#UX-pn(<7Q~yde0R6j6b!-o>Gl_ z@36=KzzrIjhIeM??JvE31ZE1B7AFsU7%lu}g+G+V{@E~2G+$AqsXO{#lI+*Jr*95% z{r5I|1nORtvT!ux82423+o&|=7{UxSCe?f_=gR8=P@<*a{Whze-YM+!Jm|LpDb2b| zgq_EF#qgZhA3DGOp(t~{gOW+28ibeces`2 z2d9gn0q*p`bkvw7JU|OL|2dtPukXL1BATgl&=K-JsCPFRs>X~kglr5tLT_yEFYOA}qG0}p zfM`vpMbsN6@_J=Do$C}~9t$>LsEku6s+Kk20-euvxPU%n_#FVhMY(0O=;hldt*w8O z>x~sn)hc120)KHVt|F z*~55IF?T&x=u-az#A_!)@ajYcwqPmm`pTy;J~E`JM=U1JxTjXj71QqZ-<=*&J0mmA zYj)2`M?&>4=NChfao;cyX*)g{dkGZ{1?#gZ&9O{{8f-QrqFK{J9)9nAZFrGiz9|Nb z)IJovmjjtpZNDplBiBy_^i<1Xz79je>(S_l)I|w#kF@?u+F46=R|G_G$;0BirWN_d zBs!2_2UD#A5H0_U>b>6^JGiA;<3jh2sE9G&DFynIZMRS&XvhQor!Sq_njs8oF z-1Cs9;&n7$8`#aq`TfkXJ1&0ZFeG~pQG3(%)cb45$rMW@I{M zEq)x9u*TnmA`on$!?zXV{4QeEaTTe}vS4mD0!+1k0a7KU>*t!LHbb1oc4~>`4F&1a zKB=OuTd*#v5nGCc+_w z0*_gFUl1!Y3B*!Xxj>d$MRY(;I$nLRi%F}Dk zl;(%Hr;iggC?t8x^jC0)@4=^N|IdFRc23ZBNv>CRLIv3(GuE24&fi_7g6^kK?oynD z((l`zJ_%e*p;@%-k860*f`(;e!ZRrt%OoqcUrK)Z4T22&$T+7G|Ne?1>$2qy+GQ6$ zo0V>U)W>m*`oGiu3VKc`eUyKo%443lepi;3h7IUmH*{UX1DLdVr4( zJ}rIw!}3`pF73C?QT~L+W!US<%bi2vIcaO3myMezjdbh(6#V@j7#7y)e%GgnpR+g# ziS!(Q%l^chUcr!7<$#Az_*ebZNIeO|gQM*;SH8Y49qu{6lExAFu1(9$56-NhvOa6- zK&9bi4%j!jqFymK0zEQdSgbS?} zd+4Hs9Tx3G@v=H*LQN^rcco|Ko}&oqTr?gGMTynym$TG_zp1H#<;$QozkwJ0hY`MH zfs(B8m0XgY!r5)V>J7`6=h#cU4Yg+97>#)91`Iqpw)sUniFl`x@5Qcz4_i#AJf|m; zx|q+OVd4gE%VGJrhTurHj|GW798hQ1den3O(VkQLH60P(EB?N4^;)fVTok2)uL)_H zu<%ol6&Ot|r~W?~Y^Bm^y}C}{tpkbHjQam3h2qO{katR$-d+i#9>pUZ&$V4%QTt^HT;m|JxKQwnAAT1dxG3yR~b+EJ~lIX)k+dm4hsgvC;2jo6G7Z-CbM|yswxOpfPOh>B~KQw1lZn zvUYf`GqM+v-cx^chmCH@6b?((S{{kw`iM8mPK3a?(!x%Bs{y6E@ zUySnh;Q0FI#7lAzQ@Jd#zD)(xpW&U{b=-{caDQ+w*jVnU@4$Boo}b?BeQe`W79|qC zY&V!&nE4&YsB^(ZySa~p23pU}{k7s!yK5FkSx)xDwARMEgy+5bw#-vHh)&K1unlHm zT`ON0oZ*HOEfG_ZT?FIHQ-8;7^PpUhD_Qio`;iQ_fxU~7RnBlB3wa)QjuJdxGqxz^ zqU)(?-`pUiP!EhkSVTvVaQ|TOUCgiFMS-_?2F@4l?}&kVDVJMeLw9~qScF2zO9M$v zu7Zw-fBzzgUA5-vw34KdYzk||`9pj>06Izmcw*t6ku_N~#3{}|MXjS>^CVe2TPMFD z>1;miT3Wqh05^`%J2)Iu>#1HtE zlKUL;B>drL?P5V468}y|0}8%m`3ChFjrhsPG={#oHQ}@MIU(_55pkG>|_=#p|$4?JQ;b9NPME zgtSgWifMO3{?9oi0~Y{Dk-NWKFhDX#43JevghF4AG>s*xd0TRp+7}+aDlGIgo- zi)MFNq?AV+vgp8nTXA^oVv;_+F0#I_Wq@ zmtC1adlJC5k}z6Ct8KBu?0b0d;33wuQ;xIGJezSUhVn$tvgEX>^RE1EHa!4S59C1o zjm9_(X4J5WZ?Fa$3RD3Ie@J)mz^Cf%aFX|BItHwkgP2)TU$Ik_l4!=Ef9(hr#bV@U zH1B=!jg@$mKS`aJta;}F9ejKOy8Mp5u!15j_~RuiAh3-W9Yt|o48R{N#BWbU zwlD7Qo_VIs;yHU5R2k1T__a8tf&ja6?jKY501rd#-22g-Az5Hv=%jdI<4qF^oE`vT z$b@whplITd{8_j@@eXOhh`?v}kRPkQdso+q@5x?{rM-8SUs%%=>qtF$usGKL-u6*K za{>kUn4*}@Nep!Kx$K|;TmT&Au~^fD5+YB>@^SJHQ?NUEIPb#3(gC{9S&?U*@2HB% z*QSLwwWsSvg6pygm8+(4GGMuiEcDxfrZymg6@7H>zTkrCOtpW0JQvnLJ*`PPhj)BZ zL^t{RM@*G&DJjr}4gA0a=0`!mB0BKl4FJS?#Kh|G4wAXJ{~9arr%ULUOSoMKSP$~V zq;BzuKWcv0e`VbI=c;Q26NT1?7Q2xshN?3tu4)xnjsl>}U*;rty7Gu-QI=a9fmgc~ zDLY)BcCb3X5l?7lIWT>Z_^{PSTYF*=tkc--3>_Eh!soMnR!+Jg`4-UIBLyrFHS^N@ zB4DY<%4kP4Bs5>DP;e(wN>O1A6zmWK;W_|ZQQ`wlmm<6Sy;u$YEU|XSzndc027&7r zT|*gAx(|=@_@C5if}&?}ly-k56d1eAuh11<6s# z^SWMgt|TPE@z1rwD10|(dv&GblPEMZv7TQ z_pwSMa)2k|#(K@WI-nqSR&m;pKr|U|?x8brWldyizW7U<%KK^Lf|U5&b8_AjNq%hp zeEf_)2UIw;if>#{kKSFPQShcV_ap!WeKYchi`u)eAO7HX&mfnfIS)%V5AYBr|mxvzH*Tp$m`X!-yrDA z!7a0gv=X3vjsu8G0v$v#(?m!r$h>v20WjZ7Z0f|rZ_cs)=IFR6HDfsNi9AP->ukvA zJkw+zjKaNWsXpTW0UbEbmHfqWsRmlEKm)SDz~D!6Q0iYw14GtHr6w6qy(buO$yKdA zUhZG$*P9`3H)EWF3W)5G4Q3fSslPtTQZy@z5HTyXco4Az08MI?4(%tPgSF#OQpR$D zy;7gcn;B7U^%QWo6gTs{|5y$i_-5%31$Xcplq(w|nK%^ZHswIaCpIAFXAWv=ERYQF z#L*puVFKqyrG}%P-+xf11OdlJRDg{z0C?PvI$8k0xJ%xnyM!hg@O4ZO$&kYYFh8Qe zPmvWA=DFSSQQY6oZFmgTKj%EcvdMrLzy;v(Wjn2+Nanb|SRry=v>3MAzqI)IU?!hi zRQWRDbqh#SbIK3{UPm2J9f%C95T`d|LbbzxrcANZy?C)$z-Y$u!^D4;ay4C}_XDyn zaYGNMaQW8LRo)$#D~JBbi33d00B4IH@fbji9^CQzKF)$z=|vIE(xXpd@Y2$jx1MnFi+H)g2!RR6_CqWByv$pBxY=rs zK!B}zl8AZDN2*<9VvH6@L*P12ZO=4&hb&%pJ&XTQ>30|%h@>ptV!fZ;3NKqbS_@D9 zh-5)x0<~bk2L!O-T|p8I4Cc3=GdlSQbEPz|QjT06_2rQcT#SY)z~-;D1;J!iXERaW zE^3WcFA!|k=~~B5F=WU5EZYF%ucK=*-zUEiV>1BOYDSNL;pLfuE?GYoWbA>tHLQp3 z{QQV~{9#XR2b7@wH2|ya>RafDA+Rh^vSHYoOOxD`%8i-TQ>_mzG^JH}Fq8fu-Xe$w zoUgdlkh%JXSY%XTU;o(!jvJ-+rpFn8XXr3#-D>rGIvgmyUP4n-e`NMMC%O1QTLn!& zONB9Imlw;{UcGsM2y&w!027e9k93=x<`#pNehpdHp>LClh>mg2#TLO2z?Cy~2j52q zuI&x%^y^y~Lm(rhKv03Eh=#=BU9;Z~ju$l}!vs0zF2P5yVKHZYz!b+?*?G~-j9xbf zL0iaDsojnkp^GoS$D2&_xk3uw>~FC=suSg;=0RSIkw+OR?_}}5m+$ioZ!LDh6!)Ga!-#PYt;+G7X{7>Nj>F6n*YRRFYOdX08(%$ z-G?10!Xq57XXJ`3v?gG;6UOd&gFK|}NN{Cr56GZgrh<~*FB%vS`{L*}cDr6+D9m?d z<{N4+@>WO{CO5B9{4qjB@4Cw6MSfb6@fws5+l+S7g{&KudS%=xWn7Trfrrd9i5xI3 zMbdA%08?aD%_N8~U1ExI-^Y1=QG^u5^CvcMP;`B1mgf~k1z-zVKQZ;7fE&;eK3V%d zLs-1ev5V)gb%(ps-hRGap*p11fm)HBL(Vo-AD>-f^(t?EdQDv#XK3|Zl!gm+aphT- zjnlD%68Fhfl4^r1QJ{~1K2|IGgHRew-ZV!!EE5Dqlwx>}{$b!l!)2GrddIhs{jTe(OwoeJRadkLg_Z#>vGbKEJ{;8&qx!=wSdrC~gqJ z8Vc7(FnqrefH5BOM$zGUq9+XJjk{rgR1{-fddg;1(62?S8cxkNa#AhroKEkgPrrgB zz9S25iZ@yjmEBOu<>&K0i~Mt+c*VK>JQ|n{D9X$sH}MI4Kw{wD4(0@&J48zF8vi8; z9iiETSWxkXO3X++{*Y<1=CY5lJ?=7{S1$tKoX9cf2sIUXljspq5$)o{W5rjpGC1WX z9XS_S9X}>*2B?A&gq@J4KB-rp2R$~=TI$z)o*T4!_ejO;HM+!aSz7LF;l5%!YH*Hj zuN}`?{Br!BhP{1D%!lO?JP1 zz85;dB!~lo@>ekT!)HM)Tom{hvt> zCve4y;?q6oiyYamJEDxjXAh**Jm18PxwpB+dt45jN%Q6RmEV(Ye{2+*oE*wo+EaIu zk-r+{`+P3Ol=!+za)%AQL&WeYj{0%^D)0`);-@{3Jao(4ezzH6HBY9CSIEZry99t| zw1E)NIsM;IvxZ&Y;^$i41ABAZZPJH%DZ!sf^-LChdx@2PWMmlb9B>}i`ZbGx@wgoF zPap0sXz{8iO1bIFI#2!iL=@j~^*Oo`3kaehqvoBU;fu94TRuaF0iO<53W(w`s2hab zmn4AW(|sK4{T945FD^qxoL3^41Y;Mm!~5?HLuzfxzWWcKB<_%Vr$vhIG2#I#*4EGb z%g6$z%*#>oLdWNfYztlJ9BLw;hiq>AD2T*zkC-TV2-7+g`Hb1y%UnG zK!6HB4F9|89ozWUlVxer!#;}xEXe&S2xQ7Zr3z3!5M+z^OetqAw3tTz@Zp;6SpGC^BtAYVbx5!u*cI$E}x`XAcNwqH4y zF{_wD25zFXWaGi=y7PrqIvU{b@lnXFSQ+7M#nX00KniBn4DCdpcz>m+M0SR^*xv0zZ)j_R(1o7PQ7T8r*u_vnG0Cc?3jEstj>m9T>6>!a`Emz7Qj#Lb( zZ8Mrm-ya(0%#|9}AVh`qANk7w46++U5|T{;z~^Q`%ewD)J+~$4!ElK>du{+}W96O@ z9t~QqS4E-Q7|NoWghnk1=jS^txIIo8EHRA)^d->vjAR<$P6zqZ2K3=Tb1DN?<+%g* zl`G+<&=^;{5EMz=-WUzu(P_Rdk@R8XwfM3cM7?mc+$^CQni(+}x<#sUzoG6Fhfna> zcJ51-&XHXRaAZn=4^*+kcQiNwV-qSsA*{l1ld{>ivsux-%Ixq4S86w5tBgO-E9;e~ z*Zvy=$(3SCc9=2BL+IJQu_Ma*1RDm2mE82 zhyPK6O$TLBC_R-8}# z^Xlr8C%-3ud1{UUf7<7ieV3JE-_e`b(t6I__MfDGEbD~}qDO@BF#MXuQADPXC@^)Q zV!NAp^jT)$RdxSYbBa-XbF+_j5uC2C&JW;+JCc$%>#ofqZ$w+L&8@QxGa8f=>-(yvtk8 zyAH%Mho66x(N~{i0LQRCpkwR`1R#i3ZjyoD(++)wPBp7lqs6nsXfIq-F<166HG=9` zK8Du_9{Y3Rg9LwhuHh)ZK_=*&#P*vAh=xqmtJ7=@$(?6EmYY@R1skfyvnJER6KU?` z`F5)@L8f6hfdY1myMhngOSh%vs#3Tg(k^=fYQFWp-iC;ehmNc2$Wa3D7X#!(0OAtK zr`93?1okk2w8DA%`DKe{bO#}9dZS)~f*!imd0likq5yT=P^L2Q*M)>_+SzFq5|<3C zQ(CIGOfWm!2qWNmuf*HuAPjxJEg@39@abuZ=Q#IYXPTkKA}{U~X02gU51zo!(=!`F zOuDN!sNJsz>Nad9O-y*y78H(Bgk=Nz?D@_up3Q!XO7mic-|v=Ed?v)eh2Wpu+AAcP zP6=)QvvQ6<;SQXNrue)>J!I!;Y4Iam!Nno)u~RRJY{@r0MPiM=UscVix0@{g2dO-M zq3}+tCq63b4mj_xxw$#V*fiyj6a)*~W!!5Js{x=IzTRw_N@$AkR80JE9q{C4zBVM+7ZnN3{J_XGr>tH zr-zCHAmPMLKE-^`mkFFdtrS=gH&&R^iD1GNU`%ki{)=KTQJ8lNqiAVBo4)%Muapm! zKs_8uLY_yS{RTe|uW1 ziB8d_69~-ETrT@t^er9GUZ_?`2(m1B3FyDyzV?IxGJ124e~_2K%h?*wuJv=X4DFcDWkRV0?D?`T}=hbKmxmTjN> z={O_p+OxWnFQOCqqC@&j@J{%HGX`_}J2Vtf_h<8^=W13wUakqTRC+%vJv7%G^2{WT zM@l@6k>DRANWfq6qI2d$r~?&|pc=FG0c%H8U)*<%oG}vn3Ra2_RCwpp@1ezCf%FC% zEyGQ!&n(m4Fa?pkWr!`ftdDKt9#Og zf4xUcGG(8#hgSa+i^B(9k1;H9{=RYwNi;-T<35gH4vb`flYMnX8z{vU?!J1}0@gnX zEyaW!GvmWiX0W_ZP)ngj5amKbtwwZwb;*ukU7WVkD`Cer2DBL?w>T6q)Ik!oP6rZ6 zG+d)|#bHP@KYr9(V&TDaf&)mj@mSjlOk5<^K8>nv!RZ_q_W07;5k=G_biS3&l;B4D z%l@*|o5GEFr}gaQteH9`563A`+IujRVaw1Q`uU}mv#zA*o8Z0*4-++{ zD+kpcHVPu+mZ);}H23u^EfQtv63`A%D1F}N`*0`ycBScC!@%>jc4sAg*fW1NH957n z;}sO(>pC0^I&!Efh9-bTK@Z%aVJw?n_!o}E6;_zm@A)Q3jN`i!U_T48$1AB^)Oznr z&vIy;gZkmJV1@}R=DB~FXtSh5)jj4@027G5Xh2tEzg#fo>Vr#7F;KXtZ)qfthF@fZB0tqQ0c;xSt@CyQIDVI~ zUb-`kzA$=w%CCrrITyc@yq;4uJB8n|U`+t?;RMyzp^X}Xd0g!hfXA@f7Yn`NlJ$29 zfA{-aSNiw4qUlaE77Sg9Z|~QGv2|UVoEA&e`C2Y_^$#s;zH`r5Z)yrr_$G1EB&tMn zPR^jZu);{61GR5h3;ZdWXQ)AxG`Yhx0L~<6I4!i<%?`ka+fOmTEfClwHWjswFA!)L zrrJ7D9F8p*%~k=`S{W6aRG%Nr*A+a{u9J+yqzahoAK7}FNh>8aXl;fovUZgN1dog4 z{Gfh0ltzIXGoZz4yZD2{_!^lw9_-IPh%-eCZvd^Ong_~^_ymi z>^b;)rp^Tn3puET076kKfsN`MM7-`QV$;XsoC8lAeY24sN2&{2kG;{0Qm38dv-HuGG&eCFJ)?!!*u9$$|Fo54fgjNo$aB$$R} zPdDgu|KOb( zroXuDf1YmZ@1{CqaQkS|pxS~{Yu2iCh`!-Mz#Xjp?+wqgxuJx~h7c9QZ48L%6dGh1 z3$olp&@tq2A!+%>4pj+gD#4(mZoy|;)kJ&~mm^I6rE`2*jcp_@WU!QI`n-LSM^EW@2-p>5xXb=w|{c2 zqN8T(GbPPSx@G|t099jbU3Sx_@bzb_+e_rl#)e_~#f|05!fx8(x{a#zX|XKl#)vN- zO`y$Yf6?A_Ro>Fk#NCn|9X3kMbmWP}FSyD<^aH2_+UlWd780rTyQ zNBtY?Lk~!Mztb=g&kW#9|C3D75xe(PTOz#zG9g|SKOqSJ1)qR-h4z?T3ZSeMV0#rE z-#T=OuV_?`AL2|bp^5|Ynk0m^$;m-Xk^gBrABaQP`Gqv4&u7jT0hS?&!i`DAE&BZx zpRi~0SihF7R=qtu&Do&=`NpFBwyP0c2a^f49iqtMXSEjz{Jbt^X zT+;p;)aM?WmQgLF{K7$or$P9b^+Hr2DqR^s8s-(%+djpX?2{@f#rw%>*U8q!gu_1* zVLzmzqO#|2?D`n>A+(Ku#&K5@S3fk-a6a1ktF$P2yCLi6S1*BKqYOZ(VZ=h1{4T+t zPP`lU51lLRdbwAulTq+Yba>pP{S_@;{E% zhLT6>b<#MDus-Nq0smALFc)K1^rsG7P8q9!ILG5XMRSny=sMwIIi2#F?$WH81hzUg zYMZmFqZn#6zJu8ZDTrs9{ey^r^~x@h2AAwqL}`0MudxrO4FYz}_n@Zz@f?nCVm0d< zt!anmUE^#j#Cmy@%C{2sGOk$hVK-~F{a;~#2Qp6!bH!&cylpEEki|B}94`SzzuvF% zy6?8UT3h^z)j#0vzE}aT9igTvb8hwfTRZu$8fax+V4}saL3vFZi|p_C#K&oTXo*5B%%(S z--+=Q=!sFfgfK4r4r`;guD_T*5wCs_y3^Zy(Xts5#Dbgj1`H~Q5w_qj8h(LJ8dxb0 zl*Y{WJ4Vw5k9CprfCXY&xf0hCf2F(M9~O06@ufv&**pJ!rRAlh>q&iwq+uU=?x?}2 z(W|FA!VPO9=$>bV0qX=Xoe%<;2MVOB(}8OSoH*_DEO(uW%sN{iN)1Xhe4YI~t>#>O zrmN&Vi5uL~{q)s>KiQ+tHB!!tM>E+@S^$XNX0OM1i?CKln-vU4zbjZfPzV0uZ@i?t zH@S%2aQs*gy!91vZm%fLCWgW+Uc*S~;rXs$AOzN>WYJ_rG?Xb2g0FAJ#-j-ASPV^X z5S8~Q5?g^`N3A1lpG-;aqYNb67Om(-1$=QEYdyk0|61Y@;TCVTH%W3}#`eBf^=iQ; zX{_KaPdmhB1xP`^F_zoJAox`=njir+Ae#y_4+4wqwImo0XmNq9DMfxP?FEJ7s=rz! z_|1!ry%;?yU7@G&7tC_$B_hhdnwLXE*Y)IG>xgHALRGsg z#CZ~oLHCugqxI{ympCEd^TbwcSw!|LFr&UL0+R8NqxdWEhPQTU&;JhvG2s|fm7_tJDlVYf~1V9z`&FOv{P>^3OU@DJkrVMoPd&t3R4p6H3X;dmUxv^hcM z$l1V5wetP%m+d(s8D-fYG*8E;SjzcLoR&2%GtiUV;>Of+eF@#Dod*NqhL(3WBL(LCvQAgUBm@f53$QC_u#C$VYmBUTY7p1g;YgH*CL3eey z-!172)U^14%GLBMttcJ=&SjVSp6zDYjbU|Q5jX3QcgueJ!V{J1_Z)MY*CKmRvDNt`MpWm}W*Y+{rh zVU~5N!Nj~-ToW#1hC8fog%|d`hO)RZ!C`!%UusMFGik=qX0K(n(lhHSJ?DY66`b$# zEJkE*bFA5o8|l9dUE{3cW)u@FR_s1J{(bY|aFuqt>-$noIDPQ%NM*B{qM1aav(Gb> z=x3Iz8J?DZ=bl%K-)vcm96lsPlHR;t3N6cAQNiu8uJ$G`omnHdpVrowuJNVX{A>Yx zCt|3l5)I!aKz6B4B7NGy?6V%<-c&bdyXn>akw~EHUpQ5>R`E~KB5c^q{+;O_|6J*w zNIA}icxD&#rquwVUwNeH%zjQq6zDoC9=32m-m~e&VS44GSkq1cPJ~e?+7Qq7@ID8a zy!Y3-YP$M!JLEm;O*opznbQ|mh|iRz&jDWi4(d5FH1hjo6T*HRRkJJ9=T)L01l=Pqj`>?t zsylmShMw$Erpx6_;%BXA1cq<%(Bi{P4il7dR1Sjz_^}HbFCBRt{UjmA!c#2x!x#k? zm)D|%P@fWs2xTsPMcKKAJ-Wl`K0eY9^hQ6>p4UR@#fQMZg|q|T9m_6PO+6zrsUG_s zeh(Hs_L0-PjmrLr?&n#rz%ye{E^Fd87{YRVln-Ou%Ch*)xdlll{H_XohGTDjRb_A^ zpN+g*KMGljzq@ei0H+&u={eEL3W<#5POlSc)HN%7Hlb$x=3?v~nV-*00^=fi{F5qf z{CyC0i)>T)dZ<}_S$J77hmJj&U(q3=>zKwWj877pO&d#l2i%ctgtcS51Z>F6-@&&I zgN_b^=lzSb(R*+^JOD-K40x(AkbpRGx~DOwJc?cnz(YROfMk&e;Is8ND{h=U?= zB}DU0;@d0RWt&dQyv`I+CLZ%*CPm1v-aU)tpfFe52te{ho4KP8xvS1S)fec9B+3{{ zTRSFAsP-~sRuRI+7nU}aTfSAskm|?Nv}W3Jb9vVPG8~nddD)*v!z6TM2MTU6*Yl^4 zVBZ|yGBV#zy&x~P6&u^2@(U9Tn=@urBgZwl;V7gG7ElM4{{B4daf~L@3Hacatw1Jf zOQIOg>BbRE5$4V5jP2k4hQ)!o4zD#ZjkXr$cOlp~dOj&_5BGJ=A5x4BVCeXXKP2?Kl>hyyLEoPnwpCSN`ept9vFiG)K{hV>p7&gv)f)TMtC|@ z!NT3aU9FP|FiJvzPpFv6WkNgO+-z%rB#+fpFLJ5J?buLqPLDe$wU~7IDcLlUU*mYD9J5xX}NVj6(3mo6~yW#5-b`>V!TrFZ0+lf4-{y zxTg7|6!+@W+Y(uipAQLoN$LF?oahvMdHCmmHkv!;bk22alfS}|)t?2txuB_T`!&fD zBQahOf`h7aMPGs6EJv-Q@rl-~X;Bw~usRtYP5HA=)LkA2c6N<_2T7300p>EDp;xlS z1E8=sx)ufB@UiTP^H}H65TBPDH(Iv(>~+U-UpWduo)km7pXiy0qj%}PZw5kHD_^~7 z(drKhRK0Pl8e+hNx}Cg!=a?X#zXCfn#9br`;-^-?S6Qny5^!eqanEQO}qX) zg?dR^&nRUwF+~SN82a>YpuKIMZSS|{>rL}U4g$YjcwqER+xv54!QLe;u<~2gO_Ba@ zR*-~W-hp@WoBM-^lE>C;el#Ngm!+}sHX|8BMv7_Ek=)QaNCOI{PvMJK8#$el+tuV# z6-A*ci^|@G2_mio2pU5B#kIyS6MN26I&E(c9wvgs4s;=GlEczU3c0GUH^R!%CK7)z z980uF5rRHL7qw^AB7zDAB(&39rz{mqdYz_-DfZ&o8ea>K(*C1x-15(H|GCZ?L>x<~ zUK41~ckxIv$*a~o)jM}x*e*Gn4m@&ue_HA5Cmbi@m1nvaRml>4F)yyK#1ijkDIGL`WjK51or!Sv+0-#8#AqVmx)?1k z`z;5fu<8v9f*rpj34%?^-ue5LP*(4CP(ne%pC}2fonMT(IkVZo=v}nKYU;(B^;WQY zoJ0R`J7*ktQmu*xy>T~RrSF*{$S4$8zoF%->sZir?t2q2@UFYNPtHYeVE*jgK&#eq z&YI_OvsWBteS*Ak!x*Hh+9VRS05=A0^q7}{z?-WGkHO(hxBN!tJ9e8Zv~@#LS8w0r zSIIwB(RVc{3E}g1O9EL}%ATX>GNwsRKi{v#Kci}`G#FY6Z~WQvY?Q-PX{_ppiw+p1 zJg=1z=>HL(kA~ow{`y{r>Bwq68yrABC^=s$$)j2>Bt#jMxiWt?vVOQx6C(wX)Tb96 zxY+d`q4p#OgAkds1?91DuJ|73rP$JeQcfoU9RcGUj|4$Yt#cYL?tPPOn|-I*qoFUJ z{II|rii7;FTCSl+p_IGm^0m}*B5I8wPK^(La@*0uAyewr;{>8iuCHNkFA65ruV7{P4qNf5gyH#;B&G6G%LBF9ogCj3UpzB)ni1yEiEX%<3 zoa^zor6F-Px@uL~dX72Q^*S`DB84UB!mm<0oHFE}vm`@8D|RV{$=WPLFoG{pd$4}% z?xkyDA7A}Cs<`ZqWU>=<{SHafc`Y}w|4?#Y12-Kt;M}~$+c-?QaPM!am|wZdr}{=y zGCJbBNdDeP6MJ22qpWB_WVNhazG&3zkg>Is7IKF7Fgm&0k$fYXkoVzD66#osKN-wV6+K%ZB6!0bq#jXMpZJWxAX9#-}YW$p2O!KGZsET=O_#%vhy5(pB?I zDkxFx8v!a5LI;DtgTNPQu>w8S7|Gi4dO1J_cP)~e_riDEbM;Q`?9T|4-fIVODFdK+ zKM%~`E6y(%z&6eF&SgfpnVtXe3#5K7m7Z@5m!Gaxgid`~3W<}J9zvr_@wBMEw3D6u zcq<>^`1Z5lbNCC{z{G#D)^qaVHM6s`;~f*XowaAEk|ZPniYQSX zW~Ff@)4JM)fAScyzaUt&cwS+!kn36~7^|jYjtCU}vfyQOMQ5#5?_FuA(tek!>>vUq6XP3C^d0)^k2W`-!_D5ylf8sF*QIn$5j+oL5D07 z#kik^15G0NH3bAA^vvPs9x0-Pxhi01J$cVtR-tH1^DvD&j1PNz!#daYRdm-Ys@ynm z9XgW_Im+Z}R>TN1@GC6|TI4g=R){MZlPd%VR7gl9$?tI~I86&i`=Ou>4dxFrbFb3& z2B;$6{Pf(*Jk@=%TIitC?Zx>I@{00yOU<=uUbXoS`%xz6srVl%?!h;LaV3(WxbI&D zN+>@uc?{;M}=6>qtGc8BT5#^x%lK098T)**u zIoLWx1I6RNG(+D~7{xHSs)Kq|i+uN3jh--+o@-#C<`c;sa-eyeI!3GLQzLF(YG>Z_ zHwZpCSGp!k9_pME53~*>&TT;L#m$pZzC38Xdy5F|v;Ivx^4c$!q1-<9lB^9U#;3GxH01>u5U9))iyH;##Us4AP|A6 z1PbAA6_EG$rsx}+uSI6Ic2y-!c-E$_J;!~5;~giR0(s9%W?EzkVj-VdFiee>`#Tsy zuc9fuxtDh!HunvUN}&qGZeAryW8(fM)mzMW?o&RPafdXtG35RW5_tuVOmr+^@Zz59 z!inqrGzp`l(~-NUsjSNDs|lZ|IGWBJjdVj>8hwqI9dXPc5IEa*f+Dc3BFAg~kJC*s zCw5P(;Lq$|`CFNq%o^;q5#R7s`U|#^+!RsF!){ zl0;O0#-`D5_(K!;k3=*LqG2KWz2rMW*LaG3>a@TLM2w3YQ53sVv zO6}5~kG>8@D%&LYovL_jd9J%wifvGH*z4o0y1^I%CF|;}^(9X)QEO>ofjzoE291(# z+%t1%ckiz4IIq7;%3sf@R<`AoZZ)@0u%Ri_Wmnr;$kRCl&#52A5DVXHS5`hxacU{n~$V zM2S|?UXLENIyt{tVhV5!6z(iAsDeX}^N!FhaJUCCTi@W`Bu^`56Eg99NqvU2HSUCU z^_}=*6qFSv9MPt&8V^=(J*AucyNWR#C}#{>WPE(MtqK{JxJuTO=g$)xgCpQzG2EZV zCfM{(oL$b=L$-hN?=__K_I)2;8vLD0?5k<5twrN}nq_JlbzhVeQ-Rr|U$Td6I9#hZ4BYHMZ}v8nMdQtK+t|u%S$J z2!Cvo!;2rIo_)47HTJ=48-N;`YPCEch5y_s`KMzr@B;9v6mdm67tMWwl6z_rob%D$6YE{)kceo0 zxdBy9Qdb38O5_=|?Y-VmXlw3G9CV>}B5<7TmoB_==-l(nwnZ%UJ$YGmhy4&L#f;sO z;;3IA2M0kRwY|qO@dIklct|fo9`(fhHYfk9JooYA@wM%>Vx>Rfu;yBi z#1pESkjc~waAx8~(Rc!ZkSI~sB`HPd(;I7oAssyz`3?nC!@?9@KU9gvZx-hwk?5tj zg~&izJs{dAWxrL5Z%bzWipJaYQi_&Z(j^-Y{L2tL^%1Sjsmd|tqDd8v{>yXZLB&j` z>DDStz_c6{Qk?qyG$(+f?q^~?1i~)r$Va~d5(W>-TAj}yR}~Diy4#hN-pPty~|$1zShKn3ttoRTq841tt~FzcEPrkafMc0`N_4zlv%2!&!OG~ z<$NvH`1e6*{u|5DSZ+eIVUdEj^^PVK*pH?je|Z)r)GZvYvQszRwr&Tj+Ig&4%IQNx z1-s#)3Iw#Azv&5H*)N}(XMLoW3s=U%^0eI-!F!r4YeeSOa=UA>7jZecl{e4oT7TM_ ziT8MZcwHUt(T2h3PDMR!cl_-6$_6J{ zi?jP|@(Q_>!*;*#w?_$H(^~jl56;ne`BuY~36&|2#j(<`u~VR=@aY;WD~yo6?P@1amr^K8Nj|txos_?xZ zysIwCu=0wYnb>Vngmbr}1$Xd$>@zthAL{olSld5OX!JgGJsdi9JtVeZ{x9tT68@nr zKxE978NrV(=mkDJt?Mhv)A1_VKgs3F&hZYXeja6Y@Vl>!wj&*WEozo<8Sz<*C8XqeBb*fD)hr~i3>l` zS(fzq^yR5v%vhNHP#@_;*pCOi2&= zU|f>tyQ3F6z4hf>!~cHdJLNw^Tt4qV*!tyOeP{OT(R*4qt!@)|qaMk(AAiROSUcf= z-$4HN_T$s2UVI3zEf~6R2qaM(LCA@sJ5Lt9dBS*sH=cj;hV(K|9)oxTdxIyDcX%TC zkoT^i@do)NZ=9ofLmkckJ%a!LXZ-g+;?IAFzuy1&>ks0M_+|e7|Ki)zmv3KhzP;V~ z_IIme*n)8jhj6q5fyhZjP9)mAAwFA&RC@4++LM3(Y#oEuuH%uPQMj&f2#0V8hj4WD a7 diff --git a/go/cmd/init.go b/go/cmd/init.go deleted file mode 100644 index 97b27f8..0000000 --- a/go/cmd/init.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "errors" - "os" - "os/exec" - "path" - path2 "path" - "path/filepath" - "pikapika/pikapika/config" - "runtime" - "strings" -) - -// 根据不通系统初始化数据的保存路径 -func init() { - applicationDir, err := os.UserHomeDir() - if err != nil { - panic(err) - } - switch runtime.GOOS { - case "windows": - // applicationDir = path.Join(applicationDir, "AppData", "Roaming") - file, err := exec.LookPath(os.Args[0]) - if err != nil { - panic(err) - } - path, err := filepath.Abs(file) - if err != nil { - panic(err) - } - i := strings.LastIndex(path, "/") - if i < 0 { - i = strings.LastIndex(path, "\\") - } - if i < 0 { - panic(errors.New(" can't find \"/\" or \"\\\"")) - } - applicationDir = path2.Join(path[0:i+1], "data") - case "darwin": - applicationDir = path.Join(applicationDir, "Library", "Application Support", "pikapika") - case "linux": - applicationDir = path.Join(applicationDir, ".pikapika") - default: - panic(errors.New("not supported system")) - } - if _, err = os.Stat(applicationDir); err != nil { - if os.IsNotExist(err) { - err = os.MkdirAll(applicationDir, os.FileMode(0700)) - if err != nil { - panic(err) - } - } else { - panic(err) - } - } - config.InitApplication(applicationDir) -} diff --git a/go/cmd/main.go b/go/cmd/main.go deleted file mode 100644 index 8ac25b5..0000000 --- a/go/cmd/main.go +++ /dev/null @@ -1,70 +0,0 @@ -package main - -import ( - "fmt" - "github.com/go-flutter-desktop/go-flutter" - "github.com/pkg/errors" - "image" - _ "image/gif" - _ "image/jpeg" - _ "image/png" - "os" - "path/filepath" - "pikapika/pikapika/database/properties" - "strconv" - "strings" -) - -// vmArguments may be set by hover at compile-time -var vmArguments string - -func main() { - // DO NOT EDIT, add options in options.go - mainOptions := []flutter.Option{ - flutter.OptionVMArguments(strings.Split(vmArguments, ";")), - flutter.WindowIcon(iconProvider), - } - // 窗口初始化大小的处理 - widthStr, _ := properties.LoadProperty("window_width", "600") - heightStr, _ := properties.LoadProperty("window_height", "900") - width, _ := strconv.Atoi(widthStr) - height, _ := strconv.Atoi(heightStr) - if width <= 0 { - width = 600 - } - if height <= 0 { - height = 900 - } - var runOptions []flutter.Option - runOptions = append(runOptions, flutter.WindowInitialDimensions(width, height)) - fullScreen, _ := properties.LoadBoolProperty("full_screen", false) - if fullScreen { - runOptions = append(runOptions, flutter.WindowMode(flutter.WindowModeMaximize)) - } - // ------ - err := flutter.Run(append(append(runOptions, options...), mainOptions...)...) - if err != nil { - fmt.Println(err) - os.Exit(1) - } -} - -func iconProvider() ([]image.Image, error) { - execPath, err := os.Executable() - if err != nil { - return nil, errors.Wrap(err, "failed to resolve executable path") - } - execPath, err = filepath.EvalSymlinks(execPath) - if err != nil { - return nil, errors.Wrap(err, "failed to eval symlinks for executable path") - } - imgFile, err := os.Open(filepath.Join(filepath.Dir(execPath), "assets", "icon.png")) - if err != nil { - return nil, errors.Wrap(err, "failed to open assets/icon.png") - } - img, _, err := image.Decode(imgFile) - if err != nil { - return nil, errors.Wrap(err, "failed to decode image") - } - return []image.Image{img}, nil -} diff --git a/go/cmd/options.go b/go/cmd/options.go deleted file mode 100644 index 1c605ef..0000000 --- a/go/cmd/options.go +++ /dev/null @@ -1,88 +0,0 @@ -package main - -import ( - "errors" - "github.com/go-flutter-desktop/go-flutter" - "github.com/go-flutter-desktop/go-flutter/plugin" - "github.com/go-flutter-desktop/plugins/url_launcher" - "github.com/go-gl/glfw/v3.3/glfw" - "github.com/miguelpruivo/flutter_file_picker/go" - "pikapika/pikapika" - "pikapika/pikapika/database/properties" - "strconv" - "sync" -) - -var options = []flutter.Option{ - flutter.AddPlugin(&PikapikaPlugin{}), - flutter.AddPlugin(&file_picker.FilePickerPlugin{}), - flutter.AddPlugin(&url_launcher.UrlLauncherPlugin{}), -} - -var eventMutex = sync.Mutex{} -var eventSink *plugin.EventSink - -type EventHandler struct { -} - -func (s *EventHandler) OnListen(arguments interface{}, sink *plugin.EventSink) { - eventMutex.Lock() - defer eventMutex.Unlock() - eventSink = sink -} - -func (s *EventHandler) OnCancel(arguments interface{}) { - eventMutex.Lock() - defer eventMutex.Unlock() - eventSink = nil -} - -const channelName = "method" - -type PikapikaPlugin struct { -} - -func (p *PikapikaPlugin) InitPlugin(messenger plugin.BinaryMessenger) error { - - channel := plugin.NewMethodChannel(messenger, channelName, plugin.StandardMethodCodec{}) - - channel.HandleFunc("flatInvoke", func(arguments interface{}) (interface{}, error) { - if argumentsMap, ok := arguments.(map[interface{}]interface{}); ok { - if method, ok := argumentsMap["method"].(string); ok { - if params, ok := argumentsMap["params"].(string); ok { - return pikapika.FlatInvoke(method, params) - } - } - } - return nil, errors.New("params error") - }) - - exporting := plugin.NewEventChannel(messenger, "flatEvent", plugin.StandardMethodCodec{}) - exporting.Handle(&EventHandler{}) - - pikapika.EventNotify = func(message string) { - eventMutex.Lock() - defer eventMutex.Unlock() - sink := eventSink - if sink != nil { - sink.Success(message) - } - } - - return nil // no error -} - -func (p *PikapikaPlugin) InitPluginGLFW(window *glfw.Window) error { - window.SetSizeCallback(func(w *glfw.Window, width int, height int) { - go func() { - properties.SaveProperty("window_width", strconv.Itoa(width)) - properties.SaveProperty("window_height", strconv.Itoa(height)) - }() - }) - window.SetMaximizeCallback(func(w *glfw.Window, iconified bool) { - go func() { - properties.SaveProperty("full_screen", strconv.FormatBool(iconified)) - }() - }) - return nil -} diff --git a/go/go.mod b/go/go.mod deleted file mode 100644 index e2a7248..0000000 --- a/go/go.mod +++ /dev/null @@ -1,21 +0,0 @@ -module pikapika - -go 1.16 - -require ( - github.com/PuerkitoBio/goquery v1.7.1 - github.com/go-flutter-desktop/go-flutter v0.43.0 - github.com/go-flutter-desktop/plugins/url_launcher v0.1.2 - github.com/go-gl/glfw/v3.3/glfw v0.0.0-20201108214237-06ea97f0c265 - github.com/miguelpruivo/flutter_file_picker/go v0.0.0-20210622152105-9f0a811028a0 - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/niuhuan/pica-go v0.0.0-20220415110443-b5c4a97b0103 - github.com/pkg/errors v0.9.1 - golang.org/x/image v0.0.0-20190802002840-cff245a6509b - golang.org/x/mobile v0.0.0-20220414153400-ce6a79cf6a13 // indirect - golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect - golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7 // indirect - golang.org/x/text v0.3.7 // indirect - gorm.io/driver/sqlite v1.1.4 - gorm.io/gorm v1.21.12 -) diff --git a/go/go.sum b/go/go.sum deleted file mode 100644 index 9dbc2de..0000000 --- a/go/go.sum +++ /dev/null @@ -1,127 +0,0 @@ -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/PuerkitoBio/goquery v1.7.1 h1:oE+T06D+1T7LNrn91B4aERsRIeCLJ/oPSa6xB9FPnz4= -github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY= -github.com/Xuanwo/go-locale v1.0.0 h1:oqC32Kyiu2XZq+fxtwEg0mWiv9WyDhyHu+sT5cDkgME= -github.com/Xuanwo/go-locale v1.0.0/go.mod h1:kB9tcLfr4Sp+ByIE9SE7vbUkXkGQqel2XH3EHpL0haA= -github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE= -github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gen2brain/dlgs v0.0.0-20190708095831-3854608588f7 h1:qA8Mdjwrlv/r/aMqArqO0IMHUiy6ApdW4+8DtKr7PvA= -github.com/gen2brain/dlgs v0.0.0-20190708095831-3854608588f7/go.mod h1:/eFcjDXaU2THSOOqLxOPETIbHETnamk8FA/hMjhg/gU= -github.com/go-flutter-desktop/go-flutter v0.30.0/go.mod h1:NCryd/AqiRbYSd8pMzQldYkgH1tZIFGt2ToUghZcWGA= -github.com/go-flutter-desktop/go-flutter v0.43.0 h1:7tdUbGKmHwdsUnBfC/h7zAO3T67cAkKSCWi9ZDFg25A= -github.com/go-flutter-desktop/go-flutter v0.43.0/go.mod h1:GSCn6XOpB0cnYlK9/BdSwxi99t5YD1XEk0v4agI7SS4= -github.com/go-flutter-desktop/plugins/url_launcher v0.1.2 h1:oFiIJjotMQvF8rfKWVJrf+1/JgTXShEIsibkiXrQnUw= -github.com/go-flutter-desktop/plugins/url_launcher v0.1.2/go.mod h1:GYgRDaLDAJRYvaASQk8HEmI8YJurbZGW5VVDIMxwzBU= -github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw= -github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20201108214237-06ea97f0c265 h1:BcbKYUZo/TKPsiSh7LymK3p+TNAJJW3OfGO/21sBbiA= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20201108214237-06ea97f0c265/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20190915194858-d3ddacdb130f h1:TyqzGm2z1h3AGhjOoRYyeLcW4WlW81MDQkWa+rx/000= -github.com/gopherjs/gopherjs v0.0.0-20190915194858-d3ddacdb130f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= -github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ= -github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= -github.com/miguelpruivo/flutter_file_picker/go v0.0.0-20210622152105-9f0a811028a0 h1:hXl9AMW20Php3xWlWZr2Acw50tqeblLgtLfLoRCACmA= -github.com/miguelpruivo/flutter_file_picker/go v0.0.0-20210622152105-9f0a811028a0/go.mod h1:csuW+TFyYKtiUwNvcvhcpyX4quPI7Pvv0SUogdqCW4I= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/niuhuan/pica-go v0.0.0-20220224154849-76bf750f8c4d h1:f3V6V1Y+5j/AvhsIGA6aQ/K2Ez1AeYbuCG9uI4fGC6M= -github.com/niuhuan/pica-go v0.0.0-20220224154849-76bf750f8c4d/go.mod h1:r76zBgH9AYkv0ptyEVoPUIdt33sT0Ts7xgcg742OZtw= -github.com/niuhuan/pica-go v0.0.0-20220415110443-b5c4a97b0103 h1:Qc40/rqbtY0fXAlPEzlfBdQk7wIHTjcTmejmBvdKeco= -github.com/niuhuan/pica-go v0.0.0-20220415110443-b5c4a97b0103/go.mod h1:r76zBgH9AYkv0ptyEVoPUIdt33sT0Ts7xgcg742OZtw= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20220224134551-8a0a1e50732f h1:G/wQ/Mbs60nXhRM80J4DOzy7FEIZjNprzOneArSgOl0= -golang.org/x/mobile v0.0.0-20220224134551-8a0a1e50732f/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= -golang.org/x/mobile v0.0.0-20220307220422-55113b94f09c h1:9J0m/JcA5YXYbamDhF5I3T7cJnR7V75OCLnMCPb5gl4= -golang.org/x/mobile v0.0.0-20220307220422-55113b94f09c/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= -golang.org/x/mobile v0.0.0-20220325161704-447654d348e3 h1:ZDL7hDvJEQEcHVkoZawKmRUgbqn1pOIzb8EinBh5csU= -golang.org/x/mobile v0.0.0-20220325161704-447654d348e3/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= -golang.org/x/mobile v0.0.0-20220414153400-ce6a79cf6a13 h1:C4eR3yV9J3RkP8WKkcQzpcPyr/foOcUtxW72DvJEOlI= -golang.org/x/mobile v0.0.0-20220414153400-ce6a79cf6a13/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200802091954-4b90ce9b60b3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7 h1:BXxu8t6QN0G1uff4bzZzSkpsax8+ALqTGUtz08QrV00= -golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098 h1:YuekqPskqwCCPM79F1X5Dhv4ezTCj+Ki1oNwiafxkA0= -golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM= -gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw= -gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= -gorm.io/gorm v1.21.12 h1:3fQM0Eiz7jcJEhPggHEpoYnsGZqynMzverL77DV40RM= -gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= diff --git a/go/hover.yaml b/go/hover.yaml deleted file mode 100644 index 81c398e..0000000 --- a/go/hover.yaml +++ /dev/null @@ -1,9 +0,0 @@ -#application-name: "pikapi" # Uncomment to modify this value. -#executable-name: "pikapi" # Uncomment to modify this value. Only lowercase a-z, numbers, underscores and no spaces -#package-name: "pikapi" # Uncomment to modify this value. Only lowercase a-z, numbers and no underscores or spaces -organization-name: "niuhuan" -license: "" # MANDATORY: Fill in your SPDX license name: https://spdx.org/licenses -target: lib/main_desktop.dart -# opengl: "none" # Uncomment this line if you have trouble with your OpenGL driver (https://github.com/go-flutter-desktop/go-flutter/issues/272) -docker: false -engine-version: "" # change to a engine version commit diff --git a/go/mobile/lib/.keep b/go/mobile/lib/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/go/mobile/mobile.go b/go/mobile/mobile.go deleted file mode 100644 index e357c9f..0000000 --- a/go/mobile/mobile.go +++ /dev/null @@ -1,22 +0,0 @@ -package mobile - -import ( - "pikapika/pikapika" - "pikapika/pikapika/config" -) - -func InitApplication(application string) { - config.InitApplication(application) -} - -func FlatInvoke(method string, params string) (string, error) { - return pikapika.FlatInvoke(method, params) -} - -func EventNotify(notify EventNotifyHandler) { - pikapika.EventNotify = notify.OnNotify -} - -type EventNotifyHandler interface { - OnNotify(message string) -} diff --git a/go/packaging/darwin-bundle/{{.applicationName}} {{.version}}.app/Contents/Info.plist.tmpl b/go/packaging/darwin-bundle/{{.applicationName}} {{.version}}.app/Contents/Info.plist.tmpl deleted file mode 100644 index e49ccf2..0000000 --- a/go/packaging/darwin-bundle/{{.applicationName}} {{.version}}.app/Contents/Info.plist.tmpl +++ /dev/null @@ -1,38 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - {{.executableName}} - CFBundleGetInfoString - {{.description}} - CFBundleIconFile - icon.icns - NSHighResolutionCapable - - CFBundleIdentifier - {{.organizationName}}.{{.packageName}} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - {{.version}} - CFBundleName - {{.applicationName}} - CFBundlePackageType - APPL - CFBundleShortVersionString - {{.version}} - CFBundleSignature - {{.organizationName}}.{{.packageName}} - CFBundleVersion - {{.version}} - CSResourcesFileMapped - - NSHumanReadableCopyright - - NSPrincipalClass - NSApplication - - diff --git a/go/packaging/linux-appimage/AppRun.tmpl b/go/packaging/linux-appimage/AppRun.tmpl deleted file mode 100644 index 9272c3d..0000000 --- a/go/packaging/linux-appimage/AppRun.tmpl +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -cd "$(dirname "$0")" -exec ./build/{{.executableName}} diff --git a/go/packaging/linux-appimage/{{.packageName}}.desktop.tmpl b/go/packaging/linux-appimage/{{.packageName}}.desktop.tmpl deleted file mode 100644 index cd17a38..0000000 --- a/go/packaging/linux-appimage/{{.packageName}}.desktop.tmpl +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -Version=1.0 -Type=Application -Terminal=false -Categories= -Comment={{.description}} -Name={{.applicationName}} -Icon={{.iconPath}} -Exec={{.executablePath}} diff --git a/go/pikapika/client.go b/go/pikapika/client.go deleted file mode 100644 index 681520e..0000000 --- a/go/pikapika/client.go +++ /dev/null @@ -1,617 +0,0 @@ -// 透传Client的功能并增加缓存 - -package pikapika - -import ( - "encoding/base64" - "encoding/json" - "fmt" - source "github.com/niuhuan/pica-go" - "golang.org/x/net/proxy" - "net" - "net/http" - "net/url" - "pikapika/pikapika/database/comic_center" - "pikapika/pikapika/database/network_cache" - "pikapika/pikapika/database/properties" - "regexp" - "time" -) - -import ( - "context" - "strconv" - "strings" -) - -func InitClient() { - client.Timeout = time.Second * 60 - switchAddress, _ = properties.LoadIntProperty("switchAddress", 1) - imageSwitchAddress, _ = properties.LoadIntProperty("imageSwitchAddress", 1) - proxy, _ := properties.LoadProxy() - changeProxyUrl(proxy) -} - -var client = source.Client{} -var dialer = &net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, -} - -// SwitchAddress -var switchAddresses = map[int]string{ - 1: "172.67.7.24:443", - 2: "104.20.180.50:443", - 3: "172.67.208.169:443", -} - -var switchAddress = 1 -var switchAddressPattern, _ = regexp.Compile("^.+picacomic\\.com:\\d+$") - -func changeProxyUrl(urlStr string) bool { - if urlStr == "" { - client.Transport = &http.Transport{ - TLSHandshakeTimeout: time.Second * 10, - ExpectContinueTimeout: time.Second * 10, - ResponseHeaderTimeout: time.Second * 10, - IdleConnTimeout: time.Second * 10, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - if sAddr, ok := switchAddresses[switchAddress]; ok { - addr = sAddr - } - return dialer.DialContext(ctx, network, addr) - }, - } - imageHttpClient.Transport = &http.Transport{ - TLSHandshakeTimeout: time.Second * 10, - ExpectContinueTimeout: time.Second * 10, - ResponseHeaderTimeout: time.Second * 10, - IdleConnTimeout: time.Second * 10, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return dialer.DialContext(ctx, network, addr) - }, - } - return false - } - client.Transport = &http.Transport{ - TLSHandshakeTimeout: time.Second * 10, - ExpectContinueTimeout: time.Second * 10, - ResponseHeaderTimeout: time.Second * 10, - IdleConnTimeout: time.Second * 10, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - proxyUrl, err := url.Parse(urlStr) - if err != nil { - return nil, err - } - proxy, err := proxy.FromURL(proxyUrl, proxy.Direct) - if err != nil { - return nil, err - } - if sAddr, ok := switchAddresses[switchAddress]; ok { - addr = sAddr - } - return proxy.Dial(network, addr) - }, - } - imageHttpClient.Transport = &http.Transport{ - TLSHandshakeTimeout: time.Second * 10, - ExpectContinueTimeout: time.Second * 10, - ResponseHeaderTimeout: time.Second * 10, - IdleConnTimeout: time.Second * 10, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - proxyUrl, err := url.Parse(urlStr) - if err != nil { - return nil, err - } - proxy, err := proxy.FromURL(proxyUrl, proxy.Direct) - if err != nil { - return nil, err - } - return proxy.Dial(network, addr) - }, - } - return true -} - -func userProfile() (string, error) { - return serialize(client.UserProfile()) -} - -func punchIn() (string, error) { - return serialize(client.PunchIn()) -} - -func categories() (string, error) { - key := "CATEGORIES" - expire := time.Hour * 3 - cache := network_cache.LoadCache(key, expire) - if cache != "" { - return cache, nil - } - categories, err := client.Categories() - if err != nil { - return "", err - } - buff, _ := json.Marshal(&categories) - cache = string(buff) - network_cache.SaveCache(key, cache) - return cache, nil -} - - -func collections(_ string) (string, error) { - key := "COLLECTIONS" - expire := time.Hour * 3 - cache := network_cache.LoadCache(key, expire) - if cache != "" { - return cache, nil - } - collections, err := client.Collections() - if err != nil { - return "", err - } - buff, _ := json.Marshal(&collections) - cache = string(buff) - network_cache.SaveCache(key, cache) - return cache, nil -} - -func comics(params string) (string, error) { - var paramsStruct struct { - Category string `json:"category"` - Tag string `json:"tag"` - CreatorId string `json:"creatorId"` - ChineseTeam string `json:"chineseTeam"` - Sort string `json:"sort"` - Page int `json:"page"` - } - json.Unmarshal([]byte(params), ¶msStruct) - return cacheable( - fmt.Sprintf("COMICS$%s$%s$%s$%s$%s$%d", paramsStruct.Category, paramsStruct.Tag, paramsStruct.CreatorId, paramsStruct.ChineseTeam, paramsStruct.Sort, paramsStruct.Page), - time.Hour*2, - func() (interface{}, error) { - return client.Comics(paramsStruct.Category, paramsStruct.Tag, paramsStruct.CreatorId, paramsStruct.ChineseTeam, paramsStruct.Sort, paramsStruct.Page) - }, - ) -} - -func searchComics(params string) (string, error) { - var paramsStruct struct { - Categories []string `json:"categories"` - Keyword string `json:"keyword"` - Sort string `json:"sort"` - Page int `json:"page"` - } - json.Unmarshal([]byte(params), ¶msStruct) - categories := paramsStruct.Categories - keyword := paramsStruct.Keyword - sort := paramsStruct.Sort - page := paramsStruct.Page - // - var categoriesInKey string - if len(categories) == 0 { - categoriesInKey = "" - } else { - b, _ := json.Marshal(categories) - categoriesInKey = string(b) - } - return cacheable( - fmt.Sprintf("SEARCH$%s$%s$%s$%d", categoriesInKey, keyword, sort, page), - time.Hour*2, - func() (interface{}, error) { - return client.SearchComics(categories, keyword, sort, page) - }, - ) -} - -func randomComics() (string, error) { - return cacheable( - fmt.Sprintf("RANDOM"), - time.Millisecond*1, - func() (interface{}, error) { - return client.RandomComics() - }, - ) -} - -func leaderboard(typeName string) (string, error) { - return cacheable( - fmt.Sprintf("LEADERBOARD$%s", typeName), - time.Second*200, - func() (interface{}, error) { - return client.Leaderboard(typeName) - }, - ) -} - -func comicInfo(comicId string) (string, error) { - var err error - var comic *source.ComicInfo - // cache - key := fmt.Sprintf("COMIC_INFO$%s", comicId) - expire := time.Hour * 24 * 7 - cache := network_cache.LoadCache(key, expire) - if cache != "" { - var co source.ComicInfo - err = json.Unmarshal([]byte(cache), &co) - if err != nil { - panic(err) - return "", err - } - comic = &co - } else { - // get - comic, err = client.ComicInfo(comicId) - if err != nil { - return "", err - } - var buff []byte - buff, err = json.Marshal(comic) - if err != nil { - return "", err - } - cache = string(buff) - network_cache.SaveCache(key, cache) - } - // 标记历史记录 - view := comic_center.ComicView{} - view.ID = comicId - view.CreatedAt = comic.CreatedAt - view.UpdatedAt = comic.UpdatedAt - view.Title = comic.Title - view.Author = comic.Author - view.PagesCount = int32(comic.PagesCount) - view.EpsCount = int32(comic.EpsCount) - view.Finished = comic.Finished - c, _ := json.Marshal(comic.Categories) - view.Categories = string(c) - view.ThumbOriginalName = comic.Thumb.OriginalName - view.ThumbFileServer = comic.Thumb.FileServer - view.ThumbPath = comic.Thumb.Path - view.LikesCount = int32(comic.LikesCount) - view.Description = comic.Description - view.ChineseTeam = comic.ChineseTeam - t, _ := json.Marshal(comic.Tags) - view.Tags = string(t) - view.AllowDownload = comic.AllowDownload - view.ViewsCount = int32(comic.ViewsCount) - view.IsFavourite = comic.IsFavourite - view.IsLiked = comic.IsLiked - view.CommentsCount = int32(comic.CommentsCount) - err = comic_center.ViewComicUpdateInfo(&view) - if err != nil { - return "", err - } - // return - return cache, nil -} - -func ComicInfoCleanCache(comicId string) { - key := fmt.Sprintf("COMIC_INFO$%s", comicId) - network_cache.RemoveCache(key) -} - -func epPage(params string) (string, error) { - var paramsStruct struct { - ComicId string `json:"comicId"` - Page int `json:"page"` - } - json.Unmarshal([]byte(params), ¶msStruct) - comicId := paramsStruct.ComicId - page := paramsStruct.Page - // - return cacheable( - fmt.Sprintf("COMIC_EP_PAGE$%s$%d", comicId, page), - time.Hour*2, - func() (interface{}, error) { - return client.ComicEpPage(comicId, page) - }, - ) -} - -func comicPicturePageWithQuality(params string) (string, error) { - var paramsStruct struct { - ComicId string `json:"comicId"` - EpOrder int `json:"epOrder"` - Page int `json:"page"` - Quality string `json:"quality"` - } - json.Unmarshal([]byte(params), ¶msStruct) - comicId := paramsStruct.ComicId - epOrder := paramsStruct.EpOrder - page := paramsStruct.Page - quality := paramsStruct.Quality - // - return cacheable( - fmt.Sprintf("COMIC_EP_PAGE$%s$%ds$%ds$%s", comicId, epOrder, page, quality), - time.Hour*2, - func() (interface{}, error) { - return client.ComicPicturePageWithQuality(comicId, epOrder, page, quality) - }, - ) -} - -func switchLike(comicId string) (string, error) { - point, err := client.SwitchLike(comicId) - if err != nil { - return "", err - } - // 更新viewLog里面的favour - comic_center.ViewComicUpdateLike(comicId, strings.HasPrefix(*point, "un")) - // 删除缓存 - ComicInfoCleanCache(comicId) - return *point, nil -} - -func switchFavourite(comicId string) (string, error) { - point, err := client.SwitchFavourite(comicId) - if err != nil { - return "", err - } - // 更新viewLog里面的favour - comic_center.ViewComicUpdateFavourite(comicId, strings.HasPrefix(*point, "un")) - // 删除缓存 - ComicInfoCleanCache(comicId) - return *point, nil -} - -func favouriteComics(params string) (string, error) { - var paramsStruct struct { - Sort string `json:"sort"` - Page int `json:"page"` - } - json.Unmarshal([]byte(params), ¶msStruct) - sort := paramsStruct.Sort - page := paramsStruct.Page - // - point, err := client.FavouriteComics(sort, page) - if err != nil { - return "", err - } - str, err := json.Marshal(point) - if err != nil { - return "", err - } - return string(str), nil -} - -func recommendation(comicId string) (string, error) { - return cacheable( - fmt.Sprintf("RECOMMENDATION$%s", comicId), - time.Hour*2, - func() (interface{}, error) { - return client.ComicRecommendation(comicId) - }, - ) -} - -func comments(params string) (string, error) { - var paramsStruct struct { - ComicId string `json:"comicId"` - Page int `json:"page"` - } - json.Unmarshal([]byte(params), ¶msStruct) - comicId := paramsStruct.ComicId - page := paramsStruct.Page - return cacheable( - fmt.Sprintf("COMMENTS$%s$%d", comicId, page), - time.Hour*2, - func() (interface{}, error) { - return client.ComicCommentsPage(comicId, page) - }, - ) -} - -func commentChildren(params string) (string, error) { - var paramsStruct struct { - CommentId string `json:"commentId"` - Page int `json:"page"` - } - json.Unmarshal([]byte(params), ¶msStruct) - commentId := paramsStruct.CommentId - page := paramsStruct.Page - return cacheable( - fmt.Sprintf("COMMENT_CHILDREN$%s$%d", commentId, page), - time.Hour*2, - func() (interface{}, error) { - return client.CommentChildren(commentId, page) - }, - ) -} - -func postComment(params string) (string, error) { - var paramsStruct struct { - ComicId string `json:"comicId"` - Content string `json:"content"` - } - json.Unmarshal([]byte(params), ¶msStruct) - err := client.PostComment(paramsStruct.ComicId, paramsStruct.Content) - if err != nil { - return "", err - } - network_cache.RemoveCaches("MY_COMMENTS$%") - network_cache.RemoveCaches(fmt.Sprintf("COMMENTS$%s$%%", paramsStruct.ComicId)) - return "", nil -} - -func postChildComment(params string) (string, error) { - var paramsStruct struct { - ComicId string `json:"comicId"` - CommentId string `json:"commentId"` - Content string `json:"content"` - } - json.Unmarshal([]byte(params), ¶msStruct) - err := client.PostChildComment(paramsStruct.CommentId, paramsStruct.Content) - if err != nil { - return "", err - } - network_cache.RemoveCaches(fmt.Sprintf("COMMENT_CHILDREN$%s$%%", paramsStruct.CommentId)) - network_cache.RemoveCaches("MY_COMMENTS$%") - network_cache.RemoveCaches(fmt.Sprintf("COMMENTS$%s$%%", paramsStruct.ComicId)) - return "", nil -} - -func postGameChildComment(params string) (string, error) { - var paramsStruct struct { - GameId string `json:"gameId"` - CommentId string `json:"commentId"` - Content string `json:"content"` - } - json.Unmarshal([]byte(params), ¶msStruct) - err := client.PostChildComment(paramsStruct.CommentId, paramsStruct.Content) - if err != nil { - return "", err - } - network_cache.RemoveCaches(fmt.Sprintf("GAME_COMMENT_CHILDREN$%s$%%", paramsStruct.CommentId)) - network_cache.RemoveCaches("MY_COMMENTS$%") - network_cache.RemoveCaches(fmt.Sprintf("GAME_COMMENTS$%s$%%", paramsStruct.GameId)) - return "", nil -} - -func switchLikeComment(params string) (string, error) { - var paramsStruct struct { - CommentId string `json:"commentId"` - ComicId string `json:"comicId"` - } - json.Unmarshal([]byte(params), ¶msStruct) - rsp, err := client.SwitchLikeComment(paramsStruct.CommentId) - if err != nil { - return "", err - } - network_cache.RemoveCaches(fmt.Sprintf("COMMENT_CHILDREN$%s$%%", paramsStruct.CommentId)) - network_cache.RemoveCaches("MY_COMMENTS$%") - network_cache.RemoveCaches(fmt.Sprintf("COMMENTS$%s$%%", paramsStruct.ComicId)) - return *rsp, nil -} - -func myComments(pageStr string) (string, error) { - page, err := strconv.Atoi(pageStr) - if err != nil { - return "", err - } - return cacheable( - fmt.Sprintf("MY_COMMENTS$%d", page), - time.Hour*2, - func() (interface{}, error) { - return client.MyComments(page) - }, - ) -} - -func games(pageStr string) (string, error) { - page, err := strconv.Atoi(pageStr) - if err != nil { - return "", err - } - return cacheable( - fmt.Sprintf("GAMES$%d", page), - time.Hour*2, - func() (interface{}, error) { - return client.GamePage(page) - }, - ) -} - -func game(gameId string) (string, error) { - return cacheable( - fmt.Sprintf("GAME$%s", gameId), - time.Hour*2, - func() (interface{}, error) { - return client.GameInfo(gameId) - }, - ) -} - -func gameComments(params string) (string, error) { - var paramsStruct struct { - GameId string `json:"gameId"` - Page int `json:"page"` - } - json.Unmarshal([]byte(params), ¶msStruct) - gameId := paramsStruct.GameId - page := paramsStruct.Page - return cacheable( - fmt.Sprintf("GAME_COMMENTS$%s$%d", gameId, page), - time.Hour*2, - func() (interface{}, error) { - return client.GameCommentsPage(gameId, page) - }, - ) -} - -func postGameComment(params string) (string, error) { - var paramsStruct struct { - GameId string `json:"gameId"` - Content string `json:"content"` - } - json.Unmarshal([]byte(params), ¶msStruct) - err := client.PostGameComment(paramsStruct.GameId, paramsStruct.Content) - if err != nil { - return "", err - } - network_cache.RemoveCaches("MY_COMMENTS$%") - network_cache.RemoveCaches(fmt.Sprintf("GAME_COMMENTS$%s$%%", paramsStruct.GameId)) - return "", nil -} - -func gameCommentChildren(params string) (string, error) { - var paramsStruct struct { - CommentId string `json:"commentId"` - Page int `json:"page"` - } - json.Unmarshal([]byte(params), ¶msStruct) - commentId := paramsStruct.CommentId - page := paramsStruct.Page - return cacheable( - fmt.Sprintf("GAME_COMMENT_CHILDREN$%s$%d", commentId, page), - time.Hour*2, - func() (interface{}, error) { - return client.GameCommentChildren(commentId, page) - }, - ) -} - -func switchLikeGameComment(params string) (string, error) { - var paramsStruct struct { - CommentId string `json:"commentId"` - GameId string `json:"gameId"` - } - json.Unmarshal([]byte(params), ¶msStruct) - rsp, err := client.SwitchLikeComment(paramsStruct.CommentId) - if err != nil { - return "", err - } - network_cache.RemoveCaches(fmt.Sprintf("GAME_COMMENT_CHILDREN$%s$%%", paramsStruct.CommentId)) - network_cache.RemoveCaches("MY_COMMENTS$%") - network_cache.RemoveCaches(fmt.Sprintf("GAME_COMMENTS$%s$%%", paramsStruct.GameId)) - return *rsp, nil -} - -func updatePassword(params string) (string, error) { - var paramsStruct struct { - OldPassword string `json:"oldPassword"` - NewPassword string `json:"newPassword"` - } - err := json.Unmarshal([]byte(params), ¶msStruct) - if err != nil { - return "", err - } - err = client.UpdatePassword(paramsStruct.OldPassword, paramsStruct.NewPassword) - if err != nil { - return "", err - } - setPassword(paramsStruct.NewPassword) - return "", nil -} - -func updateSlogan(slogan string) (string, error) { - return "", client.UpdateSlogan(slogan) -} - -func updateAvatar(avatarBase64 string) (string, error) { - buff, err := base64.StdEncoding.DecodeString(avatarBase64) - if err != nil { - return "", err - } - return "", client.UpdateAvatar(buff) -} diff --git a/go/pikapika/common.go b/go/pikapika/common.go deleted file mode 100644 index 0ff18b5..0000000 --- a/go/pikapika/common.go +++ /dev/null @@ -1,76 +0,0 @@ -package pikapika - -import ( - "encoding/json" - "pikapika/pikapika/database/comic_center" - "pikapika/pikapika/database/network_cache" - "time" -) - -// EventNotify EventChannel 总线 -var EventNotify func(message string) - -// 所有的EventChannel都是从这里发出, 格式为json, function代表了是什么事件, content是消息的内容 -// 消息传到前端后由前端调度分发 -func onEvent(function string, content string) { - event := EventNotify - if event != nil { - message := map[string]string{ - "function": function, - "content": content, - } - buff, err := json.Marshal(message) - if err == nil { - event(string(buff)) - } else { - print("SEND ERR?") - } - } -} - -// 发送下载的事件 -func downloadComicEventSend(comicDownload *comic_center.ComicDownload) { - buff, err := json.Marshal(comicDownload) - if err == nil { - onEvent("DOWNLOAD", string(buff)) - } else { - print("SEND ERR?") - } -} - -// 发送导出的事件 -func notifyExport(str string) { - onEvent("EXPORT", str) -} - -// 缓存接口 -func cacheable(key string, expire time.Duration, reload func() (interface{}, error)) (string, error) { - // CACHE - cache := network_cache.LoadCache(key, expire) - if cache != "" { - return cache, nil - } - // obj - obj, err := reload() - if err != nil { - return "", err - } - buff, err := json.Marshal(obj) - // push to cache - if err != nil { - return "", err - } - // return - cache = string(buff) - network_cache.SaveCache(key, cache) - return cache, nil -} - -// 将interface序列化成字符串, 方便与flutter通信 -func serialize(point interface{}, err error) (string, error) { - if err != nil { - return "", err - } - buff, err := json.Marshal(point) - return string(buff), nil -} diff --git a/go/pikapika/config/common.go b/go/pikapika/config/common.go deleted file mode 100644 index fbebc90..0000000 --- a/go/pikapika/config/common.go +++ /dev/null @@ -1,29 +0,0 @@ -package config - -import ( - "path" - "pikapika/pikapika" - "pikapika/pikapika/database/comic_center" - "pikapika/pikapika/database/network_cache" - "pikapika/pikapika/database/properties" - "pikapika/pikapika/utils" -) - -// InitApplication 由不同的平台直接调用, 根据提供的路径初始化数据库, 资料文件夹 -func InitApplication(applicationDir string) { - println("初始化 : " + applicationDir) - var databasesDir, remoteDir, downloadDir, tmpDir string - databasesDir = path.Join(applicationDir, "databases") - remoteDir = path.Join(applicationDir, "pictures", "remote") - downloadDir = path.Join(applicationDir, "download") - tmpDir = path.Join(applicationDir, "tmp") - utils.Mkdir(databasesDir) - utils.Mkdir(remoteDir) - utils.Mkdir(downloadDir) - utils.Mkdir(tmpDir) - properties.InitDBConnect(databasesDir) - network_cache.InitDBConnect(databasesDir) - comic_center.InitDBConnect(databasesDir) - pikapika.InitClient() - pikapika.InitPlugin(remoteDir, downloadDir, tmpDir) -} diff --git a/go/pikapika/database/comic_center/center.go b/go/pikapika/database/comic_center/center.go deleted file mode 100644 index 85de0b6..0000000 --- a/go/pikapika/database/comic_center/center.go +++ /dev/null @@ -1,583 +0,0 @@ -package comic_center - -import ( - "gorm.io/driver/sqlite" - "gorm.io/gorm" - "gorm.io/gorm/clause" - "path" - "pikapika/pikapika/utils" - "sync" - "time" -) - -var mutex = sync.Mutex{} -var db *gorm.DB - -func InitDBConnect(databaseDir string) { - mutex.Lock() - defer mutex.Unlock() - var err error - db, err = gorm.Open(sqlite.Open(path.Join(databaseDir, "comic_center.db")), utils.GormConfig) - if err != nil { - panic("failed to connect database") - } - db.AutoMigrate(&ComicView{}) - db.AutoMigrate(&RemoteImage{}) - db.AutoMigrate(&ComicDownload{}) - db.AutoMigrate(&ComicDownloadEp{}) - db.AutoMigrate(&ComicDownloadPicture{}) -} - -func Transaction(t func(tx *gorm.DB) error) error { - mutex.Lock() - defer mutex.Unlock() - return db.Transaction(t) -} - -func NoLockActionViewComicUpdateInfoDB(view *ComicView, db *gorm.DB) error { - view.LastViewTime = time.Now() - return db.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "id"}}, - DoUpdates: clause.AssignmentColumns([]string{ - "created_at", - "updated_at", - "title", - "author", - "pages_count", - "eps_count", - "finished", - "categories", - "thumb_original_name", - "thumb_file_server", - "thumb_path", - "likes_count", - "description", - "chinese_team", - "tags", - "allow_download", - "views_count", - "is_favourite", - "is_liked", - "comments_count", - "last_view_time", - }), - }).Create(view).Error -} - -func ViewComicUpdateInfo(view *ComicView) error { - mutex.Lock() - defer mutex.Unlock() - return NoLockActionViewComicUpdateInfoDB(view, db) -} - -func ViewComic(comicId string) error { - mutex.Lock() - defer mutex.Unlock() - return db.Model(&ComicView{}).Where( - "id = ?", comicId, - ).Update( - "last_view_time", - time.Now(), - ).Error -} - -func ViewComicUpdateFavourite(comicId string, favourite bool) error { - mutex.Lock() - defer mutex.Unlock() - return db.Model(&ComicView{}).Where( - "id = ?", comicId, - ).Update( - "is_favourite", - favourite, - ).Error -} - -func ViewComicUpdateLike(comicId string, like bool) error { - mutex.Lock() - defer mutex.Unlock() - return db.Model(&ComicView{}).Where( - "id = ?", comicId, - ).Update( - "is_like", - like, - ).Error -} - -func ViewEpAndPicture(comicId string, epOrder int, epTitle string, pictureRank int) error { - mutex.Lock() - defer mutex.Unlock() - return db.Model(&ComicView{}).Where("id", comicId).Updates( - map[string]interface{}{ - "last_view_time": time.Now(), - "last_view_ep_order": epOrder, - "last_view_ep_title": epTitle, - "last_view_picture_rank": pictureRank, - }, - ).Error -} - -func LoadViewLog(comicId string) (*ComicView, error) { - mutex.Lock() - defer mutex.Unlock() - var view ComicView - err := db.First(&view, "id = ?", comicId).Error - if err == gorm.ErrRecordNotFound { - return nil, nil - } - if err != nil { - return nil, err - } - return &view, nil -} - -func FindRemoteImage(fileServer string, path string) *RemoteImage { - mutex.Lock() - defer mutex.Unlock() - var remoteImage RemoteImage - err := db.First(&remoteImage, "file_server = ? AND path = ?", fileServer, path).Error - if err != nil { - if err == gorm.ErrRecordNotFound { - return nil - } else { - panic(err) - } - } - return &remoteImage -} - -func SaveRemoteImage(remote *RemoteImage) error { - mutex.Lock() - defer mutex.Unlock() - return db.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "file_server"}, {Name: "path"}}, - DoUpdates: clause.AssignmentColumns([]string{ - "updated_at", - "file_size", - "format", - "width", - "height", - "local_path", - }), - }).Create(remote).Error -} - -func CreateDownload(comic *ComicDownload, epList *[]ComicDownloadEp) error { - mutex.Lock() - defer mutex.Unlock() - comic.SelectedEpCount = int32(len(*epList)) - return db.Transaction(func(tx *gorm.DB) error { - err := tx.Create(comic).Error - if err != nil { - return err - } - for _, ep := range *epList { - err := tx.Create(&ep).Error - if err != nil { - return err - } - } - return nil - }) -} - -func AddDownload(comic *ComicDownload, epList *[]ComicDownloadEp) error { - mutex.Lock() - defer mutex.Unlock() - return db.Transaction(func(tx *gorm.DB) error { - err := tx.Model(comic).Where("id = ?", comic.ID).Updates(map[string]interface{}{ - "created_at": comic.CreatedAt, - "updated_at": comic.UpdatedAt, - "title": comic.Title, - "author": comic.Author, - "pages_count": comic.PagesCount, - "eps_count": comic.EpsCount, - "finished": comic.Finished, - "categories": comic.Categories, - "thumb_original_name": comic.ThumbOriginalName, - "thumb_file_server": comic.ThumbFileServer, - "thumb_path": comic.ThumbPath, - "description": comic.Description, - "chinese_team": comic.ChineseTeam, - "tags": comic.Tags, - "download_finished": false, // restart - }).Error - if err != nil { - return err - } - err = tx.Exec( - "UPDATE comic_downloads SET eps_count = selected_ep_count + ? WHERE id = ?", - len(*epList), comic.ID, - ).Error - if err != nil { - return err - } - for _, ep := range *epList { - err := tx.Create(&ep).Error - if err != nil { - return err - } - } - return nil - }) -} - -func UpdateDownloadLogo(comicId string, fileSize int64, format string, width int32, height int32, localPath string) error { - mutex.Lock() - defer mutex.Unlock() - return db.Model(&ComicDownload{}).Where("id = ?", comicId).Updates(map[string]interface{}{ - "thumb_file_size": fileSize, - "thumb_format": format, - "thumb_width": width, - "thumb_height": height, - "thumb_local_path": localPath, - }).Error -} - -func FindComicDownloadById(comicId string) (*ComicDownload, error) { - mutex.Lock() - defer mutex.Unlock() - var download ComicDownload - err := db.First(&download, "id = ?", comicId).Error - if err != nil { - if err == gorm.ErrRecordNotFound { - return nil, nil - } - return nil, err - } - return &download, nil -} - -func ListDownloadEpByComicId(comicId string) ([]ComicDownloadEp, error) { - mutex.Lock() - defer mutex.Unlock() - var epList []ComicDownloadEp - err := db.Where("comic_id = ?", comicId).Order("ep_order ASC").Find(&epList).Error - return epList, err -} - -func ListDownloadPictureByEpId(epId string) ([]ComicDownloadPicture, error) { - mutex.Lock() - defer mutex.Unlock() - var pictureList []ComicDownloadPicture - err := db.Where("ep_id = ?", epId).Order("rank_in_ep ASC").Find(&pictureList).Error - return pictureList, err -} - -func AllDownloads() (*[]ComicDownload, error) { - mutex.Lock() - defer mutex.Unlock() - var downloads []ComicDownload - err := db.Table("comic_downloads"). - Joins("LEFT JOIN comic_views ON comic_views.id = comic_downloads.id"). - Select("comic_downloads.*"). - Order("comic_views.last_view_time DESC"). - Scan(&downloads).Error - // err := db.Find(&downloads).Error - return &downloads, err -} - -func LoadFirstNeedDownload() (*ComicDownload, error) { - mutex.Lock() - defer mutex.Unlock() - var download ComicDownload - err := db.First(&download, "download_failed = 0 AND pause = 0 AND deleting = 0 AND download_finished = 0").Error - if err != nil { - if err == gorm.ErrRecordNotFound { - return nil, nil - } - return nil, err - } - return &download, nil -} - -func LoadFirstNeedDownloadEp(comicId string) (*ComicDownloadEp, error) { - mutex.Lock() - defer mutex.Unlock() - var ep ComicDownloadEp - err := db.First( - &ep, - " comic_id = ? AND download_failed = 0 AND download_finished = 0 AND fetched_pictures = 1", - comicId, - ).Error - if err != nil { - if err == gorm.ErrRecordNotFound { - return nil, nil - } - return nil, err - } - return &ep, nil -} - -// LoadNeedDownloadPictures 根据EP.ID获取需要下载的图片 -func LoadNeedDownloadPictures(epId string, limit int) (*[]ComicDownloadPicture, error) { - mutex.Lock() - defer mutex.Unlock() - var pictures []ComicDownloadPicture - err := db.Find( - &pictures, - "ep_id = ? AND download_failed = 0 AND download_finished = 0 LIMIT ?", - epId, limit, - ).Error - return &pictures, err -} - -func FetchPictures(comicId string, epId string, list *[]ComicDownloadPicture) error { - mutex.Lock() - defer mutex.Unlock() - return db.Transaction(func(tx *gorm.DB) error { - var rankInEp int32 - for _, picture := range *list { - rankInEp = rankInEp + 1 - picture.RankInEp = rankInEp - err := tx.Create(&picture).Error - if err != nil { - return err - } - } - err := tx.Model(&ComicDownloadEp{}).Where("id = ?", epId).Updates(map[string]interface{}{ - "fetched_pictures": true, - "selected_picture_count": len(*list), - }).Error - if err != nil { - return err - } - return tx.Exec( - "UPDATE comic_downloads SET selected_picture_count = selected_picture_count + ? WHERE id = ?", - len(*list), comicId, - ).Error - }) -} - -func DownloadFailed(comicId string) error { - mutex.Lock() - defer mutex.Unlock() - return db.Model(&ComicDownload{}).Where("id = ?", comicId).Update("download_failed", true).Error -} - -func DownloadSuccess(comicId string) error { - mutex.Lock() - defer mutex.Unlock() - return db.Model(&ComicDownload{}).Where("id = ?", comicId).Updates(map[string]interface{}{ - "download_finished": true, - "download_finished_time": time.Now(), - }).Error -} - -func EpFailed(epId string) error { - mutex.Lock() - defer mutex.Unlock() - return db.Model(&ComicDownloadEp{}).Where("id = ?", epId).Update("download_failed", true).Error -} - -func EpSuccess(comicId string, epId string) error { - mutex.Lock() - defer mutex.Unlock() - return db.Transaction(func(tx *gorm.DB) error { - err := tx.Model(&ComicDownloadEp{}).Where("id = ?", epId).Updates(map[string]interface{}{ - "download_finished": true, - "download_finished_time": time.Now(), - }).Error - if err != nil { - return err - } - return tx.Exec( - "UPDATE comic_downloads SET download_ep_count = download_ep_count + 1 WHERE id = ?", - comicId, - ).Error - }) -} - -func PictureFailed(pictureId string) error { - mutex.Lock() - defer mutex.Unlock() - return db.Model(&ComicDownloadPicture{}).Where("id = ?", pictureId).Update("download_failed", true).Error -} - -func PictureSuccess( - comicId string, epId string, pictureId string, - fileSize int64, format string, width int32, height int32, localPath string, -) error { - mutex.Lock() - defer mutex.Unlock() - return db.Transaction(func(tx *gorm.DB) error { - err := tx.Model(&ComicDownloadPicture{}).Where("id = ?", pictureId).Updates(map[string]interface{}{ - "file_size": fileSize, - "format": format, - "width": width, - "height": height, - "local_path": localPath, - "download_finished": true, - "download_finished_time": time.Now(), - }).Error - if err != nil { - return err - } - err = tx.Exec( - "UPDATE comic_download_eps SET download_picture_count = download_picture_count + 1 WHERE id = ?", - epId, - ).Error - if err != nil { - return err - } - return tx.Exec( - "UPDATE comic_downloads SET download_picture_count = download_picture_count + 1 WHERE id = ?", - comicId, - ).Error - }) -} - -func ResetAll() error { - mutex.Lock() - defer mutex.Unlock() - return db.Transaction(func(tx *gorm.DB) error { - err := tx.Model(&ComicDownload{}).Where("1 = 1"). - Update("download_failed", false).Error - if err != nil { - return err - } - err = tx.Model(&ComicDownloadEp{}).Where("1 = 1"). - Update("download_failed", false).Error - if err != nil { - return err - } - err = tx.Model(&ComicDownloadPicture{}).Where("1 = 1"). - Update("download_failed", false).Error - return err - }) -} - -func ViewLogPage(offset int, limit int) (*[]ComicView, error) { - mutex.Lock() - defer mutex.Unlock() - var list []ComicView - err := db.Offset(offset).Limit(limit).Order("last_view_time DESC").Find(&list).Error - return &list, err -} - -func ClearAllViewLog() { - mutex.Lock() - defer mutex.Unlock() - db.Unscoped().Where("1 = 1").Delete(&ComicView{}) -} - -func DeleteViewLog(id string) { - mutex.Lock() - defer mutex.Unlock() - db.Unscoped().Where("id = ?", id).Delete(&ComicView{}) -} - -func DeletingComic() (*ComicDownload, error) { - mutex.Lock() - defer mutex.Unlock() - var download ComicDownload - err := db.First(&download, "deleting = 1").Error - if err == gorm.ErrRecordNotFound { - return nil, nil - } - return &download, err -} - -func TrueDelete(comicId string) error { - mutex.Lock() - defer mutex.Unlock() - err := db.Transaction(func(tx *gorm.DB) error { - err := tx.Unscoped().Delete(&ComicDownload{}, "id = ?", comicId).Error - if err != nil { - return err - } - err = tx.Unscoped().Delete(&ComicDownloadEp{}, "comic_id = ?", comicId).Error - if err != nil { - return err - } - err = tx.Unscoped().Delete(&ComicDownloadPicture{}, "comic_id = ?", comicId).Error - if err != nil { - return err - } - return nil - }) - if err != nil { - return err - } - return db.Raw("VACUUM").Error -} - -func Deleting(comicId string) error { - mutex.Lock() - defer mutex.Unlock() - return db.Model(&ComicDownload{}).Where("id = ?", comicId).Updates(map[string]interface{}{ - "deleting": true, - }).Error -} - -func RemoveAllRemoteImage() error { - mutex.Lock() - defer mutex.Unlock() - err := db.Unscoped().Delete(&RemoteImage{}, "1 = 1").Error - if err != nil { - return err - } - return db.Raw("VACUUM").Error -} - -func EarliestRemoteImage(earliest time.Time, pageSize int) ([]RemoteImage, error) { - mutex.Lock() - defer mutex.Unlock() - var images []RemoteImage - err := db.Where("strftime('%s',updated_at) < strftime('%s',?)", earliest). - Order("updated_at").Limit(pageSize).Find(&images).Error - return images, err -} - -func DeleteRemoteImages(images []RemoteImage) error { - mutex.Lock() - defer mutex.Unlock() - if len(images) == 0 { - return nil - } - ids := make([]uint, len(images)) - for i := 0; i < len(images); i++ { - ids[i] = images[i].ID - } - return db.Unscoped().Model(&RemoteImage{}).Delete("id in ?", ids).Error -} - -func DownloadInfo(comicId string) (*ComicDownload, error) { - mutex.Lock() - defer mutex.Unlock() - var download ComicDownload - err := db.First(&download, "id = ?", comicId).Error - if err != nil { - if err == gorm.ErrRecordNotFound { - return nil, nil - } - return nil, err - } - return &download, nil -} - -func UpdateTimeCacheImageTime(id uint) { - now := time.Now() - mutex.Lock() - defer mutex.Unlock() - err := db.Model(&RemoteImage{}).Where("id = ?", id).Updates(map[string]interface{}{ - "created_at": now, - "updated_at": now, - }).Error - if err != nil { - panic(err) - } -} - -func ViewedList(ids []string) (viewedList []ComicView) { - err := db.Find(&viewedList, ids).Error - if err != nil { - panic(err) - } - return -} - -func VACUUM() error { - mutex.Lock() - defer mutex.Unlock() - return db.Raw("VACUUM").Error -} diff --git a/go/pikapika/database/comic_center/entities.go b/go/pikapika/database/comic_center/entities.go deleted file mode 100644 index f2f6a6b..0000000 --- a/go/pikapika/database/comic_center/entities.go +++ /dev/null @@ -1,107 +0,0 @@ -package comic_center - -import ( - "gorm.io/gorm" - "time" -) - -type RemoteImage struct { - gorm.Model - FileServer string `gorm:"index:uk_fp,unique" json:"fileServer"` - Path string `gorm:"index:uk_fp,unique" json:"path"` - FileSize int64 `json:"fileSize"` - Format string `json:"format"` - Width int32 `json:"width"` - Height int32 `json:"height"` - LocalPath string `json:"localPath"` -} - -type ComicSimple struct { - ID string `gorm:"primarykey" json:"id"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - Title string `json:"title"` - Author string `json:"author"` - PagesCount int32 `json:"pagesCount"` - EpsCount int32 `json:"epsCount"` - Finished bool `json:"finished"` - Categories string `json:"categories"` - ThumbOriginalName string `json:"thumbOriginalName"` - ThumbFileServer string `json:"thumbFileServer"` - ThumbPath string `json:"thumbPath"` -} - -type ComicInfo struct { - ComicSimple - LikesCount int32 `json:"likesCount"` - Description string `json:"description"` - ChineseTeam string `json:"chineseTeam"` - Tags string `json:"tags"` - AllowDownload bool `json:"allowDownload"` - ViewsCount int32 `json:"viewsCount"` - IsFavourite bool `json:"isFavourite"` - IsLiked bool `json:"isLiked"` - CommentsCount int32 `json:"commentsCount"` -} - -type ComicView struct { - ComicInfo - LastViewTime time.Time `json:"lastViewTime"` - LastViewEpOrder int32 `json:"lastViewEpOrder"` - LastViewEpTitle string `json:"lastViewEpTitle"` - LastViewPictureRank int32 `json:"lastViewPictureRank"` -} - -type ComicDownload struct { - ComicSimple - Description string `json:"description"` - ChineseTeam string `json:"chineseTeam"` - Tags string `json:"tags"` - SelectedEpCount int32 `json:"selectedEpCount"` - SelectedPictureCount int32 `json:"selectedPictureCount"` - DownloadEpCount int32 `json:"downloadEpCount"` - DownloadPictureCount int32 `json:"downloadPictureCount"` - DownloadFinished bool `json:"downloadFinished"` - DownloadFinishedTime time.Time `json:"downloadFinishedTime"` - DownloadFailed bool `json:"downloadFailed"` - Deleting bool `json:"deleting"` - ThumbFileSize int64 `json:"thumbFileSize"` - ThumbFormat string `json:"thumbFormat"` - ThumbWidth int32 `json:"thumbWidth"` - ThumbHeight int32 `json:"thumbHeight"` - ThumbLocalPath string `json:"thumbLocalPath"` - Pause bool `json:"pause"` -} - -type ComicDownloadEp struct { - ComicId string `gorm:"index:idx_comic_id" json:"comicId"` - ID string `gorm:"primarykey" json:"id"` - UpdatedAt time.Time `json:"updated_at"` - EpOrder int32 `json:"epOrder"` - Title string `json:"title"` - FetchedPictures bool `json:"fetchedPictures"` - SelectedPictureCount int32 `json:"selectedPictureCount"` - DownloadPictureCount int32 `json:"downloadPictureCount"` - DownloadFinished bool `json:"downloadFinish"` - DownloadFinishedTime time.Time `json:"downloadFinishTime"` - DownloadFailed bool `json:"downloadFailed"` -} - -type ComicDownloadPicture struct { - ID string `gorm:"primarykey" json:"id"` - ComicId string `gorm:"index:idx_comic_id" json:"comicId"` - EpId string `gorm:"index:idx_ep_id" json:"epId"` - EpOrder int32 `gorm:"index:idx_ep_order" json:"epOrder"` - RankInEp int32 `json:"rankInEp"` - DownloadFinished bool `json:"downloadFinish"` - DownloadFinishedTime time.Time `json:"downloadFinishTime"` - DownloadFailed bool `json:"downloadFailed"` - OriginalName string - FileServer string `gorm:"index:idx_fp,priority:1" json:"fileServer"` - Path string `gorm:"index:idx_fp,priority:2" json:"path"` - FileSize int64 `json:"fileSize"` - Format string `json:"format"` - Width int32 `json:"width"` - Height int32 `json:"height"` - LocalPath string `json:"localPath"` -} diff --git a/go/pikapika/database/network_cache/cache.go b/go/pikapika/database/network_cache/cache.go deleted file mode 100644 index 3bf8e31..0000000 --- a/go/pikapika/database/network_cache/cache.go +++ /dev/null @@ -1,99 +0,0 @@ -package network_cache - -import ( - "errors" - "gorm.io/driver/sqlite" - "gorm.io/gorm" - "gorm.io/gorm/clause" - "path" - "pikapika/pikapika/utils" - "sync" - "time" -) - -var mutex = sync.Mutex{} -var db *gorm.DB - -type NetworkCache struct { - gorm.Model - K string `gorm:"index:uk_k,unique"` - V string -} - -func InitDBConnect(databaseDir string) { - mutex.Lock() - defer mutex.Unlock() - var err error - db, err = gorm.Open(sqlite.Open(path.Join(databaseDir, "network_cache.db")), utils.GormConfig) - if err != nil { - panic("failed to connect database") - } - db.AutoMigrate(&NetworkCache{}) -} - -func LoadCache(key string, expire time.Duration) string { - mutex.Lock() - defer mutex.Unlock() - var cache NetworkCache - err := db.First(&cache, "k = ? AND updated_at > ?", key, time.Now().Add(expire*-1)).Error - if err == nil { - return cache.V - } - if gorm.ErrRecordNotFound == err { - return "" - } - panic(errors.New("?")) -} - -func SaveCache(key string, value string) { - mutex.Lock() - defer mutex.Unlock() - db.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "k"}}, - DoUpdates: clause.AssignmentColumns([]string{"created_at", "updated_at", "v"}), - }).Create(&NetworkCache{ - K: key, - V: value, - }) -} - -func RemoveCache(key string) error { - mutex.Lock() - defer mutex.Unlock() - err := db.Unscoped().Delete(&NetworkCache{}, "k = ?", key).Error - if err == gorm.ErrRecordNotFound { - return nil - } - return err -} - -func RemoveCaches(like string) error { - mutex.Lock() - defer mutex.Unlock() - err := db.Unscoped().Delete(&NetworkCache{}, "k LIKE ?", like).Error - if err == gorm.ErrRecordNotFound { - return nil - } - return err -} - -func RemoveAll() error { - mutex.Lock() - defer mutex.Unlock() - err := db.Unscoped().Delete(&NetworkCache{}, "1 = 1").Error - if err != nil { - return err - } - return db.Raw("VACUUM").Error -} - -func RemoveEarliest(earliest time.Time) error { - mutex.Lock() - defer mutex.Unlock() - err := db.Unscoped().Where("strftime('%s',updated_at) < strftime('%s',?)", earliest). - Delete(&NetworkCache{}).Error - if err != nil { - return err - } - return db.Raw("VACUUM").Error -} diff --git a/go/pikapika/database/properties/properties.go b/go/pikapika/database/properties/properties.go deleted file mode 100644 index 573cd01..0000000 --- a/go/pikapika/database/properties/properties.go +++ /dev/null @@ -1,127 +0,0 @@ -package properties - -import ( - "errors" - "fmt" - "gorm.io/driver/sqlite" - "gorm.io/gorm" - "gorm.io/gorm/clause" - "path" - "pikapika/pikapika/utils" - "strconv" - "sync" -) - -var mutex = sync.Mutex{} -var db *gorm.DB - -func InitDBConnect(databaseDir string) { - mutex.Lock() - defer mutex.Unlock() - var err error - db, err = gorm.Open(sqlite.Open(path.Join(databaseDir, "properties.db")), utils.GormConfig) - if err != nil { - panic("failed to connect database") - } - db.AutoMigrate(&Property{}) -} - -type Property struct { - gorm.Model - K string `gorm:"index:uk_k,unique"` - V string -} - -func LoadProperty(name string, defaultValue string) (string, error) { - mutex.Lock() - defer mutex.Unlock() - var property Property - err := db.First(&property, "k", name).Error - if err == nil { - return property.V, nil - } - if gorm.ErrRecordNotFound == err { - return defaultValue, nil - } - panic(errors.New("?")) -} - -func SaveProperty(name string, value string) error { - mutex.Lock() - defer mutex.Unlock() - return db.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "k"}}, - DoUpdates: clause.AssignmentColumns([]string{"created_at", "updated_at", "v"}), - }).Create(&Property{ - K: name, - V: value, - }).Error -} - -func LoadBoolProperty(name string, defaultValue bool) (bool, error) { - stringValue, err := LoadProperty(name, strconv.FormatBool(defaultValue)) - if err != nil { - return false, err - } - return strconv.ParseBool(stringValue) -} - -func SaveBoolProperty(name string, value bool) error { - return SaveProperty(name, strconv.FormatBool(value)) -} - -func LoadIntProperty(name string, defaultValue int) (int, error) { - str, err := LoadProperty(name, fmt.Sprintf("%d", defaultValue)) - if err != nil { - return 0, err - } - return strconv.Atoi(str) -} - -func SaveIntProperty(name string, value int) error { - return SaveProperty(name, strconv.Itoa(value)) -} - -func SaveProxy(value string) error { - return SaveProperty("proxy", value) -} - -func LoadProxy() (string, error) { - return LoadProperty("proxy", "") -} - -func SaveUsername(value string) error { - return SaveProperty("username", value) -} - -func LoadUsername() (string, error) { - return LoadProperty("username", "") -} - -func SavePassword(value string) error { - return SaveProperty("password", value) -} - -func LoadPassword() (string, error) { - return LoadProperty("password", "") -} - -func SaveToken(value string) { - SaveProperty("token", value) -} - -func LoadToken() (string, error) { - return LoadProperty("token", "") -} - -func SaveTokenTime(value int64) { - SaveProperty("token_time", strconv.FormatInt(value, 10)) -} - -func LoadTokenTime() (int64, error) { - str, err := LoadProperty("token_time", "0") - if err != nil { - return 0, err - } - return strconv.ParseInt(str, 10, 64) -} diff --git a/go/pikapika/download.go b/go/pikapika/download.go deleted file mode 100644 index cd3b8ee..0000000 --- a/go/pikapika/download.go +++ /dev/null @@ -1,498 +0,0 @@ -package pikapika - -import ( - "bytes" - "fmt" - "image" - "io/ioutil" - "os" - "path" - "path/filepath" - comic_center2 "pikapika/pikapika/database/comic_center" - utils2 "pikapika/pikapika/utils" - "sync" - "time" -) - -// 使用协程进行后台下载 -// downloadRunning 如果为false则停止下载 -// downloadRestart 为true则取消从新启动下载功能 - -var downloadThreadCount = 1 -var downloadThreadFetch = 100 - -var downloadRunning = false -var downloadRestart = false - -var downloadingComic *comic_center2.ComicDownload -var downloadingEp *comic_center2.ComicDownloadEp - -var dlFlag = true - -// 程序启动后仅调用一次, 启动后台线程 -func downloadBackground() { - println("后台线程启动") - if dlFlag { - dlFlag = false - go downloadBegin() - } -} - -// 下载启动/重新启动会暂停三秒 -func downloadBegin() { - time.Sleep(time.Second * 3) - go downloadLoadComic() -} - -// 下载周期中, 每个下载单元会调用此方法, 如果返回true应该停止当前动作 -func downloadHasStop() bool { - if !downloadRunning { - return true - } - if downloadRestart { - downloadRestart = false - return true - } - return false -} - -// 删除下载任务, 当用户要删除下载的时候, 他会被加入删除队列, 而不是直接被删除, 以减少出错 -func downloadDelete() bool { - c, e := comic_center2.DeletingComic() - if e != nil { - panic(e) - } - if c != nil { - os.RemoveAll(downloadPath(c.ID)) - e = comic_center2.TrueDelete(c.ID) - if e != nil { - panic(e) - } - return true - } - return false -} - -// 加载第一个需要下载的漫画 -func downloadLoadComic() { - // 每次下载完一个漫画, 或者启动的时候, 首先进行删除任务 - for downloadDelete() { - } - // 检测是否需要停止 - if downloadHasStop() { - go downloadBegin() - return - } - // 找到第一个要下载的漫画, 查库有错误就停止, 因为这些错误很少出现, 一旦出现必然是严重的, 例如数据库文件突然被删除 - var err error - downloadingComic, err = comic_center2.LoadFirstNeedDownload() - if err != nil { - panic(err) - } - // 处理找到的下载任务 - go downloadInitComic() -} - -// 初始化找到的下载任务 -func downloadInitComic() { - // 检测是否需要停止 - if downloadHasStop() { - go downloadBegin() - return - } - // 若没有漫画要下载则重新启动 - if downloadingComic == nil { - println("没有找到要下载的漫画") - go downloadBegin() - return - } - // 打印日志, 并向前端的eventChannel发送下载信息 - println("正在下载漫画 " + downloadingComic.Title) - downloadComicEventSend(downloadingComic) - eps, err := comic_center2.ListDownloadEpByComicId(downloadingComic.ID) - if err != nil { - panic(err) - } - // 找到这个漫画需要下载的EP, 并搜索获取图片地址 - for _, ep := range eps { - // FetchedPictures字段标志着这个章节的图片地址有没有获取过, 如果没有获取过就重新获取 - if !ep.FetchedPictures { - println("正在获取章节的图片 " + downloadingComic.Title + " " + ep.Title) - // 搜索图片地址, 如果五次没有请求成功, 就不在请求 - for i := 0; i < 5; i++ { - if client.Token == "" { - continue - } - err := downloadFetchPictures(&ep) - if err != nil { - println(err.Error()) - continue - } - ep.FetchedPictures = true - break - } - // 如果未能获取图片地址, 则直接置为失败 - if !ep.FetchedPictures { - println("章节的图片获取失败 " + downloadingComic.Title + " " + ep.Title) - err = comic_center2.EpFailed(ep.ID) - if err != nil { - panic(err) - } - } else { - println("章节的图片获取成功 " + downloadingComic.Title + " " + ep.Title) - downloadingComic.SelectedPictureCount = downloadingComic.SelectedPictureCount + ep.SelectedPictureCount - downloadComicEventSend(downloadingComic) - } - } - } - // 获取图片地址结束, 去初始化下载的章节 - go downloadLoadEp() -} - -// 获取图片地址 -func downloadFetchPictures(downloadEp *comic_center2.ComicDownloadEp) error { - var list []comic_center2.ComicDownloadPicture - // 官方的图片只能分页获取, 从第1页开始获取, 每页最多40张图片 - page := 1 - for true { - rsp, err := client.ComicPicturePage(downloadingComic.ID, int(downloadEp.EpOrder), page) - if err != nil { - return err - } - for _, doc := range rsp.Docs { - list = append(list, comic_center2.ComicDownloadPicture{ - ID: doc.Id, - ComicId: downloadEp.ComicId, - EpId: downloadEp.ID, - EpOrder: downloadEp.EpOrder, - OriginalName: doc.Media.OriginalName, - FileServer: doc.Media.FileServer, - Path: doc.Media.Path, - }) - } - // 如果不是最后一页, 页码加1, 获取下一页 - if rsp.Page < rsp.Pages { - page++ - continue - } - break - } - // 保存获取到的图片 - err := comic_center2.FetchPictures(downloadEp.ComicId, downloadEp.ID, &list) - if err != nil { - panic(err) - } - downloadEp.SelectedPictureCount = int32(len(list)) - return err -} - -// 初始化下载 -func downloadLoadEp() { - // 周期停止检测 - if downloadHasStop() { - go downloadBegin() - return - } - // 找到第一个需要下载的章节并去处理 (未下载失败的, 且未完成下载的) - var err error - downloadingEp, err = comic_center2.LoadFirstNeedDownloadEp(downloadingComic.ID) - if err != nil { - panic(err) - } - go downloadInitEp() -} - -// 处理需要下载的EP -func downloadInitEp() { - if downloadingEp == nil { - // 所有Ep都下完了, 汇总Download下载情况 - go downloadSummaryDownload() - return - } - // 没有下载完则去下载图片 - println("正在下载章节 " + downloadingEp.Title) - go downloadLoadPicture() -} - -// EP下载汇总 -func downloadSummaryDownload() { - // 暂停检测 - if downloadHasStop() { - go downloadBegin() - return - } - // 加载这个漫画的所有EP - list, err := comic_center2.ListDownloadEpByComicId(downloadingComic.ID) - if err != nil { - panic(err) - } - // 判断所有章节是否下载完成 - over := true - for _, downloadEp := range list { - over = over && downloadEp.DownloadFinished - } - if over { - // 如果所有章节下载完成则下载成功 - downloadAndExportLogo(downloadingComic) - err = comic_center2.DownloadSuccess(downloadingComic.ID) - if err != nil { - panic(err) - } - downloadingComic.DownloadFinished = true - downloadingComic.DownloadFinishedTime = time.Now() - } else { - // 否则下载失败 - err = comic_center2.DownloadFailed(downloadingComic.ID) - if err != nil { - panic(err) - } - downloadingComic.DownloadFailed = true - } - // 向前端发送下载状态 - downloadComicEventSend(downloadingComic) - // 去下载下一个漫画 - go downloadLoadComic() -} - -// 加载需要下载的图片 -func downloadLoadPicture() { - // 暂停检测 - if downloadHasStop() { - go downloadBegin() - return - } - // 获取到这个章节需要下载的图片 - downloadingPictures, err := comic_center2.LoadNeedDownloadPictures(downloadingEp.ID, downloadThreadFetch) - if err != nil { - panic(err) - } - // 如果不需要下载 - if len(*downloadingPictures) == 0 { - // 所有图片都下完了, 汇总EP下载情况 - go downloadSummaryEp() - return - } - // 线程池 - channel := make(chan int, downloadThreadCount) - defer close(channel) - wg := sync.WaitGroup{} - for i := 0; i < len(*downloadingPictures); i++ { - // 暂停检测 - if downloadHasStop() { - wg.Wait() - go downloadBegin() - return - } - channel <- 0 - wg.Add(1) - // 不放入携程, 防止i已经变化 - picPoint := &((*downloadingPictures)[i]) - go func() { - downloadPicture(picPoint) - <-channel - wg.Done() - }() - } - wg.Wait() - // 再次新一轮的下载, 直至 len(*downloadingPictures) == 0 - go downloadLoadPicture() -} - -var downloadEventChannelMutex = sync.Mutex{} - -// 这里不能使用暂停检测, 多次检测会导致问题 -func downloadPicture(downloadingPicture *comic_center2.ComicDownloadPicture) { - // 下载图片, 最多重试5次 - println("正在下载图片 " + fmt.Sprintf("%d", downloadingPicture.RankInEp)) - for i := 0; i < 5; i++ { - err := downloadThePicture(downloadingPicture) - if err != nil { - continue - } - func() { - downloadEventChannelMutex.Lock() - defer downloadEventChannelMutex.Unlock() - // 对下载的漫画临时变量热更新并通知前端 - downloadingPicture.DownloadFinished = true - downloadingEp.DownloadPictureCount = downloadingEp.DownloadPictureCount + 1 - downloadingComic.DownloadPictureCount = downloadingComic.DownloadPictureCount + 1 - downloadComicEventSend(downloadingComic) - }() - break - } - // 没能下载成功, 图片置为下载失败 - if !downloadingPicture.DownloadFinished { - err := comic_center2.PictureFailed(downloadingPicture.ID) - if err != nil { - // ??? panic X channel ??? - // panic(err) - } - } -} - -// 下载指定图片 -func downloadThePicture(picturePoint *comic_center2.ComicDownloadPicture) error { - // 为了不和页面前端浏览的数据冲突, 使用url做hash锁 - lock := utils2.HashLock(fmt.Sprintf("%s$%s", picturePoint.FileServer, picturePoint.Path)) - lock.Lock() - defer lock.Unlock() - // 图片保存位置使用相对路径储存, 使用绝对路径操作 - picturePath := fmt.Sprintf("%s/%d/%d", picturePoint.ComicId, picturePoint.EpOrder, picturePoint.RankInEp) - realPath := downloadPath(picturePath) - // 从缓存获取图片 - buff, img, format, err := decodeFromCache(picturePoint.FileServer, picturePoint.Path) - if err != nil { - // 若缓存不存在, 则从网络获取 - buff, img, format, err = decodeFromUrl(picturePoint.FileServer, picturePoint.Path) - } - if err != nil { - return err - } - // 将图片保存到文件 - dir := filepath.Dir(realPath) - if _, err := os.Stat(dir); os.IsNotExist(err) { - os.Mkdir(dir, utils2.CreateDirMode) - } - err = ioutil.WriteFile(downloadPath(picturePath), buff, utils2.CreateFileMode) - if err != nil { - return err - } - // 下载时同时导出 - downloadAndExport(downloadingComic, downloadingEp, picturePoint, buff, format) - // 存入数据库 - return comic_center2.PictureSuccess( - picturePoint.ComicId, - picturePoint.EpId, - picturePoint.ID, - int64(len(buff)), - format, - int32(img.Bounds().Dx()), - int32(img.Bounds().Dy()), - picturePath, - ) -} - -// EP 下载内容汇总 -func downloadSummaryEp() { - // 暂停检测 - if downloadHasStop() { - go downloadBegin() - return - } - // 找到所有下载的图片 - list, err := comic_center2.ListDownloadPictureByEpId(downloadingEp.ID) - if err != nil { - panic(err) - } - // 全部下载完成置为成功, 否则置为失败 - over := true - for _, downloadPicture := range list { - over = over && downloadPicture.DownloadFinished - } - if over { - err = comic_center2.EpSuccess(downloadingEp.ComicId, downloadingEp.ID) - if err != nil { - panic(err) - } - } else { - err = comic_center2.EpFailed(downloadingEp.ID) - if err != nil { - panic(err) - } - } - // 去加载下一个EP - go downloadLoadEp() -} - -// 边下载边导出(导出路径) -var downloadAndExportPath = "" - -// 边下载边导出(导出图片) -func downloadAndExport( - downloadingComic *comic_center2.ComicDownload, - downloadingEp *comic_center2.ComicDownloadEp, - downloadingPicture *comic_center2.ComicDownloadPicture, - buff []byte, - format string, -) { - if downloadAndExportPath == "" { - return - } - if i, e := os.Stat(downloadAndExportPath); e == nil { - if i.IsDir() { - // 进入漫画目录 - comicDir := path.Join(downloadAndExportPath, utils2.ReasonableFileName(downloadingComic.Title)) - i, e = os.Stat(comicDir) - if e != nil { - if os.IsNotExist(e) { - e = os.Mkdir(comicDir, utils2.CreateDirMode) - } else { - return - } - } - if e != nil { - return - } - // 进入章节目录 - epDir := path.Join(comicDir, utils2.ReasonableFileName(fmt.Sprintf("%02d - ", downloadingEp.EpOrder)+downloadingEp.Title)) - i, e = os.Stat(epDir) - if e != nil { - if os.IsNotExist(e) { - e = os.Mkdir(epDir, utils2.CreateDirMode) - } else { - return - } - } - if e != nil { - return - } - // 写入文件 - filePath := path.Join(epDir, fmt.Sprintf("%03d.%s", downloadingPicture.RankInEp, aliasFormat(format))) - ioutil.WriteFile(filePath, buff, utils2.CreateFileMode) - } - } -} - -// 边下载边导出(导出logo) -func downloadAndExportLogo( - downloadingComic *comic_center2.ComicDownload, -) { - if downloadAndExportPath == "" { - return - } - comicLogoPath := downloadPath(path.Join(downloadingComic.ID, "logo")) - if _, e := os.Stat(comicLogoPath); e == nil { - buff, e := ioutil.ReadFile(comicLogoPath) - if e == nil { - _, f, e := image.Decode(bytes.NewBuffer(buff)) - if e == nil { - if i, e := os.Stat(downloadAndExportPath); e == nil { - if i.IsDir() { - // 进入漫画目录 - comicDir := path.Join(downloadAndExportPath, utils2.ReasonableFileName(downloadingComic.Title)) - i, e = os.Stat(comicDir) - if e != nil { - if os.IsNotExist(e) { - e = os.Mkdir(comicDir, utils2.CreateDirMode) - } - } - if e != nil { - return - } - // 写入文件 - filePath := path.Join(comicDir, fmt.Sprintf("%s.%s", "logo", aliasFormat(f))) - ioutil.WriteFile(filePath, buff, utils2.CreateFileMode) - } - } - } - } - } -} - -// jpeg的拓展名 -func aliasFormat(format string) string { - if format == "jpeg" { - return "jpg" - } - return format -} diff --git a/go/pikapika/export.go b/go/pikapika/export.go deleted file mode 100644 index 231d632..0000000 --- a/go/pikapika/export.go +++ /dev/null @@ -1,510 +0,0 @@ -package pikapika - -import ( - "archive/tar" - "archive/zip" - "compress/gzip" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net" - "os" - "path" - "pikapika/pikapika/database/comic_center" - "pikapika/pikapika/utils" - "strings" - "time" -) - -// 通过局域网导出 - -var exportingListener net.Listener -var exportingConn net.Conn - -func exportComicUsingSocket(comicId string) (int, error) { - var err error - exportingListener, err = net.Listen("tcp", ":0") - if err != nil { - return 0, err - } - go handleExportingConn(comicId) - return exportingListener.Addr().(*net.TCPAddr).Port, nil -} - -func handleExportingConn(comicId string) { - defer exportingListener.Close() - var err error - exportingConn, err = exportingListener.Accept() - if err != nil { - notifyExport(fmt.Sprintf("导出失败")) - println(err.Error()) - return - } - defer exportingConn.Close() - gw := gzip.NewWriter(exportingConn) - defer gw.Close() - tw := tar.NewWriter(gw) - defer tw.Close() - err = exportComicDownloadFetch(comicId, func(path string, size int64) (io.Writer, error) { - header := tar.Header{} - header.Name = path - header.Size = size - return tw, tw.WriteHeader(&header) - }) - if err != nil { - notifyExport(fmt.Sprintf("导出失败")) - } else { - notifyExport(fmt.Sprintf("导出成功")) - } -} - -func exportComicUsingSocketExit() error { - if exportingConn != nil { - exportingConn.Close() - } - if exportingListener != nil { - exportingListener.Close() - } - return nil -} - -func exportComicDownload(params string) (filePath string, err error) { - var paramsStruct struct { - ComicId string `json:"comicId"` - Dir string `json:"dir"` - Name string `json:"name"` - } - json.Unmarshal([]byte(params), ¶msStruct) - comicId := paramsStruct.ComicId - dir := paramsStruct.Dir - println(fmt.Sprintf("导出 %s 到 %s", comicId, dir)) - comic, err := comic_center.FindComicDownloadById(comicId) - if err != nil { - return - } - if comic == nil { - err = errors.New("not found") - return - } - if !comic.DownloadFinished { - err = errors.New("not download finish") - return - } - name := strings.TrimSpace(paramsStruct.Name) - if len(name) > 0 { - name = utils.ReasonableFileName(name) + ".zip" - } else { - name = fmt.Sprintf("%s-%s.zip", utils.ReasonableFileName(comic.Title), time.Now().Format("2006_01_02_15_04_05.999")) - } - filePath = path.Join(dir, name) - ex, err := utils.Exists(filePath) - if err != nil { - return "", err - } - if ex { - err = errors.New("exists") - return - } - println(fmt.Sprintf("ZIP : %s", filePath)) - fileStream, err := os.Create(filePath) - if err != nil { - return - } - defer fileStream.Close() - zipWriter := zip.NewWriter(fileStream) - defer zipWriter.Close() - err = exportComicDownloadFetch(comicId, func(path string, size int64) (io.Writer, error) { - header := tar.Header{} - header.Name = path - header.Size = size - return zipWriter.Create(path) - }) - return -} - -func exportComicDownloadFetch(comicId string, onWriteFile func(path string, size int64) (io.Writer, error)) error { - comic, err := comic_center.FindComicDownloadById(comicId) - if err != nil { - return err - } - if comic == nil { - return errors.New("not found") - } - if !comic.DownloadFinished { - return errors.New("not download finish") - } - epList, err := comic_center.ListDownloadEpByComicId(comicId) - if err != nil { - return err - } - jsonComic := JsonComicDownload{} - jsonComic.ComicDownload = *comic - jsonComic.EpList = make([]JsonComicDownloadEp, 0) - for _, ep := range epList { - jsonEp := JsonComicDownloadEp{} - jsonEp.ComicDownloadEp = ep - jsonEp.PictureList = make([]JsonComicDownloadPicture, 0) - pictures, err := comic_center.ListDownloadPictureByEpId(ep.ID) - if err != nil { - return err - } - for _, picture := range pictures { - jsonPicture := JsonComicDownloadPicture{} - jsonPicture.ComicDownloadPicture = picture - jsonPicture.SrcPath = fmt.Sprintf("pictures/%04d_%04d", ep.EpOrder, picture.RankInEp) - notifyExport(fmt.Sprintf("正在导出 EP:%d PIC:%d", ep.EpOrder, picture.RankInEp)) - entryWriter, err := onWriteFile(jsonPicture.SrcPath, jsonPicture.FileSize) - if err != nil { - return err - } - source, err := os.Open(downloadPath(picture.LocalPath)) - if err != nil { - return err - } - _, err = func() (int64, error) { - defer source.Close() - return io.Copy(entryWriter, source) - }() - if err != nil { - return err - } - jsonEp.PictureList = append(jsonEp.PictureList, jsonPicture) - } - jsonComic.EpList = append(jsonComic.EpList, jsonEp) - } - if comic.ThumbLocalPath != "" { - logoBuff, err := ioutil.ReadFile(downloadPath(comic.ThumbLocalPath)) - if err == nil { - entryWriter, err := onWriteFile("logo", int64(len(logoBuff))) - if err != nil { - return err - } - _, err = entryWriter.Write(logoBuff) - if err != nil { - return err - } - } - } - // JS - { - buff, err := json.Marshal(&jsonComic) - if err != nil { - return err - } - logoBuff := append([]byte("data = "), buff...) - if err == nil { - entryWriter, err := onWriteFile("data.js", int64(len(logoBuff))) - if err != nil { - return err - } - _, err = entryWriter.Write(logoBuff) - if err != nil { - return err - } - } - } - // HTML - { - var htmlBuff = []byte(indexHtml) - if err == nil { - entryWriter, err := onWriteFile("index.html", int64(len(htmlBuff))) - if err != nil { - return err - } - _, err = entryWriter.Write(htmlBuff) - if err != nil { - return err - } - } - } - println("OK") - // - return nil -} - -const indexHtml = ` - - - - - - - - - -
-
    -
  • - -
  • - -
- -
-
-
- - -` - -func exportComicDownloadToJPG(params string) error { - var paramsStruct struct { - ComicId string `json:"comicId"` - Dir string `json:"dir"` - Name string `json:"name"` - } - json.Unmarshal([]byte(params), ¶msStruct) - comicId := paramsStruct.ComicId - dir := paramsStruct.Dir - println(fmt.Sprintf("导出 %s 到 %s", comicId, dir)) - comic, err := comic_center.FindComicDownloadById(comicId) - if err != nil { - return err - } - if comic == nil { - return errors.New("not found") - } - if !comic.DownloadFinished { - return errors.New("not download finish") - } - name := strings.TrimSpace(paramsStruct.Name) - if len(name) > 0 { - name = utils.ReasonableFileName(name) - } else { - name = fmt.Sprintf("%s-%s", utils.ReasonableFileName(comic.Title), time.Now().Format("2006_01_02_15_04_05.999")) - } - dirPath := path.Join(dir, name) - println(fmt.Sprintf("DIR : %s", dirPath)) - ex, err := utils.Exists(dirPath) - if err != nil { - return err - } - if ex { - return errors.New("exists") - } - err = os.Mkdir(dirPath, utils.CreateDirMode) - if err != nil { - return err - } - err = os.Mkdir(path.Join(dirPath, "pictures"), utils.CreateDirMode) - if err != nil { - return err - } - - epList, err := comic_center.ListDownloadEpByComicId(comicId) - if err != nil { - return err - } - jsonComic := JsonComicDownload{} - jsonComic.ComicDownload = *comic - jsonComic.EpList = make([]JsonComicDownloadEp, 0) - for _, ep := range epList { - jsonEp := JsonComicDownloadEp{} - jsonEp.ComicDownloadEp = ep - jsonEp.PictureList = make([]JsonComicDownloadPicture, 0) - pictures, err := comic_center.ListDownloadPictureByEpId(ep.ID) - if err != nil { - return err - } - for _, picture := range pictures { - jsonPicture := JsonComicDownloadPicture{} - jsonPicture.ComicDownloadPicture = picture - jsonPicture.SrcPath = fmt.Sprintf("pictures/%04d_%04d.%s", ep.EpOrder, picture.RankInEp, picture.Format) - notifyExport(fmt.Sprintf("正在导出 EP:%d PIC:%d", ep.EpOrder, picture.RankInEp)) - entryWriter, err := os.Create(path.Join(dirPath, jsonPicture.SrcPath)) - if err != nil { - return err - } - err = func() error { - defer entryWriter.Close() - source, err := os.Open(downloadPath(picture.LocalPath)) - if err != nil { - return err - } - _, err = func() (int64, error) { - defer source.Close() - return io.Copy(entryWriter, source) - }() - return err - }() - jsonEp.PictureList = append(jsonEp.PictureList, jsonPicture) - } - jsonComic.EpList = append(jsonComic.EpList, jsonEp) - } - if comic.ThumbLocalPath != "" { - logoBuff, err := ioutil.ReadFile(downloadPath(comic.ThumbLocalPath)) - if err == nil { - entryWriter, err := os.Create(path.Join(dirPath, "logo")) - if err != nil { - return err - } - defer entryWriter.Close() - if err != nil { - return err - } - _, err = entryWriter.Write(logoBuff) - if err != nil { - return err - } - } - } - // JS - { - buff, err := json.Marshal(&jsonComic) - if err != nil { - return err - } - logoBuff := append([]byte("data = "), buff...) - if err == nil { - - entryWriter, err := os.Create(path.Join(dirPath, "data.js")) - if err != nil { - return err - } - defer entryWriter.Close() - _, err = entryWriter.Write(logoBuff) - if err != nil { - return err - } - } - } - // HTML - { - var htmlBuff = []byte(indexHtml) - if err == nil { - entryWriter, err := os.Create(path.Join(dirPath, "index.html")) - if err != nil { - return err - } - defer entryWriter.Close() - _, err = entryWriter.Write(htmlBuff) - if err != nil { - return err - } - } - } - println("OK") - return nil -} diff --git a/go/pikapika/game.go b/go/pikapika/game.go deleted file mode 100644 index e064d43..0000000 --- a/go/pikapika/game.go +++ /dev/null @@ -1,40 +0,0 @@ -package pikapika - -import ( - "errors" - "fmt" - "github.com/PuerkitoBio/goquery" - "net/http" - "regexp" - "time" -) - -var downloadGameUrlPattern, _ = regexp.Compile("^https://game\\.eroge\\.xyz/hhh\\.php\\?id=\\d+$") - -func downloadGame(url string) (string, error) { - if downloadGameUrlPattern.MatchString(url) { - return cacheable(fmt.Sprintf("GAME_PAGE$%s", url), time.Hour*1000, func() (interface{}, error) { - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, err - } - req.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36") - rsp, err := client.Do(req) - if err != nil { - return nil, err - } - defer rsp.Body.Close() - doc, err := goquery.NewDocumentFromReader(rsp.Body) - if err != nil { - return nil, err - } - find := doc.Find("a.layui-btn") - list := make([]string, find.Size()) - find.Each(func(i int, selection *goquery.Selection) { - list[i] = selection.AttrOr("href", "") - }) - return list, nil - }) - } - return "", errors.New("not support url") -} diff --git a/go/pikapika/image.go b/go/pikapika/image.go deleted file mode 100644 index 6a34b08..0000000 --- a/go/pikapika/image.go +++ /dev/null @@ -1,130 +0,0 @@ -package pikapika - -import ( - "bytes" - "context" - "errors" - _ "golang.org/x/image/webp" - "image" - _ "image/gif" - _ "image/jpeg" - _ "image/png" - "io/ioutil" - "net" - "net/http" - "pikapika/pikapika/database/comic_center" - "sync" - "time" -) - -var mutexCounter = -1 -var busMutex *sync.Mutex -var subMutexes []*sync.Mutex -var imageHttpClient *http.Client - -// imageSwitchAddress -// 图片的分流直接使用 switchAddressPattern 可以正常使用 -// 通过ping发现图片的分流地址与ip一致 -// 这里为了域名与官方一致改为域名分流 -var imageSwitchAddresses = map[int]string{ - 1: "https://storage.wika" + "wika.xyz", - 2: "https://s2.pica" + "comic.com", - 3: "https://s3.pica" + "comic.com", -} - -var imageSwitchAddress int - -func init() { - busMutex = &sync.Mutex{} - for i := 0; i < 5; i++ { - subMutexes = append(subMutexes, &sync.Mutex{}) - } - imageHttpClient = &http.Client{ - Transport: &http.Transport{ - TLSHandshakeTimeout: time.Second * 10, - ExpectContinueTimeout: time.Second * 10, - ResponseHeaderTimeout: time.Second * 10, - IdleConnTimeout: time.Second * 10, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return dialer.DialContext(ctx, network, addr) - }, - }, - } -} - -// takeMutex 下载图片获取一个锁, 这样只能同时下载5张图片 -func takeMutex() *sync.Mutex { - busMutex.Lock() - defer busMutex.Unlock() - mutexCounter = (mutexCounter + 1) % len(subMutexes) - return subMutexes[mutexCounter] -} - -func decodeInfoFromBuff(buff []byte) (image.Image, string, error) { - buffer := bytes.NewBuffer(buff) - return image.Decode(buffer) -} - -func decodeFromFile(path string) ([]byte, image.Image, string, error) { - b, e := ioutil.ReadFile(path) - if e != nil { - return nil, nil, "", e - } - i, f, e := decodeInfoFromBuff(b) - if e != nil { - return nil, nil, "", e - } - return b, i, f, e -} - -// 下载图片并decode -func decodeFromUrl(fileServer string, path string) ([]byte, image.Image, string, error) { - useClient := imageHttpClient - if imageSwitchAddress == -1 { - useClient = &client.Client - } - if server, ok := imageSwitchAddresses[imageSwitchAddress]; ok { - fileServer = server - } - m := takeMutex() - m.Lock() - defer m.Unlock() - request, err := http.NewRequest("GET", fileServer+"/static/"+path, nil) - if err != nil { - return nil, nil, "", err - } - response, err := useClient.Do(request) - if err != nil { - return nil, nil, "", err - } - defer response.Body.Close() - if response.StatusCode != 200 { - return nil, nil, "", errors.New("code is not 200") - } - buff, err := ioutil.ReadAll(response.Body) - if err != nil { - return nil, nil, "", err - } - img, format, err := decodeInfoFromBuff(buff) - if err != nil { - return nil, nil, "", err - } - return buff, img, format, err -} - -// decodeFromCache 仅下载使用 -func decodeFromCache(fileServer string, path string) ([]byte, image.Image, string, error) { - cache := comic_center.FindRemoteImage(fileServer, path) - if cache != nil { - buff, err := ioutil.ReadFile(remotePath(cache.LocalPath)) - if err != nil { - return nil, nil, "", err - } - img, format, err := decodeInfoFromBuff(buff) - if err != nil { - return nil, nil, "", err - } - return buff, img, format, err - } - return nil, nil, "", errors.New("not found") -} diff --git a/go/pikapika/import.go b/go/pikapika/import.go deleted file mode 100644 index d6d35a2..0000000 --- a/go/pikapika/import.go +++ /dev/null @@ -1,196 +0,0 @@ -package pikapika - -import ( - "archive/tar" - "archive/zip" - "compress/gzip" - "encoding/json" - "gorm.io/gorm" - "io" - "io/ioutil" - "net" - "os" - path2 "path" - "pikapika/pikapika/database/comic_center" - "pikapika/pikapika/utils" - "strconv" - "strings" -) - -func importComicDownloadUsingSocket(addr string) error { - // - conn, err := net.Dial("tcp", addr) - if err != nil { - return err - } - defer conn.Close() - gr, err := gzip.NewReader(conn) - if err != nil { - return err - } - tr := tar.NewReader(gr) - // - zipPath := path2.Join(tmpDir, "tmp.zip") - closed := false - zipFile, err := os.Create(zipPath) - if err != nil { - return err - } - defer func() { - if !closed { - zipFile.Close() - } - os.Remove(zipPath) - }() - zipWriter := zip.NewWriter(zipFile) - defer func() { - if !closed { - zipWriter.Close() - } - }() - // - for { - header, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - return err - } - if header.Typeflag != tar.TypeReg { - continue - } - writer, err := zipWriter.Create(header.Name) - if err != nil { - return err - } - _, err = io.Copy(writer, tr) - if err != nil { - return err - } - } - err = zipWriter.Close() - zipFile.Close() - closed = true - return importComicDownload(zipPath) -} - -func importComicDownload(zipPath string) error { - zip, err := zip.OpenReader(zipPath) - if err != nil { - return err - } - defer zip.Close() - dataJs, err := zip.Open("data.js") - if err != nil { - return err - } - defer dataJs.Close() - dataBuff, err := ioutil.ReadAll(dataJs) - if err != nil { - return err - } - data := strings.TrimLeft(string(dataBuff), "data = ") - var jsonComicDownload JsonComicDownload - err = json.Unmarshal([]byte(data), &jsonComicDownload) - if err != nil { - return err - } - return comic_center.Transaction(func(tx *gorm.DB) error { - // 删除 - err := tx.Unscoped().Delete(&comic_center.ComicDownload{}, "id = ?", jsonComicDownload.ID).Error - if err != nil { - return err - } - err = tx.Unscoped().Delete(&comic_center.ComicDownloadEp{}, "comic_id = ?", jsonComicDownload.ID).Error - if err != nil { - return err - } - err = tx.Unscoped().Delete(&comic_center.ComicDownloadPicture{}, "comic_id = ?", jsonComicDownload.ID).Error - if err != nil { - return err - } - // 插入 - err = tx.Save(&jsonComicDownload.ComicDownload).Error - if err != nil { - return err - } - for _, ep := range jsonComicDownload.EpList { - err = tx.Save(&ep.ComicDownloadEp).Error - if err != nil { - return err - } - for _, picture := range ep.PictureList { - notifyExport("事务 : " + picture.LocalPath) - err = tx.Save(&picture.ComicDownloadPicture).Error - if err != nil { - return err - } - } - } - // VIEW日志 - view := comic_center.ComicView{} - view.ID = jsonComicDownload.ID - view.CreatedAt = jsonComicDownload.CreatedAt - view.UpdatedAt = jsonComicDownload.UpdatedAt - view.Title = jsonComicDownload.Title - view.Author = jsonComicDownload.Author - view.PagesCount = jsonComicDownload.PagesCount - view.EpsCount = jsonComicDownload.EpsCount - view.Finished = jsonComicDownload.Finished - c, _ := json.Marshal(jsonComicDownload.Categories) - view.Categories = string(c) - view.ThumbOriginalName = jsonComicDownload.ThumbOriginalName - view.ThumbFileServer = jsonComicDownload.ThumbFileServer - view.ThumbPath = jsonComicDownload.ThumbPath - view.LikesCount = 0 - view.Description = jsonComicDownload.Description - view.ChineseTeam = jsonComicDownload.ChineseTeam - t, _ := json.Marshal(jsonComicDownload.Tags) - view.Tags = string(t) - view.AllowDownload = true - view.ViewsCount = 0 - view.IsFavourite = false - view.IsLiked = false - view.CommentsCount = 0 - err = comic_center.NoLockActionViewComicUpdateInfoDB(&view, tx) - if err != nil { - return err - } - // 覆盖文件 - comicDirPath := downloadPath(jsonComicDownload.ID) - utils.Mkdir(comicDirPath) - logoReader, err := zip.Open("logo") - if err == nil { - defer logoReader.Close() - logoBuff, err := ioutil.ReadAll(logoReader) - if err != nil { - return err - } - ioutil.WriteFile(path2.Join(comicDirPath, "logo"), logoBuff, utils.CreateFileMode) - } - for _, ep := range jsonComicDownload.EpList { - utils.Mkdir(path2.Join(comicDirPath, strconv.Itoa(int(ep.EpOrder)))) - for _, picture := range ep.PictureList { - notifyExport("写入 : " + picture.LocalPath) - zipEntry, err := zip.Open(picture.SrcPath) - if err != nil { - return err - } - err = func() error { - defer zipEntry.Close() - entryBuff, err := ioutil.ReadAll(zipEntry) - if err != nil { - return err - } - return ioutil.WriteFile(downloadPath(picture.LocalPath), entryBuff, utils.CreateFileMode) - }() - if err != nil { - return err - } - } - } - // 结束 - return nil - }) -} diff --git a/go/pikapika/modles.go b/go/pikapika/modles.go deleted file mode 100644 index 8370be4..0000000 --- a/go/pikapika/modles.go +++ /dev/null @@ -1,33 +0,0 @@ -package pikapika - -import ( - "pikapika/pikapika/database/comic_center" -) - -type DisplayImageData struct { - FileSize int64 `json:"fileSize"` - Format string `json:"format"` - Width int32 `json:"width"` - Height int32 `json:"height"` - FinalPath string `json:"finalPath"` -} - -type ComicDownloadPictureWithFinalPath struct { - comic_center.ComicDownloadPicture - FinalPath string `json:"finalPath"` -} - -type JsonComicDownload struct { - comic_center.ComicDownload - EpList []JsonComicDownloadEp `json:"epList"` -} - -type JsonComicDownloadEp struct { - comic_center.ComicDownloadEp - PictureList []JsonComicDownloadPicture `json:"pictureList"` -} - -type JsonComicDownloadPicture struct { - comic_center.ComicDownloadPicture - SrcPath string `json:"srcPath"` -} diff --git a/go/pikapika/network.go b/go/pikapika/network.go deleted file mode 100644 index 892806c..0000000 --- a/go/pikapika/network.go +++ /dev/null @@ -1,24 +0,0 @@ -package pikapika - -import ( - "net" - "strings" -) - -// 获取IP的集合 -func clientIpSet() (string, error) { - address, err := net.InterfaceAddrs() - if err != nil { - return "", err - } - ipSet := make([]string, 0) - for _, address := range address { - // 检查ip地址判断是否回环地址 - if ipNet, ok := address.(*net.IPNet); ok && !ipNet.IP.IsLoopback() { - if ipNet.IP.To4() != nil { - ipSet = append(ipSet, ipNet.IP.To4().String()) - } - } - } - return strings.Join(ipSet, ","), nil -} diff --git a/go/pikapika/pikapika.go b/go/pikapika/pikapika.go deleted file mode 100644 index b1436de..0000000 --- a/go/pikapika/pikapika.go +++ /dev/null @@ -1,755 +0,0 @@ -package pikapika - -import ( - "crypto/md5" - "encoding/json" - "errors" - "fmt" - source "github.com/niuhuan/pica-go" - "image/jpeg" - "io/ioutil" - "net/http" - "os" - path2 "path" - "pikapika/pikapika/database/comic_center" - "pikapika/pikapika/database/network_cache" - "pikapika/pikapika/database/properties" - "pikapika/pikapika/utils" - "strconv" - "time" -) - -var ( - remoteDir string - downloadDir string - tmpDir string -) - -var initFlag bool - -func InitPlugin(_remoteDir string, _downloadDir string, _tmpDir string) { - if initFlag { - return - } - initFlag = true - remoteDir = _remoteDir - downloadDir = _downloadDir - tmpDir = _tmpDir - comic_center.ResetAll() - downloadAndExportPath = loadDownloadAndExportPath() - downloadThreadCount = loadDownloadThreadCount() - go downloadBackground() - downloadRunning = true -} - -func remotePath(path string) string { - return path2.Join(remoteDir, path) -} - -func downloadPath(path string) string { - return path2.Join(downloadDir, path) -} - -func saveProperty(params string) error { - var paramsStruct struct { - Name string `json:"name"` - Value string `json:"value"` - } - json.Unmarshal([]byte(params), ¶msStruct) - return properties.SaveProperty(paramsStruct.Name, paramsStruct.Value) -} - -func loadProperty(params string) (string, error) { - var paramsStruct struct { - Name string `json:"name"` - DefaultValue string `json:"defaultValue"` - } - json.Unmarshal([]byte(params), ¶msStruct) - return properties.LoadProperty(paramsStruct.Name, paramsStruct.DefaultValue) -} - -func saveDownloadAndExportPath(value string) error { - err := properties.SaveProperty("downloadAndExportPath", value) - if err == nil { - downloadAndExportPath = value - } - return err -} - -func loadDownloadAndExportPath() string { - p, _ := properties.LoadProperty("downloadAndExportPath", "") - return p -} - -func saveDownloadThreadCount(value int) { - strValue := strconv.Itoa(value) - properties.SaveProperty("downloadThreadCount", strValue) - downloadThreadCount = value - downloadRestart = true -} - -func loadDownloadThreadCount() int { - count, err := properties.LoadProperty("downloadThreadCount", "2") - if err != nil { - return 1 - } - i, err := strconv.Atoi(count) - if err != nil { - return 1 - } - return i -} - -func setSwitchAddress(nSwitchAddress string) error { - num, err := strconv.Atoi(nSwitchAddress) - if err != nil { - return err - } - err = properties.SaveIntProperty("switchAddress", num) - if err != nil { - return err - } - switchAddress = num - return nil -} - -func getSwitchAddress() (string, error) { - return strconv.Itoa(switchAddress), nil -} - -func setImageSwitchAddress(nSwitchAddress string) error { - num, err := strconv.Atoi(nSwitchAddress) - if err != nil { - return err - } - err = properties.SaveIntProperty("imageSwitchAddress", num) - if err != nil { - return err - } - switchAddress = num - return nil -} - -func getImageSwitchAddress() (string, error) { - return strconv.Itoa(imageSwitchAddress), nil -} - -func setProxy(value string) error { - err := properties.SaveProxy(value) - if err != nil { - return err - } - changeProxyUrl(value) - return nil -} - -func getProxy() (string, error) { - return properties.LoadProxy() -} - -func setUsername(value string) error { - return properties.SaveUsername(value) -} - -func getUsername() (string, error) { - return properties.LoadUsername() -} - -func setPassword(value string) error { - return properties.SavePassword(value) -} - -func getPassword() (string, error) { - return properties.LoadPassword() -} - -func preLogin() (string, error) { - token, _ := properties.LoadToken() - tokenTime, _ := properties.LoadTokenTime() - if token != "" && tokenTime > 0 { - if utils.Timestamp()-(1000*60*60*24) < tokenTime { - client.Token = token - return "true", nil - } - } - err := login() - if err == nil { - return "true", nil - } - return "false", nil -} - -func login() error { - username, _ := properties.LoadUsername() - password, _ := properties.LoadPassword() - if password == "" || username == "" { - return errors.New(" 需要设定用户名和密码 ") - } - err := client.Login(username, password) - if err != nil { - return err - } - properties.SaveToken(client.Token) - properties.SaveTokenTime(utils.Timestamp()) - return nil -} - -func register(params string) error { - var dto source.RegisterDto - err := json.Unmarshal([]byte(params), &dto) - if err != nil { - return err - } - return client.Register(dto) -} - -func clearToken() error { - properties.SaveTokenTime(0) - properties.SaveToken("") - return nil -} - -func remoteImageData(params string) (string, error) { - var paramsStruct struct { - FileServer string `json:"fileServer"` - Path string `json:"path"` - } - json.Unmarshal([]byte(params), ¶msStruct) - fileServer := paramsStruct.FileServer - path := paramsStruct.Path - lock := utils.HashLock(fmt.Sprintf("%s$%s", fileServer, path)) - lock.Lock() - defer lock.Unlock() - cache := comic_center.FindRemoteImage(fileServer, path) - if cache == nil { - remote, err := decodeAndSaveImage(fileServer, path) - if err != nil { - return "", err - } - cache = remote - } else { - go comic_center.UpdateTimeCacheImageTime(cache.ID) - } - display := DisplayImageData{ - FileSize: cache.FileSize, - Format: cache.Format, - Width: cache.Width, - Height: cache.Height, - FinalPath: remotePath(cache.LocalPath), - } - return serialize(&display, nil) -} - -func remoteImagePreload(params string) error { - var paramsStruct struct { - FileServer string `json:"fileServer"` - Path string `json:"path"` - } - json.Unmarshal([]byte(params), ¶msStruct) - fileServer := paramsStruct.FileServer - path := paramsStruct.Path - lock := utils.HashLock(fmt.Sprintf("%s$%s", fileServer, path)) - lock.Lock() - defer lock.Unlock() - cache := comic_center.FindRemoteImage(fileServer, path) - var err error - if cache == nil { - _, err = decodeAndSaveImage(fileServer, path) - } - return err -} - -func decodeAndSaveImage(fileServer string, path string) (*comic_center.RemoteImage, error) { - buff, img, format, err := decodeFromUrl(fileServer, path) - if err != nil { - println(fmt.Sprintf("decode error : %s/static/%s %s", fileServer, path, err.Error())) - return nil, err - } - local := - fmt.Sprintf("%x", - md5.Sum([]byte(fmt.Sprintf("%s$%s", fileServer, path))), - ) - real := remotePath(local) - err = ioutil.WriteFile( - real, - buff, os.FileMode(0600), - ) - if err != nil { - return nil, err - } - remote := comic_center.RemoteImage{ - FileServer: fileServer, - Path: path, - FileSize: int64(len(buff)), - Format: format, - Width: int32(img.Bounds().Dx()), - Height: int32(img.Bounds().Dy()), - LocalPath: local, - } - err = comic_center.SaveRemoteImage(&remote) - return &remote, err -} - -func downloadImagePath(path string) (string, error) { - return downloadPath(path), nil -} - -func createDownload(params string) error { - var paramsStruct struct { - Comic comic_center.ComicDownload `json:"comic"` - EpList []comic_center.ComicDownloadEp `json:"epList"` - } - json.Unmarshal([]byte(params), ¶msStruct) - comic := paramsStruct.Comic - epList := paramsStruct.EpList - if comic.Title == "" || len(epList) == 0 { - return errors.New("params error") - } - err := comic_center.CreateDownload(&comic, &epList) - if err != nil { - return err - } - // 创建文件夹 - utils.Mkdir(downloadPath(comic.ID)) - // 复制图标 - downloadComicLogo(&comic) - return nil -} - -func downloadComicLogo(comic *comic_center.ComicDownload) { - lock := utils.HashLock(fmt.Sprintf("%s$%s", comic.ThumbFileServer, comic.ThumbPath)) - lock.Lock() - defer lock.Unlock() - buff, image, format, err := decodeFromCache(comic.ThumbFileServer, comic.ThumbPath) - if err != nil { - buff, image, format, err = decodeFromUrl(comic.ThumbFileServer, comic.ThumbPath) - } - if err == nil { - comicLogoPath := path2.Join(comic.ID, "logo") - ioutil.WriteFile(downloadPath(comicLogoPath), buff, utils.CreateFileMode) - comic_center.UpdateDownloadLogo( - comic.ID, - int64(len(buff)), - format, - int32(image.Bounds().Dx()), - int32(image.Bounds().Dy()), - comicLogoPath, - ) - comic.ThumbFileSize = int64(len(buff)) - comic.ThumbFormat = format - comic.ThumbWidth = int32(image.Bounds().Dx()) - comic.ThumbHeight = int32(image.Bounds().Dy()) - comic.ThumbLocalPath = comicLogoPath - } - if err != nil { - println(err.Error()) - } -} - -func addDownload(params string) error { - var paramsStruct struct { - Comic comic_center.ComicDownload `json:"comic"` - EpList []comic_center.ComicDownloadEp `json:"epList"` - } - json.Unmarshal([]byte(params), ¶msStruct) - comic := paramsStruct.Comic - epList := paramsStruct.EpList - if comic.Title == "" || len(epList) == 0 { - return errors.New("params error") - } - return comic_center.AddDownload(&comic, &epList) -} - -func deleteDownloadComic(comicId string) error { - err := comic_center.Deleting(comicId) - if err != nil { - return err - } - downloadRestart = true - return nil -} - -func loadDownloadComic(comicId string) (string, error) { - download, err := comic_center.FindComicDownloadById(comicId) - if err != nil { - return "", err - } - if download == nil { - return "", nil - } - comic_center.ViewComic(comicId) // VIEW - return serialize(download, err) -} - -func allDownloads() (string, error) { - return serialize(comic_center.AllDownloads()) -} - -func downloadEpList(comicId string) (string, error) { - return serialize(comic_center.ListDownloadEpByComicId(comicId)) -} - -func viewLogPage(params string) (string, error) { - var paramsStruct struct { - Offset int `json:"offset"` - Limit int `json:"limit"` - } - json.Unmarshal([]byte(params), ¶msStruct) - return serialize(comic_center.ViewLogPage(paramsStruct.Offset, paramsStruct.Limit)) -} - -func downloadPicturesByEpId(epId string) (string, error) { - return serialize(comic_center.ListDownloadPictureByEpId(epId)) -} - -func getDownloadRunning() bool { - return downloadRunning -} - -func setDownloadRunning(status bool) { - downloadRunning = status -} - -func cleanNetworkCache() error { - err := network_cache.RemoveAll() - if err != nil { - return err - } - notifyExport("清理结束") - return nil -} - -func cleanImageCache() error { - notifyExport("清理图片缓存") - err := comic_center.RemoveAllRemoteImage() - if err != nil { - return err - } - notifyExport("清理图片文件") - os.RemoveAll(remoteDir) - utils.Mkdir(remoteDir) - notifyExport("清理结束") - return nil -} - -func clean() error { - var err error - notifyExport("清理网络缓存") - err = network_cache.RemoveAll() - if err != nil { - return err - } - notifyExport("清理图片缓存") - err = comic_center.RemoveAllRemoteImage() - if err != nil { - return err - } - notifyExport("清理图片文件") - os.RemoveAll(remoteDir) - utils.Mkdir(remoteDir) - notifyExport("清理结束") - return nil -} - -func autoClean(expire int64) error { - now := time.Now() - earliest := now.Add(time.Second * time.Duration(0-expire)) - err := network_cache.RemoveEarliest(earliest) - if err != nil { - return err - } - pageSize := 10 - for true { - images, err := comic_center.EarliestRemoteImage(earliest, pageSize) - if err != nil { - return err - } - if len(images) == 0 { - return comic_center.VACUUM() - } - // delete data & remove pic - err = comic_center.DeleteRemoteImages(images) - if err != nil { - return err - } - for i := 0; i < len(images); i++ { - err = os.Remove(remotePath(images[i].LocalPath)) - if err != nil { - return err - } - } - } - return nil -} - -func storeViewEp(params string) error { - var paramsStruct struct { - ComicId string `json:"comicId"` - EpOrder int `json:"epOrder"` - EpTitle string `json:"epTitle"` - PictureRank int `json:"pictureRank"` - } - json.Unmarshal([]byte(params), ¶msStruct) - return comic_center.ViewEpAndPicture( - paramsStruct.ComicId, - paramsStruct.EpOrder, - paramsStruct.EpTitle, - paramsStruct.PictureRank, - ) -} - -func loadView(comicId string) (string, error) { - view, err := comic_center.LoadViewLog(comicId) - if err != nil { - return "", nil - } - if view != nil { - b, err := json.Marshal(view) - if err != nil { - return "", err - } - return string(b), nil - } - return "", nil -} - -func convertImageToJPEG100(params string) error { - var paramsStruct struct { - Path string `json:"path"` - Dir string `json:"dir"` - } - err := json.Unmarshal([]byte(params), ¶msStruct) - if err != nil { - return err - } - _, i, _, err := decodeFromFile(paramsStruct.Path) - if err != nil { - return err - } - to := path2.Join(paramsStruct.Dir, path2.Base(paramsStruct.Path)+".jpg") - stream, err := os.Create(to) - if err != nil { - return err - } - defer stream.Close() - return jpeg.Encode(stream, i, &jpeg.Options{Quality: 100}) -} - -// 检查更新只能使用defaultHttpClient, 而不能使用pika的client, 否则会 "tls handshake failure" -func defaultHttpClientGet(url string) (string, error) { - rsp, err := http.DefaultClient.Get(url) - if err != nil { - return "", err - } - defer rsp.Body.Close() - buff, err := ioutil.ReadAll(rsp.Body) - if err != nil { - return "", err - } - return string(buff), nil -} - -func loadViewedList(params string) (string, error) { - var ids []string - err := json.Unmarshal([]byte(params), &ids) - if err != nil { - return "", err - } - viewedList := comic_center.ViewedList(ids) - ids = make([]string, len(viewedList)) - for i, view := range viewedList { - ids[i] = view.ID - } - return serialize(ids, nil) -} - -func FlatInvoke(method string, params string) (string, error) { - switch method { - case "saveProperty": - return "", saveProperty(params) - case "loadProperty": - return loadProperty(params) - case "setSwitchAddress": - return "", setSwitchAddress(params) - case "getSwitchAddress": - return getSwitchAddress() - case "setImageSwitchAddress": - return "", setImageSwitchAddress(params) - case "getImageSwitchAddress": - return getImageSwitchAddress() - case "setProxy": - return "", setProxy(params) - case "getProxy": - return getProxy() - case "setUsername": - return "", setUsername(params) - case "setPassword": - return "", setPassword(params) - case "getUsername": - return getUsername() - case "getPassword": - return getPassword() - case "preLogin": - return preLogin() - case "login": - return "", login() - case "register": - return "", register(params) - case "clearToken": - return "", clearToken() - case "userProfile": - return userProfile() - case "punchIn": - return punchIn() - case "categories": - return categories() - case "comics": - return comics(params) - case "searchComics": - return searchComics(params) - case "randomComics": - return randomComics() - case "leaderboard": - return leaderboard(params) - case "comicInfo": - return comicInfo(params) - case "comicEpPage": - return epPage(params) - case "comicPicturePageWithQuality": - return comicPicturePageWithQuality(params) - case "switchLike": - return switchLike(params) - case "switchFavourite": - return switchFavourite(params) - case "favouriteComics": - return favouriteComics(params) - case "recommendation": - return recommendation(params) - case "comments": - return comments(params) - case "commentChildren": - return commentChildren(params) - case "myComments": - return myComments(params) - case "postComment": - return postComment(params) - case "postChildComment": - return postChildComment(params) - case "game": - return game(params) - case "games": - return games(params) - case "gameComments": - return gameComments(params) - case "postGameComment": - return postGameComment(params) - case "gameCommentChildren": - return gameCommentChildren(params) - case "switchLikeGameComment": - return switchLikeGameComment(params) - case "postGameChildComment": - return postGameChildComment(params) - case "viewLogPage": - return viewLogPage(params) - case "clearAllViewLog": - comic_center.ClearAllViewLog() - return "", nil - case "deleteViewLog": - comic_center.DeleteViewLog(params) - return "", nil - case "cleanNetworkCache": - return "", cleanNetworkCache() - case "cleanImageCache": - return "", cleanImageCache() - case "clean": - return "", clean() - case "autoClean": - expire, err := strconv.ParseInt(params, 10, 64) - if err != nil { - return "", err - } - return "", autoClean(expire) - case "storeViewEp": - return "", storeViewEp(params) - case "loadView": - return loadView(params) - case "downloadRunning": - return strconv.FormatBool(getDownloadRunning()), nil - case "setDownloadRunning": - b, e := strconv.ParseBool(params) - if e != nil { - return "", e - } - setDownloadRunning(b) - return "", nil - case "createDownload": - return "", createDownload(params) - case "addDownload": - return "", addDownload(params) - case "loadDownloadComic": - return loadDownloadComic(params) - case "allDownloads": - return allDownloads() - case "deleteDownloadComic": - return "", deleteDownloadComic(params) - case "downloadEpList": - return downloadEpList(params) - case "downloadPicturesByEpId": - return downloadPicturesByEpId(params) - case "resetAllDownloads": - return "", comic_center.ResetAll() - case "exportComicDownload": - return exportComicDownload(params) - case "exportComicDownloadToJPG": - return "", exportComicDownloadToJPG(params) - case "exportComicUsingSocket": - i, e := exportComicUsingSocket(params) - return fmt.Sprintf("%d", i), e - case "exportComicUsingSocketExit": - return "", exportComicUsingSocketExit() - case "importComicDownload": - return "", importComicDownload(params) - case "importComicDownloadUsingSocket": - return "", importComicDownloadUsingSocket(params) - case "remoteImageData": - return remoteImageData(params) - case "remoteImagePreload": - return "", remoteImagePreload(params) - case "clientIpSet": - return clientIpSet() - case "downloadImagePath": - return downloadImagePath(params) - case "downloadGame": - return downloadGame(params) - case "convertImageToJPEG100": - return "", convertImageToJPEG100(params) - case "loadDownloadAndExportPath": - return loadDownloadAndExportPath(), nil - case "saveDownloadAndExportPath": - return "", saveDownloadAndExportPath(params) - case "saveDownloadThreadCount": - i, e := strconv.Atoi(params) - if e != nil { - return "", e - } - saveDownloadThreadCount(i) - return "", nil - case "loadDownloadThreadCount": - return strconv.Itoa(loadDownloadThreadCount()), nil - case "switchLikeComment": - return switchLikeComment(params) - case "updatePassword": - return updatePassword(params) - case "updateSlogan": - return updateSlogan(params) - case "updateAvatar": - return updateAvatar(params) - case "defaultHttpClientGet": - return defaultHttpClientGet(params) - case "loadViewedList": - return loadViewedList(params) - case "collections": - return collections(params) - } - return "", errors.New("method not found : " + method) -} diff --git a/go/pikapika/utils/const.go b/go/pikapika/utils/const.go deleted file mode 100644 index 79bef19..0000000 --- a/go/pikapika/utils/const.go +++ /dev/null @@ -1,16 +0,0 @@ -package utils - -import ( - "gorm.io/gorm" - "gorm.io/gorm/logger" - "os" -) - -var ( - CreateDirMode = os.FileMode(0700) - CreateFileMode = os.FileMode(0600) - GormConfig = &gorm.Config{ - Logger: logger.Default.LogMode(logger.Info), - } -) - diff --git a/go/pikapika/utils/file.go b/go/pikapika/utils/file.go deleted file mode 100644 index 0ba204c..0000000 --- a/go/pikapika/utils/file.go +++ /dev/null @@ -1,42 +0,0 @@ -package utils - -import ( - "errors" - "os" - "strings" -) - -func Mkdir(dir string) { - if _, err := os.Stat(dir); err != nil { - if os.IsNotExist(err) { - err = os.MkdirAll(dir, CreateDirMode) - if err != nil { - panic(err) - } - } else { - panic(err) - } - } -} - -func ReasonableFileName(title string) string { - title = strings.ReplaceAll(title, "\\", "_") - title = strings.ReplaceAll(title, "/", "_") - title = strings.ReplaceAll(title, "*", "_") - title = strings.ReplaceAll(title, "?", "_") - title = strings.ReplaceAll(title, "<", "_") - title = strings.ReplaceAll(title, ">", "_") - title = strings.ReplaceAll(title, "|", "_") - return title -} - -func Exists(name string) (bool, error) { - _, err := os.Stat(name) - if err == nil { - return true, nil - } - if errors.Is(err, os.ErrNotExist) { - return false, nil - } - return false, err -} \ No newline at end of file diff --git a/go/pikapika/utils/mutex.go b/go/pikapika/utils/mutex.go deleted file mode 100644 index 9f73309..0000000 --- a/go/pikapika/utils/mutex.go +++ /dev/null @@ -1,21 +0,0 @@ -package utils - -import ( - "hash/fnv" - "sync" -) - -var hashMutex []*sync.Mutex - -func init() { - for i := 0; i < 32; i++ { - hashMutex = append(hashMutex, &sync.Mutex{}) - } -} - -// HashLock Hash一样的图片不同时处理 -func HashLock(key string) *sync.Mutex { - hash := fnv.New32() - hash.Write([]byte(key)) - return hashMutex[int(hash.Sum32()%uint32(len(hashMutex)))] -} diff --git a/go/pikapika/utils/time.go b/go/pikapika/utils/time.go deleted file mode 100644 index 8b1b471..0000000 --- a/go/pikapika/utils/time.go +++ /dev/null @@ -1,8 +0,0 @@ -package utils - -import "time" - -// Timestamp 获取当前的Unix时间戳 -func Timestamp() int64 { - return time.Now().UnixNano() / int64(time.Millisecond) -} diff --git a/lib/basic/Method.dart b/lib/basic/Method.dart index 6e2ee33..0d0035a 100644 --- a/lib/basic/Method.dart +++ b/lib/basic/Method.dart @@ -285,7 +285,7 @@ class Method { return ComicsPage.fromJson(json.decode(rsp)); } - /// 看了此漫画的人还看了...(此接口似乎失效了) + /// 看了此漫画的人还看了... Future> recommendation(String comicId) async { String rsp = await _flatInvoke("recommendation", comicId); List list = json.decode(rsp); diff --git a/lib/screens/ComicInfoScreen.dart b/lib/screens/ComicInfoScreen.dart index 3d6082c..70cd7d6 100644 --- a/lib/screens/ComicInfoScreen.dart +++ b/lib/screens/ComicInfoScreen.dart @@ -7,6 +7,7 @@ import 'package:pikapika/basic/Navigator.dart'; import 'package:pikapika/screens/ComicsScreen.dart'; import 'package:pikapika/screens/components/CommentMainType.dart'; import 'package:pikapika/screens/components/ItemBuilder.dart'; +import 'package:pikapika/screens/components/Recommendation.dart'; import 'ComicReaderScreen.dart'; import 'DownloadConfirmScreen.dart'; @@ -111,10 +112,12 @@ class _ComicInfoScreenState extends State with RouteAware { var _tabs = [ Tab(text: '章节 (${_comicInfo.epsCount})'), Tab(text: '评论 (${_comicInfo.commentsCount})'), + const Tab(text: '推荐'), ]; var _views = [ _buildEpWrap(_epListFuture, _comicInfo), CommentList(CommentMainType.COMIC, _comicInfo.id), + Recommendation(comicId: _comicInfo.id), ]; return DefaultTabController( length: _tabs.length, diff --git a/lib/screens/components/Recommendation.dart b/lib/screens/components/Recommendation.dart index 44c0ba9..4ea310a 100644 --- a/lib/screens/components/Recommendation.dart +++ b/lib/screens/components/Recommendation.dart @@ -7,7 +7,6 @@ import 'ItemBuilder.dart'; import 'Images.dart'; // 看过此本子的也在看 -// 一直返回空数组, 所以没有使用 class Recommendation extends StatefulWidget { final String comicId;